@@ -47,8 +47,12 @@ class ClaimFaultProxy {
4747 private pipe ( client : Socket , upstream : URL ) : void {
4848 const server = tcpConnect ( Number ( upstream . port || 6379 ) , upstream . hostname )
4949 let pendingFault : ClaimFault | undefined
50+ // Keeps the stream's tail so a marker that TCP split across chunks still matches.
51+ let tail = ''
5052 client . on ( 'data' , ( chunk ) => {
51- if ( chunk . includes ( CLAIM_SCRIPT_MARKER ) ) {
53+ const seen = tail + chunk . toString ( 'latin1' )
54+ tail = seen . slice ( - ( CLAIM_SCRIPT_MARKER . length - 1 ) )
55+ if ( seen . includes ( CLAIM_SCRIPT_MARKER ) ) {
5256 this . claimsForwarded ++
5357 if ( this . armed ) {
5458 pendingFault = this . armed
@@ -86,8 +90,9 @@ describe.runIf(Boolean(redisUrl))('Redis idempotency claim with a lost or late r
8690 const proxy = new ClaimFaultProxy ( )
8791 let redis : Redis
8892 let direct : Redis
93+ const namespace = `claim-faults-${ generateId ( ) } `
8994 const service = new IdempotencyService ( {
90- namespace : `claim-faults- ${ generateId ( ) } ` ,
95+ namespace,
9196 inProgressTtlSeconds : 60 * 60 * 2 ,
9297 storeResultBody : false ,
9398 forceStorage : 'redis' ,
@@ -116,6 +121,8 @@ describe.runIf(Boolean(redisUrl))('Redis idempotency claim with a lost or late r
116121
117122 afterAll ( async ( ) => {
118123 redis ?. disconnect ( )
124+ const keys = ( await direct ?. keys ( `idempotency:${ namespace } :*` ) ) ?? [ ]
125+ if ( keys . length > 0 ) await direct . del ( ...keys )
119126 await direct ?. quit ( )
120127 await proxy . stop ( )
121128 } )
@@ -179,15 +186,25 @@ describe.runIf(Boolean(redisUrl))('Redis idempotency claim with a lost or late r
179186
180187 it ( "still skips a delivery whose key is another holder's live claim" , async ( ) => {
181188 const identifier = generateId ( )
189+ let holderStarted ! : ( ) => void
190+ const started = new Promise < void > ( ( resolve ) => {
191+ holderStarted = resolve
192+ } )
193+ let releaseHolder ! : ( ) => void
194+ const released = new Promise < void > ( ( resolve ) => {
195+ releaseHolder = resolve
196+ } )
182197 const holder = vi . fn ( async ( ) => {
183- await sleep ( COMMAND_TIMEOUT_MS )
198+ holderStarted ( )
199+ await released
184200 return 'holder'
185201 } )
186202 const duplicate = vi . fn ( async ( ) => 'duplicate' )
187203
188204 const running = service . executeOrSkipInProgress ( 'generic' , identifier , holder )
189- await sleep ( 50 )
205+ await started
190206 const second = await service . executeOrSkipInProgress ( 'generic' , identifier , duplicate )
207+ releaseHolder ( )
191208
192209 expect ( second ) . toEqual ( { outcome : 'in-progress' } )
193210 expect ( duplicate ) . not . toHaveBeenCalled ( )
0 commit comments