A shelf-audit app keeps working in a store with no signal when the device, not the server, is the source of truth for the data the audit needs. That takes three things. The app holds the last-known-good SKU and planogram data locally. It applies any update to that data all-or-nothing, so a half-finished download is never used. It also queues audit results on the device and resolves any conflicts when the store reconnects. That is offline-first data caching applied to a retail field app.
TechnoLynx starts by pinning down what “last-known-good”, “half-downloaded” and “in-progress audit” must mean for the app and its devices. It then chooses the local store, the versioning scheme, the atomic staging pattern and the conflict policy, builds and tests them on the actual store hardware with forced disconnects, and monitors sync conflicts after rollout, as part of its AI engineering for retail practice.
Offline-first data caching on a shelf-audit device
Android’s architecture guidance defines an offline-first app as one that can perform all, or a critical subset, of its core functionality without access to the internet. In such an app, at least one data source must not need the network for its most critical tasks, and reading data is one of those tasks. The guidance goes further: the local data source is the canonical source of truth and should be the only source that higher layers of the app read from, which keeps data consistent across connection states.
Kleppmann and colleagues (Onward! 2019) draw the contrast: in a cloud app, the copy on the client is a cache subordinate to the server. In a local-first app, the copy on the device is the primary one and servers hold secondary copies. A shelf-audit app does not need the whole local-first philosophy, because the retailer’s planning system really is the authority for what the planogram should be. It does need the read path: the auditor’s screen should never wait on a network call to show what belongs on the shelf.
Published studies of mobile apps show this goes wrong in practice. The Simba study (EuroSys 2015) found that offline support was not seamless on some mobile apps it tested, while others disabled offline operation altogether. A separate study of 50 open-source Android apps ran 971 connectivity scenarios and found 320 instances of eventual-connectivity issues.
Can the app keep auditing against last-known-good planogram data?
Yes, provided the planogram is stored on the device as a complete, versioned unit and the app reads only from that store. A planogram specifies where each product sits on a store’s shelves and fixtures and how much space it occupies. The record holds the fixtures and shelves with their dimensions, and for each product its allocated space, number of facings, how many units are stacked and how many stand behind each facing, plus retailer rules such as minimum or maximum quantities and storage requirements like chilling. Planograms come out of space-planning software and travel as images, documents or text files listing products in shelf order.
That structure matters for caching because the parts depend on each other. A facing count without its fixture, or a SKU list from one version paired with shelf dimensions from another, gives a false picture of what the audit should find. The cache should therefore treat “planogram version N for store S” as one unit: SKU master entries, fixture geometry, product placements and reference images all carry the same version identifier.
Scale makes this harder in retail. Our position is that production retail computer vision runs into failures that small deployments never expose: thousands of SKU classes, visually similar products and constrained edge hardware. For the cache, this means the last-known-good dataset is large and the device that holds it has limited storage. Computer-vision compliance checking against planograms has been deployed across more than 7,000 stores of a single convenience chain, according to the Wikipedia article on planograms. At that kind of footprint, the design has to plan for refreshes that fail halfway.
Making sure a device never applies a half-downloaded planogram update
A half-downloaded planogram is never applied when the download and the switch-over are separate steps, and the switch-over is a single atomic transaction. Atomicity means a transaction completes fully or not at all, and other readers never see an intermediate state.
SQLite, one option Android apps use for local relational data, shows the mechanism concretely. Before changing any database page, SQLite copies the page’s original content into a separate rollback journal. It flushes that journal to non-volatile storage before modifying the database file. The transaction commits at the moment the journal is deleted: a crash before that point leaves the database as if nothing changed, and a crash after it leaves every change in place. On the next open, SQLite detects a leftover “hot journal” and plays it back to restore the pre-transaction state. The guarantee holds through an operating-system crash or power failure, not only a clean app exit.
In practice, we would use a staged swap on top of that guarantee:
- Download the new planogram version into staging tables (or a separate database file), tagged with its version, while the active version stays untouched.
- Verify the staged version is complete: every expected record is present and the checksums or manifest from the server match.
- In one transaction, repoint the “active version” record for the store to the staged version.
- Garbage-collect the previous version only after the new one has been read successfully, and keep it until then as the rollback target.
If the staged data spans more than one database file, SQLite’s multi-file commit updates all of them atomically or none of them.
Simba applies the same idea at the sync-service level. Its store writes new object chunks out-of-place, updates the table row to point at them atomically once all data for the row has arrived, and only then deletes the old chunks. After a crash, a status log decides whether to roll the incomplete operation forward or back, depending on whether versions match. If a client disconnects midway through a sync, the server aborts the transaction and the client retries once it recovers. The Simba authors observed this failure in Evernote: a disconnect during sync left half-formed notes and dangling pointers visible on the other client.
Because the staged swap keeps the previous version until the new one is read, the device holds two versions during every update. Size the device’s storage for two full versions, not one. If space is too tight for that, a delta update needs the same staging discipline, applied per change-set.
What happens to an in-progress audit if the device goes offline mid-scan?
Nothing should happen to the audit. Every capture is written to local storage first, and the network is treated as a later delivery step. Android’s guidance calls this a lazy write: write to the local data source first, then queue the write to notify the network at the earliest opportunity. It is the correct choice when the data is critical to the app. The Android connectivity study reaches the same conclusion from the failure side. All user-generated content and actions that need the network must be queued or stored locally when there is no connection, so they can be resubmitted when connectivity returns.
For the queue itself, WorkManager on Android keeps tasks across app restarts and device reboots. It stores scheduled work in an internally managed SQLite database, supports exponential backoff for retries, and can wait for constraints such as an unmetered network. A store that loses power, or an auditor whose battery dies mid-aisle, should not lose a single shelf record.
The auditor also has to see what state the work is in. The same study found that most of the issues it catalogued involved messages that gave the user wrong information about connectivity status. It describes “lost content”, where an offline app shows empty or incomplete content without saying why, and silent download failures that can leave a user believing the latest version is already on the device. It also recommends showing users that their content has not been sent yet but is stored for later. On a shelf-audit app, that means three visible facts on every screen: which planogram version is active, whether the device is offline, and how many audits are waiting to upload.
Syncing offline audits back without losing or duplicating any
Captured audits survive reconnection when each upload is idempotent, the sync can resume from its last confirmed point, and the conflict policy is decided before the first conflict happens.
Idempotent replay
A POST that times out leaves the client unsure whether the server created the record, as the IETF draft on the Idempotency-Key header describes. The draft’s answer is a unique client-generated key that the server uses to recognise retries of the same request. If a retry arrives after the original request completed, the server returns the original result instead of acting again. For audits, that key should be generated on the device when the audit record is created, not when the upload starts, so that every replay after a reboot carries the same key.
Resumable sync
The CouchDB replication protocol shows how resumption works. A replication log records checkpoints on both sides, a revision diff step transfers only the revisions the target lacks, and the log is updated after every committed batch so that a failure resumes from the last success rather than from the beginning. The protocol also tells the replicator to expect delays and losses and not to treat every failed request as fatal.
Choosing the conflict policy
If data written offline does not match the server, the conflict must be resolved before sync can happen, and resolution often requires versioning metadata kept by the app. The choice of strategy depends on the data:
| Policy | How it resolves | Cost | Fits shelf-audit data when |
|---|---|---|---|
| Last-write-wins | Timestamps decide; the server keeps the newer write | Can cause inconsistency with concurrent writers | Records are append-only or have a single writer |
| Strong, online-only writes | Writes disabled while disconnected; reads of possibly stale data still allowed | No offline capture at all | Never for audit capture; only for reference data the device reads |
| Merge (CRDT) | Concurrent changes to different objects merge automatically | Concurrent updates to the same property are left for the app or user to resolve | Several auditors edit the same shared record, such as a store task list |
| Manual review | A person decides per conflict | Staff time and delayed results | The disputed value drives a compliance or financial outcome |
Firestore’s offline persistence, one concrete store, syncs local changes on reconnect and uses last write wins for multiple changes to the same document. That default is fine for audits as long as the data model keeps it from mattering. We would model each audit as an immutable record keyed by its device-generated ID, so two auditors never write the same row and last-write-wins never discards an observation. Reserve merge or manual review for the few genuinely shared fields.
One more check belongs here: the server should record which planogram version each audit was captured against and reject or flag a mismatch, rather than scoring yesterday’s shelf against today’s plan.
When cached planogram data is too stale to audit against
Set an explicit staleness limit, and once it passes, stop presenting audits as compliance results. HTTP caching already has a model for this. RFC 5861 says that once the stale period passes without revalidation, the cached response should not continue to be served stale, and that servers should choose the longest total freshness lifetime they can tolerate.
For planograms, the limit comes from the business, not the network. If a reset is scheduled for Monday, an audit on Tuesday against Friday’s version is wrong however good the connection was. We would give each cached version an effective-from date and a maximum offline age. Past that age, the app keeps capturing but marks results as provisional against version N, and the server rescores them once the current version is known. Expiry should be checked on every read, much as the AWARE emergency-warning cache (preprint, November 2025) purges expired entries on every read and write. In their simulated emergency-alert runs, all tested policies kept a 0% stale rate across network reliability levels.
The same study offers a lesson for small devices, carried over here from emergency alerts rather than measured on retail data. Smarter retention policies made a real difference only when the cache or the network was constrained; once caches were large enough to hold the working set, all policies converged. For a handheld that cannot hold every store’s catalogue, that argues for prioritising the assigned store’s active and next planogram versions over everything else, and not over-engineering eviction where storage is plentiful.
Long offline periods make all of this harder. Replicas that evolve independently do not reflect remote changes until those changes propagate. The longer a device is cut off, the larger the gap between its state and the server’s, and the more conflicts sync has to resolve. Kleppmann and colleagues also note that ad-hoc diffing and merging code written by app teams is often unreliable and brittle. That is a reason to keep the merge surface small by design.
The recognition model needs the same treatment as the data: a cached model with a local fallback when connectivity drops. Cloud inference depends on network communication and so has unpredictable, potentially unacceptably long response times.
Forced-disconnect checklist before rollout
The Android connectivity study tested each feature in four contexts: online, offline, offline then reconnected after the action, and immediately after reconnecting. It ran every scenario at least twice on physical devices, using airplane mode for the offline cases. SQLite’s crash tests apply the same idea to storage: they simulate power loss at varying points and check that each transaction either happened completely or not at all. A shelf-audit build should pass at least these checks on the actual store devices:
- Kill connectivity early, midway and near the end of a planogram download, and confirm the previous version stays active and the staged copy is discarded or resumed.
- Cut power during the version swap transaction, then confirm on restart that exactly one complete version is active.
- Go offline mid-audit, keep capturing, force-close the app, reboot, and confirm every shelf record is still queued.
- Reconnect, interrupt the upload partway, reconnect again, and confirm the server holds each audit exactly once.
- Have two devices audit the same bay offline, reconnect both, and confirm the conflict policy behaves as written.
- Let a cached version pass its staleness limit offline, and confirm the app marks new audits as provisional.
- At each step, confirm the screen shows the active version, the offline state and the pending-upload count.
All of this keeps cached data safe; none of it makes the data correct. If the space-planning system publishes a wrong planogram, the device will cache it faithfully, apply it atomically and audit against it confidently. The remaining question is upstream: who validates a planogram version before it is released to devices, and how quickly can a bad one be withdrawn from a fleet that may be offline when the correction is published?
FAQ
Will I lose anything if I clear the app cache?
On a well-designed audit app, clearing the cache should only remove data that can be downloaded again. Clearing app data, as opposed to cache, deletes the app’s persistent storage, including any queued audits not yet uploaded.
What is data reconciliation in simple terms?
Data reconciliation means comparing two copies of the same data, finding where they differ and settling which value is correct. In an offline app, this happens when the device reconnects and its local changes are checked against the server’s state.
What is a cache miss?
A cache miss happens when requested data is not in the cache and must be fetched from the slower source behind it. For an offline device, a miss with no network means the data is simply unavailable, which is why the planogram for the assigned store must be cached before the auditor goes out.