Unique work, chains, retries, backoff, and idempotency
WorkManager may run a request again after interruption. Therefore “execute once” must mean repeating it produces the same business outcome, not “this function is invoked exactly once.”
Unique work prevents duplicate local schedules
workManager.enqueueUniqueWork(
"profile-sync:$accountId",
ExistingWorkPolicy.KEEP,
syncRequest,
)
KEEP: retain existing unfinished work.REPLACE: cancel existing work and enqueue new work.APPEND: add to the existing chain.APPEND_OR_REPLACE: append unless the chain is failed/cancelled.
The unique name is a domain decision. sync-all incorrectly merges different signed-in accounts; include identity where needed.
Chain dependent steps
workManager.beginUniqueWork(
"publish:$draftId",
ExistingWorkPolicy.KEEP,
compressRequest,
).then(uploadRequest)
.then(confirmRequest)
.enqueue()
If compression output is a large file, write it to an app-controlled location and pass the path/ID. A failed prerequisite prevents dependent work from running.
Classify failures
return when (response.code) {
in 200..299 -> Result.success()
401 -> Result.failure(workDataOf("reason" to "reauth_required"))
408, 429 -> Result.retry()
in 500..599 -> Result.retry()
else -> Result.failure(workDataOf("reason" to "permanent_http_${response.code}"))
}
Use exponential backoff for temporary failures. Retrying invalid input or an authorization failure forever wastes battery and hides the action the user must take.
Idempotency across the network
@POST("orders")
suspend fun createOrder(
@Header("Idempotency-Key") operationId: String,
@Body body: CreateOrderBody,
): OrderDto
Generate operationId once and persist it with the pending operation. Reuse it on retry. The server stores/replays the first result for that key. A new UUID inside every doWork() defeats idempotency.
Unique work deduplicates WorkManager requests on one installation. It cannot stop duplicate server effects after a timeout. That requires a stable operation identity and server-side idempotency or deduplication.
Corner cases
- Process dies after server creates the order but before local success is saved: retry with the same key and reconcile the returned order.
- User edits a queued draft: decide whether worker reads latest state or a versioned snapshot.
- Sign-out occurs: cancel account-tagged work and ensure the worker validates account identity before reading data.
Sources
Interview question
Does enqueueUniqueWork guarantee an order is created exactly once? No. It only controls matching local work chains. Network ambiguity still requires server-supported idempotency and reconciliation.