Answer
Yes – if you retry a Realm.executeTransaction() after a failure, Realm will not duplicate writes **provided the transaction body itself is idempotent or checks for existing state**. The transaction is atomic: if it throws before the commit, the entire write is rolled back. Realm automatically starts a fresh MVCC snapshot for each new transaction, so you do not need to call realm.refresh() before a retry.
Why this matters
The only time a retry can produce duplicates is when:
- The transaction body commits successfully and then an exception occurs after the commit (e.g., a network call fails).
- The transaction body contains non‑idempotent operations (e.g.,
create without a primary key or increment on a counter).
- In a Device Sync scenario, the local commit has already been pushed to the server and the retry re‑sends the same change.
Safe retry pattern
- Wrap the write in
realm.executeTransaction().
- Inside the transaction, perform all state checks and upserts using a stable primary key or an idempotency key.
- If the transaction body must call external APIs, move those calls outside the transaction or wrap them in a retry‑safe service layer.
- After the transaction, handle any post‑commit logic (e.g., network sync) in a separate try/catch block.
Example – idempotent insert:
realm.executeTransaction { r ->
// Use a unique primary key or idempotency key
val existing = r.where(MyObject::class.java)
.equalTo("id", newId)
.findFirst()
if (existing == null) {
val obj = r.createObject(MyObject::class.java, newId)
obj.name = "foo"
} else {
existing.name = "foo" // update instead of duplicate
}
}
When to call realm.refresh()
Realm automatically refreshes to the latest snapshot when a new transaction starts. realm.refresh() is only necessary if you are observing the same snapshot across multiple threads or you need to force a refresh in a long‑running background task. For a simple retry loop, you can skip it.
Potential pitfalls
- External side effects inside the transaction body (e.g., file writes, network calls) are not rolled back by Realm. If those fail after a commit, a retry may duplicate the external action.
- In a sync environment, a locally committed write will be sent to the server once; retrying the same transaction again will create a duplicate on the server unless the server side uses upsert semantics or you deduplicate post‑sync.
Missing diagnostic detail
Does your transaction body perform any external I/O (network, file system, etc.) after the Realm write? If so, we may need to adjust the retry strategy to avoid duplicate side effects.
Verification steps
- Run a test that inserts a temporary object and then throws; confirm the object is not present after the catch.
- Run the same transaction twice with the same primary key and verify the second run updates the existing object.
- In a sync test, commit locally, disconnect, retry, reconnect, and inspect the server to ensure no duplicate documents appear.