Expo Updates: Ensuring Idempotent Retries Without Duplicate Bundle Writes
0 reputation · 09 Mar 2022, 05:27 UTC
0 reputation · 09 Mar 2022, 05:27 UTC
Goal: guarantee that a retry triggered by a transient network error does not cause the Expo Updates client to write the same JavaScript bundle more than once to local storage.
The current mechanism relies on comparing the hash returned by the server with a locally stored hash and using a persistent store (expo‑updates) to skip re‑download when the hashes match. However, early SDK releases may perform a case‑sensitive hash comparison, which can turn a 304‑equivalent response into an unnecessary re‑fetch. Additionally, if a network interruption leaves the locally stored hash stale, the client may attempt a redundant download and cause UI flicker.
Does the Expo Updates client treat hash comparison as case‑insensitive in SDK 49 and later? How does the persistent store update when the server returns a 304 Not Modified? What safeguards prevent duplicate writes if the local hash becomes stale after a transient failure?
In SDK 49 and later, the Expo Updates client ensures idempotency during retries through normalized hash comparisons and an atomic write-and-verify cycle. This prevents transient network errors from triggering redundant bundle writes or UI flickers caused by unnecessary re-downloads.
The Expo Updates client treats manifest hash comparisons as case-insensitive. The SHA-256 hex hash returned by the server is normalized to lowercase before being compared against the locally stored hash. This eliminates the risk of a case-mismatch triggering a full re-fetch when the server is logically returning a "not modified" state.
When a server returns a 304 Not Modified response, the persistent store (the Updates database) is not updated. The client treats the 304 response as confirmation that the current local bundle and its associated hash remain valid, bypassing the download and write phases entirely.
To prevent duplicate writes when local state becomes stale or a network failure occurs mid-download, the client employs three primary safeguards:
downloadAsync. The file is only moved to its final destination after the download completes successfully.UpdatesController tracks the state of each updateId (e.g., downloading, verifying, committed). A "pending" flag prevents concurrent download attempts for the same update ID, ensuring that a JS-layer retry of checkUpdateAsync does not spawn a parallel download process.To verify this behavior in your environment, enable debug logs for the updates module (EXUpdatesLogLevelDebug) and monitor the console. You should see "Hash matches, skipping download" during a retry of an existing update, rather than a new download sequence.
Diagnostic Note: If you are using a Bare Workflow, please confirm if your native expo-updates pod/package version matches your JS SDK version, as a mismatch can lead to inconsistent state machine behavior.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.