@@ -120,7 +120,9 @@ export async function doWithLock<T>(
120120) : Promise < RedlockExecution < T > > {
121121 const redlock = await getClient ( opts . type , opts . customOptions )
122122 let lock : Redlock . Lock | undefined
123- let timeout
123+ let timeout : ReturnType < typeof setTimeout > | undefined
124+ let inflightExtend : Promise < void > | undefined
125+ let stopped = false
124126 try {
125127 const name = getLockName ( opts )
126128
@@ -134,8 +136,17 @@ export async function doWithLock<T>(
134136 // We keep extending the lock while the task is running
135137 const extendInIntervals = ( ) : void => {
136138 timeout = setTimeout ( async ( ) => {
137- lock = await lock ! . extend ( ttl , ( ) => opts . onExtend && opts . onExtend ( ) )
138-
139+ if ( stopped ) {
140+ return
141+ }
142+ inflightExtend = ( async ( ) => {
143+ lock = await lock ! . extend (
144+ ttl ,
145+ ( ) => opts . onExtend && opts . onExtend ( )
146+ )
147+ } ) ( )
148+ await inflightExtend
149+ inflightExtend = undefined
139150 extendInIntervals ( )
140151 } , ttl / 2 )
141152 }
@@ -161,7 +172,12 @@ export async function doWithLock<T>(
161172 throw e
162173 }
163174 } finally {
175+ stopped = true
164176 clearTimeout ( timeout )
177+ // If an extend is in-flight, wait for it to settle so that `lock` holds the
178+ // latest token before we call unlock. Extend errors are swallowed here
179+ // because the lock will expire on its own if unlock also fails.
180+ await inflightExtend ?. catch ( ( ) => { } )
165181 await lock ?. unlock ( )
166182 }
167183}
0 commit comments