Cross-Tab Leadership with Web Locks: Ownership Generations, Late-Result Fencing, and Fallback Paths
Web Locks can serialize same-origin tabs entering a critical section, but they do not provide fencing tokens for server writes. This article separates browser leader election, persistent generations, and side-effect commits to prevent stale tabs from overwriting current state.
Key Points
- Topic Focus: TypeScript、DevOps
- Suitable for: indie hackers, full-stack engineers, AI application developers
- Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
When multiple tabs refresh a session, drain an offline outbox, or maintain a real-time subscription simultaneously, localStorage heartbeats can easily elect two leaders. The Web Locks API can queue and grant named locks among same-origin contexts within the same browser storage partition. A lock is released when the callback promise passed to navigator.locks.request() settles. But it neither coordinates with another device nor generates a fencing token that the server recognizes. Web Worker Concurrency Fundamentals distinguishes Worker types; this article discusses only ownership protocols between pages.
Lock Grants and Business Ownership Are Two Separate Facts
Publish crossTabLeader.v1, pinning the origin/storage key, workstream, lock name, protocol version, capabilities, maximum hold time, whether ifAvailable/steal is allowed, the server-side or local canonical ledger, and the fallback. Each page generates a tabInstanceId at startup that exists only for the lifetime of that document. It must not reuse the previous page's owner identity from sessionStorage.
schema: crossTabLeader.v1
workstream: account-outbox
lockName: app:leader:account-outbox:v3
protocol: leader-worker@3
canonicalFence: server-lease-ledger
maxHold: 30s
steal: incident-only
fallback: SERVER_COORDINATED
The state machine is FOLLOWER -> REQUESTING -> LEADER -> DRAINING -> RELEASED; cancellation while waiting transitions to ABORTED. Receiving an exclusive Web Lock only authorizes the page to request a canonical claim. The ledger atomically increments leaderGeneration and binds it to the account, workstream, tab instance, lease, and payload protocol. Actual task claims, server writes, and result commits must all carry that generation, and the receiver must compare it with the current value. Optimistic vs. Pessimistic Locking provides the CAS model; a Web Lock itself cannot replace a conditional write at the receiver.
When only local data is involved, the generation can live in a small IndexedDB metadata transaction and be revalidated by every commit transaction. When work spans devices or creates server-side effects, the server ledger must be the authority. Recovering Offline Drafts with IndexedDB covers schemas, outboxes, and database migrations. This article does not repeat that storage workflow; it only requires those operations to consume the current leader fence.
A Live Callback Does Not Mean Its Results Are Still Eligible to Commit
LockOptions.signal cancels only a wait that is still REQUESTING. Once the lock has been granted, that signal no longer terminates the lock-holding callback. After entering LEADER, the page should create a separate application AbortController so the callback can stop claiming and draining work, then settle its promise promptly to release the lock. The page can trigger it when it becomes hidden or is about to freeze, but freezing may prevent the handler from running at all. This reduces load; it is not a correctness fence. BroadcastChannel is only for notifications, and navigator.locks.query() is only for diagnostics.
A forced steal or runtime termination may allow a new owner to acquire the lock while the old callback and its network requests continue running. After the new leader obtains a higher generation, an old request may finish computing, but it must receive FENCED. It cannot commit a cursor, delete new tasks, or acknowledge an external effect. Work that an external provider may have accepted but whose response is unknown must be reconciled by operation ID. It must not be resent under a different key merely because the owner was replaced. HTTP Idempotency Key Correctness contains network side effects; the leader fence determines only who may update canonical state.
Browsers without Web Locks support must not fall back to a homemade localStorage mutex based on reading before writing. The fallback can make every tab a read-only subscriber while a server queue handles draining, or require the user to select an explicit single-page mode. Background Job Queues are better suited to critical writes than a client-side leader whose uniqueness cannot be proven.
Creating Two Pages That "Still Look Like the Leader"
The fault suite launches 20 tabs simultaneously, randomly terminates waiters and holders, freezes the current leader, delays heartbeats, invokes steal before a fetch returns, restores an old tab from BFCache, clears the local ledger, disconnects and then reconnects the server, and submits the same workstream from another device. Assert that the same ledger has at most one active generation at any instant, canonical commits from old generations remain at zero, each operation produces its real side effect at most once, and critical writes fail open zero times when the lock service is unavailable. Resuming Side Effects Across the BFCache Lifecycle further addresses stale asynchronous results from restored pages.
The release gate requires zero dual active generations across 100,000 randomized leader-election rounds, zero successful writes using stale fences, a leader-transition p99 below two seconds, an owner for every unreconciled operation during each transition, and fewer than 0.1% of background holders exceeding the SLO. First, only record which page would have become leader. Then enable read-only refreshes. Finally, enable the idempotent outbox. During an incident, disable client-side claiming and advance the server workstream epoch so the server takes control. Older page versions remain subject to the server fence and cannot regain write authority merely because the frontend was rolled back.
Further Reading
Related
Related Content
Article
Is Next.js App Router a Good Fit for a Technical Community?
Technical trends, engineering practices, and independent development perspectives centered on Next.js and React.
Article
SEO Design for a Content Community with Next.js
Technical trends, engineering practices, and an indie developer's perspective on Next.js and TypeScript.
Article
The Engineering Value of TypeScript in AI Application Frontends
Technical trends, engineering practices, and an independent developer perspective on TypeScript and React.
Article
What Value Do Server Components Offer Content Sites?
Technical trends and engineering practices around Next.js and React, from an independent developer's perspective.