Answer first
Per-object request ordering is preserved across Workers runtime versions. A Durable Object instance processes requests for a given object ID sequentially, one at a time, regardless of which Workers runtime version invokes the stub. That single-threaded execution guarantee is the platform invariant that underpins any application-level lock built on top of the object.
What changes across runtimes is API surface and storage behavior, not the core serialization guarantee. Lock acquisition order for the same object ID remains deterministic. Timeout handling and error messages can appear different because older runtimes lack storage.transaction and newer runtimes change waitUntil/alarm timing, not because the queueing model changed.
Confirmed facts
- Every Durable Object instance is single-threaded per object ID. The Workers runtime queues concurrent requests to the same object and delivers them one at a time to the object’s fetch handler.
- Storage operations are atomic within a storage.transaction call on runtimes that support it. Older runtimes expose non-transactional get/put which remain supported for backward compatibility.
- Lock semantics provided by the platform apply per Durable Object ID only. There is no built-in cross-object lock.
Likely explanation for observed differences
Differences users report when moving between e.g., a 2023-era runtime and newer releases are most plausibly explained by API surface changes and timing, not by a change to per-object serialization.
Possible sources of variance:
- Availability of storage.transaction. Code that assumes transactional atomicity will behave differently on runtimes that only support direct get/put.
- Compatibility date and flags. The Workers compatibility_date in wrangler.toml selects the runtime behavior. Upgrades can change waitUntil delivery timing and alarm granularity, which can affect lock timeout implementations that rely on alarms or background work.
- Mixing transactional and non-transactional storage in the same handler can break atomicity expectations on newer runtimes.
These are implementation-level effects. The fundamental ordering guarantee is stable.
Steps needed for this case
- Identify runtime selection. Check wrangler.toml for compatibility_date and any compatibility_flags that select the Workers runtime version used to invoke the Durable Object.
- Audit the Durable Object handler. Determine whether the lock is a custom implementation using storage, and whether it uses storage.transaction or legacy get/put. Ensure fallback paths are consistent with the target runtime.
- Verify ordering empirically. Issue two rapid requests to the same Durable Object stub and log entry and exit timestamps inside the handler. Sequential handling per object ID should be observable.
Assumption and uncertainty note. This assessment relies on well-established platform behavior as of model knowledge. Current runtime flags and exact timeout semantics can change; verification in your account is required for production decisions.
One diagnostic detail that changes the recommendation: Are you using storage.transaction in the lock implementation, or legacy non-transactional storage access? The presence of transactional code determines whether a compatibility_date bump is required to preserve atomicity guarantees.