Skip to main content

Handle conflicts and recovery

A disconnected client, a rejected update and a lost acknowledgement need different responses. Recovery should preserve what is known about each operation.

Choose the right action​

SituationAction
A previously established connection dropsLet bounded automatic reconnect run, or call reconnect().
You need a current verified snapshotCall refresh() after competing local operations settle.
Another writer wins a state transitionRefresh, inspect the current data, and reconsider the intended change.
A sent write loses its acknowledgementKeep the result uncertain. Do not automatically replay it.
A content resource is unusable, but this session has a confirmed local copyConsider explicit recoverContent(name).

Reconnect after a disconnect​

By default, the SDK makes up to five bounded attempts after an established connection disconnects unintentionally. Configure the policy when creating the instance:

import { Ulda } from 'ulda-auth';

const ulda = new Ulda(endpoint, {
reconnect: {
maxAttempts: 5,
initialDelay: 500,
maxDelay: 5_000,
jitter: 0.2,
budget: 30_000,
},
});

Delays and the total reconnect budget are in milliseconds. The first failed ready is not retried automatically. Pass reconnect: false to disable automatic recovery; explicit reconnect() remains available.

Reconnect establishes transport, performs the handshake, and refreshes an open catalog. It retries eligible reads within its bounds, not writes. There is no persistent offline write queue.

Resolve a conflict​

Handle CONFLICT by waiting for active writes to settle, refreshing the catalog, and comparing the user's intended change with the current data. ULDA does not merge concurrent edits.

refresh() may return BUSY while local work is active. A successful refresh can still leave status === 'needsResync' if an uncertain operation or recovery plan remains unresolved.

Keep unknown outcomes uncertain​

Wrap both the method call and its await: some invalid inputs are rejected synchronously.

try {
await ulda.set('notes', ['text'], proposedText);
} catch (error) {
if (error.code === 'UNKNOWN_OUTCOME') {
showUncertainState();
} else {
throw error;
}
}

showUncertainState is your application's UI handler. The server may already have committed the write, or may still be processing it. Even a read that shows the old state does not prove that resending is safe.

Verified reads can help reconcile what is visible, but they do not retroactively turn the rejected promise into a successful write. Closing and reopening is not a mechanism for safely replaying an uncertain operation.

Recover a content resource explicitly​

recoverContent() creates a replacement using the session's last confirmed local copy, then updates its master entry. It preserves the old server resource.

const result = await ulda.recoverContent('notes');
const current = ulda.ref('notes');

Use this only after deciding that the confirmed local copy is the right recovery source; it may lack newer edits. The SDK requires a verified master, a live connection and no competing local work. An unacknowledged candidate or arbitrary application JSON is not a valid source.

If a new device cannot read the resource, it has no confirmed copy to recover from. A trusted deletion clears that working copy as well.

The result contains previousId, the new id, sourceAge and reconciled. Obtain new references after recovery; old references still belong to the original resource identity.

Continue a retained recovery plan​

Recovery reserves an ID before committing the replacement and updated master. If it is interrupted, the same SDK instance may retain that plan and block normal writes.

Repeat the explicit action with the same name on that instance to follow the plan's continuation rules. A lost Create ID must not be guessed or replaced with another automatic Create. After a lost Update acknowledgement, continuation performs verified reads; it reports reconciled: true only when the intended replacement is confirmed.

Closing discards the in-memory plan. A new session cannot resume it. See the complete continuation rules for each failure stage.

Content recovery does not recover a password, recreate a master, merge changes, or revoke keys already held by another party.