Occasionally, when the timing works out just right, there's a race condition between refreshing a lock and releasing it. This is the code that causes it:
async release() {
debug(
`release ${this._kind} (key: ${this._key}, identifier: ${this._identifier})`
)
if (this._refreshTimeInterval > 0) {
this.stopRefresh() <---- the refresh interval schedules `_refresh` RIGHT before it's cancelled
}
if (this._acquired || this._acquiredExternally) {
await this._release() <---- the execution of `_release` occurs at the same time, "beating" `_refresh`
// BOOM, the lock is released, but refresh has still been scheduled (or was already running and was just slower)
// it fails to refresh because we no longer have the lock
}
this._acquired = false
this._acquiredExternally = false
}
The fix is pretty simple -- just don't throw if _refresh fails and the lock is no longer acquired.
A more thorough analysis:
There are basically two cases we can run into here. Either _refresh wins, in which case there's no problem, and _release can still release. No changes necessary. The other option is that _release wins, in which case _refresh will throw because it failed to release the lock. As long as _refresh checks to see whether we still have the lock before throwing, we can avoid this after-release-refresh-failure.
Closes #205
Occasionally, when the timing works out just right, there's a race condition between refreshing a lock and releasing it. This is the code that causes it:
The fix is pretty simple -- just don't throw if
_refresh
fails and the lock is no longer acquired.A more thorough analysis:
There are basically two cases we can run into here. Either
_refresh
wins, in which case there's no problem, and_release
can still release. No changes necessary. The other option is that_release
wins, in which case_refresh
will throw because it failed to release the lock. As long as_refresh
checks to see whether we still have the lock before throwing, we can avoid this after-release-refresh-failure.Reproduction:
The refresh interval is super short because it makes the error more common, but it can occur at any interval.