@@ -250,96 +250,96 @@ describe("CK virtual-time fairness on the real batched dequeue path", () => {
250250 // The head-age starvation the spike measured appears on this path when env
251251 // capacity serializes the calls (limit 1): flag OFF then always picks the
252252 // globally oldest head, which is heavy for its whole backlog.
253- redisTest ( "ckSkew: light keys stop waiting behind the heavy backlog" , async ( {
254- redisContainer,
255- } ) => {
256- const t0 = Date . now ( ) - 500_000 ;
257- const messages : ScenarioMessage [ ] = [ ] ;
258- for ( let i = 0 ; i < 120 ; i ++ ) {
259- messages . push ( { runId : `heavy-${ i } ` , ck : "heavy" , timestamp : t0 } ) ;
260- }
261- for ( let i = 0 ; i < 10 ; i ++ ) {
262- for ( let k = 0 ; k < 4 ; k ++ ) {
263- messages . push ( {
264- runId : `light${ k } -${ i } ` ,
265- ck : `light${ k } ` ,
266- timestamp : t0 + 10_000 + i * 4 + k ,
267- } ) ;
253+ redisTest (
254+ "ckSkew: light keys stop waiting behind the heavy backlog" ,
255+ async ( { redisContainer } ) => {
256+ const t0 = Date . now ( ) - 500_000 ;
257+ const messages : ScenarioMessage [ ] = [ ] ;
258+ for ( let i = 0 ; i < 120 ; i ++ ) {
259+ messages . push ( { runId : `heavy-${ i } ` , ck : "heavy" , timestamp : t0 } ) ;
268260 }
261+ for ( let i = 0 ; i < 10 ; i ++ ) {
262+ for ( let k = 0 ; k < 4 ; k ++ ) {
263+ messages . push ( {
264+ runId : `light${ k } -${ i } ` ,
265+ ck : `light${ k } ` ,
266+ timestamp : t0 + 10_000 + i * 4 + k ,
267+ } ) ;
268+ }
269+ }
270+ const scenario : Scenario = {
271+ name : "ckSkew" ,
272+ messages,
273+ envConcurrencyLimit : 1 ,
274+ holdSteps : 3 ,
275+ maxSteps : 1_000 ,
276+ } ;
277+
278+ const on = await runScenario ( redisContainer , scenario , true ) ;
279+ const off = await runScenario ( redisContainer , scenario , false ) ;
280+
281+ assertConservation ( scenario , on , off ) ;
282+
283+ const isLight = ( ck : string ) => ck . startsWith ( "light" ) ;
284+ const onWait = meanWait ( on , isLight ) ;
285+ const offWait = meanWait ( off , isLight ) ;
286+ debugLog ( "ckSkew" , { onWait, offWait, ratio : onWait / offWait } ) ;
287+
288+ // Heavy's wait may rise under the fair order; that is expected and not
289+ // asserted down.
290+ expect ( onWait ) . toBeLessThanOrEqual ( 0.3 * offWait ) ;
269291 }
270- const scenario : Scenario = {
271- name : "ckSkew" ,
272- messages,
273- envConcurrencyLimit : 1 ,
274- holdSteps : 3 ,
275- maxSteps : 1_000 ,
276- } ;
277-
278- const on = await runScenario ( redisContainer , scenario , true ) ;
279- const off = await runScenario ( redisContainer , scenario , false ) ;
280-
281- assertConservation ( scenario , on , off ) ;
282-
283- const isLight = ( ck : string ) => ck . startsWith ( "light" ) ;
284- const onWait = meanWait ( on , isLight ) ;
285- const offWait = meanWait ( off , isLight ) ;
286- debugLog ( "ckSkew" , { onWait, offWait, ratio : onWait / offWait } ) ;
287-
288- // Heavy's wait may rise under the fair order; that is expected and not
289- // asserted down.
290- expect ( onWait ) . toBeLessThanOrEqual ( 0.3 * offWait ) ;
291- } ) ;
292+ ) ;
292293
293294 // ckTrickle (spike shape): one bulk key with a 120-message backlog on an old
294295 // shared head, two trickle keys with 15 messages each on later heads. Same
295296 // serialized contention regime as ckSkew, same assertion.
296- redisTest ( "ckTrickle: trickle keys stop waiting behind the bulk backlog" , async ( {
297- redisContainer,
298- } ) => {
299- const t0 = Date . now ( ) - 500_000 ;
300- const messages : ScenarioMessage [ ] = [ ] ;
301- for ( let i = 0 ; i < 120 ; i ++ ) {
302- messages . push ( { runId : `bulk-${ i } ` , ck : "bulk" , timestamp : t0 } ) ;
303- }
304- for ( let i = 0 ; i < 15 ; i ++ ) {
305- for ( let k = 0 ; k < 2 ; k ++ ) {
306- messages . push ( {
307- runId : `trickle${ k } -${ i } ` ,
308- ck : `trickle${ k } ` ,
309- timestamp : t0 + 10_000 + i * 2 + k ,
310- } ) ;
297+ redisTest (
298+ "ckTrickle: trickle keys stop waiting behind the bulk backlog" ,
299+ async ( { redisContainer } ) => {
300+ const t0 = Date . now ( ) - 500_000 ;
301+ const messages : ScenarioMessage [ ] = [ ] ;
302+ for ( let i = 0 ; i < 120 ; i ++ ) {
303+ messages . push ( { runId : `bulk-${ i } ` , ck : "bulk" , timestamp : t0 } ) ;
311304 }
312- }
313- const scenario : Scenario = {
314- name : "ckTrickle" ,
315- messages,
316- envConcurrencyLimit : 1 ,
317- holdSteps : 3 ,
318- maxSteps : 1_000 ,
319- } ;
305+ for ( let i = 0 ; i < 15 ; i ++ ) {
306+ for ( let k = 0 ; k < 2 ; k ++ ) {
307+ messages . push ( {
308+ runId : `trickle${ k } -${ i } ` ,
309+ ck : `trickle${ k } ` ,
310+ timestamp : t0 + 10_000 + i * 2 + k ,
311+ } ) ;
312+ }
313+ }
314+ const scenario : Scenario = {
315+ name : "ckTrickle" ,
316+ messages,
317+ envConcurrencyLimit : 1 ,
318+ holdSteps : 3 ,
319+ maxSteps : 1_000 ,
320+ } ;
320321
321- const on = await runScenario ( redisContainer , scenario , true ) ;
322- const off = await runScenario ( redisContainer , scenario , false ) ;
322+ const on = await runScenario ( redisContainer , scenario , true ) ;
323+ const off = await runScenario ( redisContainer , scenario , false ) ;
323324
324- assertConservation ( scenario , on , off ) ;
325+ assertConservation ( scenario , on , off ) ;
325326
326- const isTrickle = ( ck : string ) => ck . startsWith ( "trickle" ) ;
327- const onWait = meanWait ( on , isTrickle ) ;
328- const offWait = meanWait ( off , isTrickle ) ;
329- debugLog ( "ckTrickle" , { onWait, offWait, ratio : onWait / offWait } ) ;
327+ const isTrickle = ( ck : string ) => ck . startsWith ( "trickle" ) ;
328+ const onWait = meanWait ( on , isTrickle ) ;
329+ const offWait = meanWait ( off , isTrickle ) ;
330+ debugLog ( "ckTrickle" , { onWait, offWait, ratio : onWait / offWait } ) ;
330331
331- expect ( onWait ) . toBeLessThanOrEqual ( 0.3 * offWait ) ;
332- } ) ;
332+ expect ( onWait ) . toBeLessThanOrEqual ( 0.3 * offWait ) ;
333+ }
334+ ) ;
333335
334336 // ckSybil (spike shape, the case per-key caps cannot fix): 20 attacker keys
335337 // with 8 messages each, all on older heads, and 1 light key with 10 newer
336338 // messages. 21 variants against a batch of 10 exercises the batched path
337339 // properly: flag OFF walks the age order and only reaches the light key when
338340 // the attackers are nearly drained; flag ON serves the light key from the
339341 // floor on its first fair round.
340- redisTest ( "ckSybil: many attacker keys cannot starve a light key" , async ( {
341- redisContainer,
342- } ) => {
342+ redisTest ( "ckSybil: many attacker keys cannot starve a light key" , async ( { redisContainer } ) => {
343343 const t0 = Date . now ( ) - 500_000 ;
344344 const messages : ScenarioMessage [ ] = [ ] ;
345345 for ( let i = 0 ; i < 8 ; i ++ ) {
@@ -397,72 +397,74 @@ describe("CK virtual-time fairness on the real batched dequeue path", () => {
397397
398398 // ckBalanced (spike shape, no-harm check): 4 symmetric keys with 25 messages
399399 // each. The fair order must not make the symmetric case worse.
400- redisTest ( "ckBalanced: fair order does not hurt the symmetric case" , async ( {
401- redisContainer,
402- } ) => {
403- const t0 = Date . now ( ) - 500_000 ;
404- const cks = [ "bal0" , "bal1" , "bal2" , "bal3" ] ;
405- const messages : ScenarioMessage [ ] = [ ] ;
406- for ( let i = 0 ; i < 25 ; i ++ ) {
407- for ( let k = 0 ; k < cks . length ; k ++ ) {
408- messages . push ( {
409- runId : `${ cks [ k ] } -${ i } ` ,
410- ck : cks [ k ] ! ,
411- timestamp : t0 + i * 4 + k ,
412- } ) ;
400+ redisTest (
401+ "ckBalanced: fair order does not hurt the symmetric case" ,
402+ async ( { redisContainer } ) => {
403+ const t0 = Date . now ( ) - 500_000 ;
404+ const cks = [ "bal0" , "bal1" , "bal2" , "bal3" ] ;
405+ const messages : ScenarioMessage [ ] = [ ] ;
406+ for ( let i = 0 ; i < 25 ; i ++ ) {
407+ for ( let k = 0 ; k < cks . length ; k ++ ) {
408+ messages . push ( {
409+ runId : `${ cks [ k ] } -${ i } ` ,
410+ ck : cks [ k ] ! ,
411+ timestamp : t0 + i * 4 + k ,
412+ } ) ;
413+ }
413414 }
414- }
415- const scenario : Scenario = {
416- name : "ckBalanced" ,
417- messages,
418- envConcurrencyLimit : 4 ,
419- holdSteps : 3 ,
420- maxSteps : 500 ,
421- } ;
415+ const scenario : Scenario = {
416+ name : "ckBalanced" ,
417+ messages,
418+ envConcurrencyLimit : 4 ,
419+ holdSteps : 3 ,
420+ maxSteps : 500 ,
421+ } ;
422422
423- const on = await runScenario ( redisContainer , scenario , true ) ;
424- const off = await runScenario ( redisContainer , scenario , false ) ;
423+ const on = await runScenario ( redisContainer , scenario , true ) ;
424+ const off = await runScenario ( redisContainer , scenario , false ) ;
425425
426- assertConservation ( scenario , on , off ) ;
426+ assertConservation ( scenario , on , off ) ;
427427
428- const maxPerKeyMeanWait = ( result : ScenarioResult ) =>
429- Math . max ( ...cks . map ( ( ck ) => meanWait ( result , ( c ) => c === ck ) ) ) ;
428+ const maxPerKeyMeanWait = ( result : ScenarioResult ) =>
429+ Math . max ( ...cks . map ( ( ck ) => meanWait ( result , ( c ) => c === ck ) ) ) ;
430430
431- const onMax = maxPerKeyMeanWait ( on ) ;
432- const offMax = maxPerKeyMeanWait ( off ) ;
433- debugLog ( "ckBalanced" , { onMax, offMax, ratio : onMax / offMax } ) ;
431+ const onMax = maxPerKeyMeanWait ( on ) ;
432+ const offMax = maxPerKeyMeanWait ( off ) ;
433+ debugLog ( "ckBalanced" , { onMax, offMax, ratio : onMax / offMax } ) ;
434434
435- expect ( onMax ) . toBeLessThanOrEqual ( 1.25 * offMax ) ;
436- } ) ;
435+ expect ( onMax ) . toBeLessThanOrEqual ( 1.25 * offMax ) ;
436+ }
437+ ) ;
437438
438439 // ckHeavyIdle (spike shape, work conservation): a single key with 60
439440 // messages and nothing else contending. Any extra step to drain under the
440441 // fair order is a work-conservation bug, so the step counts must be exactly
441442 // equal.
442- redisTest ( "ckHeavyIdle: a lone key drains in exactly the same steps" , async ( {
443- redisContainer ,
444- } ) => {
445- const t0 = Date . now ( ) - 500_000 ;
446- const messages : ScenarioMessage [ ] = [ ] ;
447- for ( let i = 0 ; i < 60 ; i ++ ) {
448- messages . push ( { runId : `solo-${ i } ` , ck : "solo" , timestamp : t0 + i } ) ;
449- }
450- const scenario : Scenario = {
451- name : "ckHeavyIdle" ,
452- messages,
453- envConcurrencyLimit : 25 ,
454- holdSteps : 3 ,
455- maxSteps : 300 ,
456- } ;
443+ redisTest (
444+ "ckHeavyIdle: a lone key drains in exactly the same steps" ,
445+ async ( { redisContainer } ) => {
446+ const t0 = Date . now ( ) - 500_000 ;
447+ const messages : ScenarioMessage [ ] = [ ] ;
448+ for ( let i = 0 ; i < 60 ; i ++ ) {
449+ messages . push ( { runId : `solo-${ i } ` , ck : "solo" , timestamp : t0 + i } ) ;
450+ }
451+ const scenario : Scenario = {
452+ name : "ckHeavyIdle" ,
453+ messages,
454+ envConcurrencyLimit : 25 ,
455+ holdSteps : 3 ,
456+ maxSteps : 300 ,
457+ } ;
457458
458- const on = await runScenario ( redisContainer , scenario , true ) ;
459- const off = await runScenario ( redisContainer , scenario , false ) ;
459+ const on = await runScenario ( redisContainer , scenario , true ) ;
460+ const off = await runScenario ( redisContainer , scenario , false ) ;
460461
461- assertConservation ( scenario , on , off ) ;
462+ assertConservation ( scenario , on , off ) ;
462463
463- debugLog ( "ckHeavyIdle" , { onDrainStep : on . drainStep , offDrainStep : off . drainStep } ) ;
464+ debugLog ( "ckHeavyIdle" , { onDrainStep : on . drainStep , offDrainStep : off . drainStep } ) ;
464465
465- expect ( on . drainStep ) . toBeGreaterThanOrEqual ( 0 ) ;
466- expect ( on . drainStep ) . toBe ( off . drainStep ) ;
467- } ) ;
466+ expect ( on . drainStep ) . toBeGreaterThanOrEqual ( 0 ) ;
467+ expect ( on . drainStep ) . toBe ( off . drainStep ) ;
468+ }
469+ ) ;
468470} ) ;
0 commit comments