context.extendLockTime() while a progress callback renews the same lock at the
same time.
Before @orion-js/dogs@4.6.7, those calls could overlap while MongoDB was updating the job record.
Each call cleared the local stale watchdog, waited for its own database update, and then installed a
new watchdog. Both timers remained active, but only the last handle was retained. The orphaned timer
could mark a healthy execution stale even though later heartbeats had successfully renewed its
MongoDB lock.
Dogs now owns the complete concurrency boundary around lock renewal. Applications keep the same
ExecutionContext API and do not need a mutex around heartbeats.
One MongoDB renewal at a time
extendLockTime() now uses one single-flight renewal loop per execution. While a MongoDB update is
pending, additional pulses are coalesced into at most one follow-up update.
lockedUntil. If concurrent pulses request different
durations, Dogs retains the longest pending duration.
The pending state is constant-size: a burst of heartbeats does not create an unbounded promise or
database-operation queue.
Keep one local watchdog
Successful renewal replaces the current stale watchdog through one code path. Dogs clears the old timer before installing its replacement, so an execution owns either zero or one active watchdog and cannot leave an unreferenced timer behind. The local deadline is derived when the MongoDB renewal starts:Terminal execution states win
Dogs rechecks execution state after every asynchronous renewal. A completed or stale execution cannot install another timer, andclearStaleTimeout() permanently disarms the watchdog for that
execution context. This also protects against a heartbeat callback that was already waiting for
MongoDB when the job finished.
If MongoDB reports that the lockId no longer owns the job, Dogs invokes stale handling exactly
once. A database error keeps the existing behavior: the execution becomes stale and the original
error rejects extendLockTime().
Application code does not change
Periodic and progress-driven renewals can safely coexist:maxTries behavior changed. Upgrade Dogs to 4.6.7 to apply the fix to
every consumer using ExecutionContext.extendLockTime().
See the Jobs guide for job and lock-time configuration.