@@ -84,23 +84,50 @@ await lock(
8484);
8585```
8686
87- ### Manual Lock Control
87+ ### Manual Lock Control with Automatic Cleanup
88+
89+ Use ` await using ` for automatic cleanup on all code paths (Node.js ≥20):
8890
8991``` typescript
9092const backend = createRedisBackend (redis );
9193
92- // Acquire lock manually
94+ // Lock automatically released on scope exit
95+ {
96+ await using lock = await backend .acquire ({
97+ key: " batch:daily-report" ,
98+ ttlMs: 300000 , // 5 minutes
99+ });
100+
101+ if (lock .ok ) {
102+ // TypeScript narrows lock to include handle methods after ok check
103+ const { fence } = lock ; // Fencing token for stale lock protection
104+
105+ await generateDailyReport (fence );
106+
107+ // Extend lock for long-running tasks
108+ await lock .extend (300000 );
109+ await sendReportEmail ();
110+
111+ // Lock released automatically here
112+ } else {
113+ console .log (" Resource is locked by another process" );
114+ }
115+ }
116+ ```
117+
118+ ** For older runtimes (Node.js <20)** , use try/finally:
119+
120+ ``` typescript
93121const result = await backend .acquire ({
94122 key: " batch:daily-report" ,
95- ttlMs: 300000 , // 5 minutes
123+ ttlMs: 300000 ,
96124});
97125
98126if (result .ok ) {
99127 try {
100- const { lockId, fence } = result ; // Fencing token for stale lock protection
128+ const { lockId, fence } = result ;
101129 await generateDailyReport (fence );
102130
103- // Extend lock for long-running tasks
104131 const extended = await backend .extend ({ lockId , ttlMs: 300000 });
105132 if (! extended .ok ) {
106133 throw new Error (" Failed to extend lock" );
@@ -115,6 +142,25 @@ if (result.ok) {
115142}
116143```
117144
145+ ** Error callbacks** for disposal failures:
146+
147+ ``` typescript
148+ const backend = createRedisBackend (redis , {
149+ onReleaseError : (error , context ) => {
150+ logger .error (" Failed to release lock" , {
151+ error ,
152+ lockId: context .lockId ,
153+ key: context .key ,
154+ });
155+ },
156+ });
157+
158+ // All acquisitions automatically use the error callback
159+ await using lock = await backend .acquire ({ key: " resource" , ttlMs: 30000 });
160+ ```
161+
162+ ** Note:** SyncGuard provides a safe-by-default error handler that automatically logs disposal failures in development mode (` NODE_ENV !== 'production' ` ). In production, enable logging with ` SYNCGUARD_DEBUG=true ` or provide a custom ` onReleaseError ` callback integrated with your observability stack.
163+
118164### Ownership Checking
119165
120166``` typescript
@@ -134,12 +180,22 @@ if (info) {
134180
135181``` typescript
136182// Basic lock options
137- await lock (workFn , {
138- key: " resource:123" , // Required: unique identifier
139- ttlMs: 30000 , // Lock duration (default: 30s)
140- timeoutMs: 5000 , // Max acquisition wait (default: 5s)
141- maxRetries: 10 , // Retry attempts (default: 10)
142- });
183+ await lock (
184+ async () => {
185+ // Your critical section
186+ },
187+ {
188+ key: " resource:123" , // Required: unique identifier
189+ ttlMs: 30000 , // Lock duration (default: 30s)
190+ acquisition: {
191+ timeoutMs: 5000 , // Max acquisition wait (default: 5s)
192+ maxRetries: 10 , // Retry attempts (default: 10)
193+ retryDelayMs: 100 , // Initial retry delay (default: 100ms)
194+ backoff: " exponential" , // Backoff strategy: "exponential" | "fixed" (default: "exponential")
195+ jitter: " equal" , // Jitter strategy: "equal" | "full" | "none" (default: "equal")
196+ },
197+ },
198+ );
143199```
144200
145201### Backend Configuration
@@ -236,7 +292,7 @@ const checkRateLimit = async (userId: string) => {
236292
237293- 🔒 ** Bulletproof concurrency** - Atomic operations prevent race conditions
238294- 🛡️ ** Fencing tokens** - Monotonic counters protect against stale writes
239- - 🧹 ** Automatic cleanup** - TTL-based expiration, no manual cleanup needed
295+ - 🧹 ** Automatic cleanup** - TTL-based expiration + ` await using ` (AsyncDisposable) support
240296- 🔄 ** Backend flexibility** - Redis (performance), PostgreSQL (zero overhead), or Firestore (serverless)
241297- 🔁 ** Smart retries** - Exponential backoff with jitter handles contention
242298- 💙 ** TypeScript-first** - Full type safety with compile-time guarantees
0 commit comments