Changelog¶
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[v1.9.1] - 2026-02-24¶
Fixed¶
release()can hang forever awaiting an in-flight renew when the network is unresponsive; now bounded to 5 secondsenqueue()did not restore socket idle timeout on the"queued"path, leaving the socket unprotected untilwait()was called- Renew loop could clobber
this.leaseafter a concurrentclose()reset it to 0 readlinehangs when the socket's readable side has already ended before listeners are attached; now checksreadableEnded/destroyedup front
Changed¶
- Unified lock and semaphore protocol functions into 5 shared helpers (
protoAcquire,protoRenew,protoRelease,protoEnqueue,protoWait), eliminating duplicated validation and response-parsing logic
[v1.9.0] - 2026-02-24¶
Added¶
connectTimeoutMsoption toDistributedLock,DistributedSemaphore, andstats()for TCP connect timeout controlsocketTimeoutMsoption toDistributedLockandDistributedSemaphorefor socket idle timeout detectionreadlinemax line length guard (1 MB) to prevent unbounded memory growth- All protocol functions (
acquire,renew,release,enqueue,waitForLock,semAcquire,semRenew,semRelease,semEnqueue,semWaitForLock) now validate inputs and throwLockErroron invalid keys, tokens, timeouts, or limits - Constructor now validates
acquireTimeoutS,leaseTtlS, andrenewRatiowithNumber.isFinitechecks to rejectNaNandInfinity
Fixed¶
release()race condition: capture socket and token before awaiting in-flight renew, preventing a concurrentclose()from nulling them- Renew failure leaving the connection open and the instance in a dirty state;
onLockLostnow automatically closes the connection so the instance can be re-acquired without{ force: true } - Socket leak in
connect()when the auth handshake throws (socket is now destroyed before re-throwing) - Socket leak in TLS listener path: remove temporary
errorand connect listeners after settling - Renew loop race: save the token before the async renew call, preventing
close()from clearing it mid-flight - NUL byte injection: validate keys and tokens against
\0characters in all protocol functions and constructors parseLeaseaccepting garbage strings (e.g."abc","NaN"); now requiresNumber.isFiniteand falls back to defaultrenewRatioacceptingNaNandInfinity; now validated withNumber.isFiniterelease()silently succeeding afterclose()— now throwsLockErrorwait()error message afterclose()(was "not connected", now "connection closed; call enqueue() again")enqueue()not suspending socket idle timeout during the blocking server call- Connect listener leaks: remove the opposing listener (
error/connect) after one fires close()to be synchronous (was unnecessarilyasync)- Sharding strategy return value validated for bounds, integrality, and
NaN
Changed¶
- Extracted shared
DistributedPrimitivebase class forDistributedLockandDistributedSemaphore
[v1.8.3] - 2026-02-24¶
Changed¶
- Renewal timers now call
.unref()so they don't keep the Node.js event loop alive if a user forgets to callrelease()/close()
[v1.8.2] - 2026-02-24¶
Fixed¶
renew/semRenewresponse check tightened to reject responses like"okay"that incorrectly matchedstartsWith("ok")startRenewloop now uses the lease value returned by the server on each renewal, recalculates the interval, and subtracts round-trip elapsed timestartRenewsaves the token before the async renew call, preventing a race whereclose()clears the token mid-flightstats()now wrapsJSON.parseand throwsLockErroron malformed JSON responses- Inconsistent default lease fallback:
enqueue/waitForLockused33instead of30 - All
sock.write()calls now await the write callback viawriteAll(), properly handling TCP backpressure readlinecleans up its internal buffer on socket error/closeclose()is now synchronous (was unnecessarilyasync)
Changed¶
- Enable
setNoDelayon sockets for lower latency - Defensive copy of
serversarray in constructors
[v1.8.0] - 2026-02-24¶
Added¶
onLockLostcallback option toDistributedLockandDistributedSemaphore, called when background lease renewal fails
Fixed¶
AcquireTimeoutErrornow extendsLockError(wasError), socatch (err instanceof LockError)catches timeouts tooreadlinenow persists leftover bytes between calls, fixing data loss when the server sends multiple lines in a single TCP segmentconnectremoves the temporaryerrorlistener after a successful connection, preventing listener leaks- Add a no-op
errorlistener on sockets after connect to prevent unhandlederrorevent crashes renewandsemRenewthrowLockErroron malformed server responses instead of silently returning-1acquire()andenqueue()now throwLockErrorif called while already connected (programmer error); pass{ force: true }to silently close the previous connection insteadauthoption no longer sends an auth command for empty stringsshardingStrategyreturn value is now validated; out-of-bounds or non-integer values throwLockError
[v1.6.0] - 2026-02-18¶
Added¶
authoption toDistributedLock,DistributedSemaphore, andstats()for token-based authentication (--auth-token)AuthErrorclass (extendsLockError) thrown on authentication failure
[v1.5.0] - 2026-02-18¶
Added¶
tlsoption toDistributedLock,DistributedSemaphore, andstats()for TLS-encrypted connections
[v1.4.0] - 2026-02-18¶
Added¶
stats()function to query server runtime statistics (connections, locks, semaphores, idle entries)- Export
Stats,StatsLock,StatsSemaphore,StatsIdleLock,StatsIdleSemaphoreinterfaces
[v1.2.0] - 2026-02-16¶
Added¶
DistributedSemaphoreclass withacquire,release,withLock,enqueue, andwait- Low-level semaphore protocol functions:
semAcquire,semRenew,semRelease,semEnqueue,semWaitForLock - Export
DistributedSemaphoreOptionsinterface - Semaphore supports up to N concurrent holders per key (configurable via
limit) - Semaphore shares the same sharding, auto-renewal, and two-phase enqueue/wait patterns as
DistributedLock
[v1.1.0] - 2026-02-15¶
Added¶
- Multi-server sharding with consistent CRC32-based hashing (matches Python's
zlib.crc32) serversoption to distribute locks across multiple dflockd instancesshardingStrategyoption for custom key-to-server routing- Export
ShardingStrategytype andstableHashShardfunction - Test suite (20 tests covering sharding, locking, and enqueue/wait)
Deprecated¶
hostandportoptions in favor ofservers
[v1.0.0] - 2026-02-15¶
Added¶
DistributedLockclass withacquire,release,withLock,enqueue, andwait- Automatic lease renewal
- Low-level protocol functions:
acquire,renew,release,enqueue,waitForLock AcquireTimeoutErrorandLockErrorerror types