gh-15091: Fixed race condition when syncing new space and container (gh-15106)

This commit is contained in:
mr. m
2026-08-25 22:21:19 +02:00
committed by GitHub
parent b93d19b873
commit 03b4e934fe
2 changed files with 31 additions and 5 deletions

View File

@@ -141,12 +141,21 @@ export class ZenSpacesSyncEngine extends SyncEngine {
return ZenSpacesSyncModel.computeChangedIDs();
}
async _reconcile() {
async _reconcile(item) {
// Incoming records always apply, and any local divergence re-uploads afterwards
// (see ZenSpacesSyncModel.noteApplied). The base reconciliation would
// instead drop incoming changes whenever the id is also in the local
// changed set, because the snapshot diff stamps its changes with "now"
// and the local side would always win the age comparison.
//
// An id we accept must also leave the outgoing set. Projections only
// reflect the applied state after the delayed session collection, so
// uploading the id later in this same sync would overwrite the record
// we just accepted with the stale pre-apply state (and the other
// device would then apply that, endlessly trading states back and
// forth). Divergence that survives the collection re-uploads on a
// later sync through the snapshot diff.
this._modified.delete(item.id);
return true;
}

View File

@@ -33,6 +33,25 @@ class nsZenSpacesSyncApplier {
return id ? win.document.getElementById(id) : null;
}
/**
* Maps a record's container guid to the local userContextId. A guid with
* no local mapping (its container record hasn't applied here yet) throws,
* failing the record so it redelivers once the container exists .
*
* @param {?string} containerGuid
* @returns {number}
*/
#resolveContainerId(containerGuid) {
if (!containerGuid) {
return 0;
}
const mapped = ZenSpacesSyncModel.contextIdForGuid(containerGuid);
if (mapped === null) {
throw new Error(`unknown container guid ${containerGuid}`);
}
return mapped;
}
/**
* @param {Array} records - decrypted incoming records.
* @returns {Array<string>} ids of records that failed to apply.
@@ -259,8 +278,7 @@ class nsZenSpacesSyncApplier {
name: data.name ?? "",
icon: data.icon ?? undefined,
theme: data.theme ?? null,
containerTabId:
ZenSpacesSyncModel.contextIdForGuid(data.containerGuid) ?? 0,
containerTabId: this.#resolveContainerId(data.containerGuid),
};
// A space record re-syncs whenever its child ordering changes, so
// only propagate (and especially repaint) when its own fields did.
@@ -482,8 +500,7 @@ class nsZenSpacesSyncApplier {
}
#createTab(win, tabId, data) {
const userContextId =
ZenSpacesSyncModel.contextIdForGuid(data.containerGuid) ?? 0;
const userContextId = this.#resolveContainerId(data.containerGuid);
const tab = win.gBrowser.addTrustedTab(data.url, {
createLazyBrowser: true,
inBackground: true,