Skip to content

Commit c59a159

Browse files
committed
test(run-engine): address caps-vs-scheduling review
Blind two-model review (a third stalled). Folds in: consistent relative-ranking register instead of proven/disproven; reframes the total cap as a capacity- confounded cross-task knob, not a cross-key fairness failure; leads the sybil result with the mechanism (unbounded per-key sum) plus the real Lua's 3-wide scan window (ZRANGEBYSCORE ... LIMIT 0, maxCount*3), which governs why per-key caps work with one heavy key and give no wait improvement once a tenant shards (sybil bumped to 20 attacker keys, now flat vs baseline). Adds the shipped combined total+per-key config and a fully-layered total+per-key+sfq treatment. Marks the caps-first sequencing as interpretation, not measured. Corrects the makespan doc (last dequeue, arrival-confounded off ckHeavyIdle), defines worstWait, notes the contention-share seed spread, and comments the speculative order() coupling.
1 parent 1afa147 commit c59a159

8 files changed

Lines changed: 2236 additions & 637 deletions

File tree

internal-packages/run-engine/src/run-queue/fairness-spike-ck/CAPS_FINDINGS.md

Lines changed: 155 additions & 101 deletions
Large diffs are not rendered by default.

internal-packages/run-engine/src/run-queue/fairness-spike-ck/capsFairness.bench.test.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ const TREATMENTS: Treatment[] = [
4646
{ label: "baseline", makeDiscipline: () => new BaselineCk() },
4747
{ label: "perKeyCap", makeDiscipline: () => new BaselineCk(), perKeyCap: PER_KEY_CAP },
4848
{ label: "totalCap", makeDiscipline: () => new BaselineCk(), totalCap: TOTAL_CAP },
49+
// the plan-of-record's shipped combined config: total cap AND per-key cap
50+
{ label: "total+perKey", makeDiscipline: () => new BaselineCk(), perKeyCap: PER_KEY_CAP, totalCap: TOTAL_CAP },
4951
{ label: "sfq", makeDiscipline: () => new SfqCk() },
5052
{ label: "drr", makeDiscipline: () => new DrrCk() },
5153
{ label: "perKeyCap+sfq", makeDiscipline: () => new SfqCk(), perKeyCap: PER_KEY_CAP },
54+
// both caps plus a fair order (the fully-layered end state)
55+
{ label: "total+perKey+sfq", makeDiscipline: () => new SfqCk(), perKeyCap: PER_KEY_CAP, totalCap: TOTAL_CAP },
5256
];
5357

5458
type CapScenario = {
@@ -95,18 +99,20 @@ const SCENARIOS: Record<string, CapScenario> = {
9599
},
96100
},
97101

98-
// sybil split: one attacker spreads its backlog across 10 concurrency keys, each
99-
// with a large backlog that stays non-empty through the light key's whole
100-
// arrival window. Each attacker key is under the same per-key cap, but the cap
101-
// frees no aggregate slot (2 attacker keys fill env, and as one empties the next
102-
// attacker key's old head is served before the newer light key). Only a fair
103-
// order rescues the light key.
102+
// sybil split: one attacker spreads its backlog across 20 concurrency keys, each
103+
// with a backlog that stays non-empty through the light key's whole arrival
104+
// window. Each attacker key is under the same per-key cap, but the cap frees no
105+
// aggregate slot (attacker keys fill env, and as one empties the next attacker
106+
// key's old head is served before the newer light key). The real CK Lua also
107+
// only scans the 3 oldest-scored variants per call (ZRANGEBYSCORE ... LIMIT 0,
108+
// maxCount*3), so with 20 attacker heads ahead of it the light head is never in
109+
// the window. Only a fair order rescues the light key.
104110
ckSybil: {
105111
lightKey: "light",
106112
config: {
107113
envConcurrencyLimit: ENV_LIMIT,
108114
tenants: [
109-
...sybilHeavy(10, 30),
115+
...sybilHeavy(20, 15),
110116
{ tenantId: "light", runCount: 20, arrival: "poisson", ratePerSec: 40, holdMsMean: 25 },
111117
],
112118
},

internal-packages/run-engine/src/run-queue/fairness-spike-ck/harness/ckDriver.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ export async function runCkScenario(config: CkDriverConfig): Promise<RunMetrics>
151151
if (config.discipline.rescore) {
152152
const active = await reader.readActiveCks(baseQueue);
153153
if (active.length > 0) {
154+
// NOTE: order() runs before every dequeue attempt, including the
155+
// terminal iteration whose dequeue returns nothing, so it can advance
156+
// a discipline's state with no matching onServiced. For the shipped
157+
// SFQ/DRR this is idempotent (SFQ floor already at the min clock; DRR's
158+
// winner already has deficit >= 1). A non-idempotent discipline dropped
159+
// in here would need its accounting made robust to that speculative call.
154160
const order = config.discipline.order(active, scoreBase + t);
155161
await rescoreCkIndex(admin, keys, baseQueue, order, Date.now());
156162
}

internal-packages/run-engine/src/run-queue/fairness-spike-ck/results/caps-ckHeavyIdle.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,42 @@
121121
}
122122
]
123123
},
124+
{
125+
"treatment": "total+perKey",
126+
"lightWait": {
127+
"mean": 1290.6516666666669,
128+
"min": 1173.15,
129+
"max": 1431.305
130+
},
131+
"worstWait": {
132+
"mean": 1290.6516666666669,
133+
"min": 1173.15,
134+
"max": 1431.305
135+
},
136+
"makespan": {
137+
"mean": 2507,
138+
"min": 2344,
139+
"max": 2638
140+
},
141+
"contentionWorst": {
142+
"mean": 1,
143+
"min": 1,
144+
"max": 1
145+
},
146+
"detailSeed0": [
147+
{
148+
"groupId": "heavy",
149+
"dequeued": 200,
150+
"weight": 1,
151+
"share": 1,
152+
"contentionShareOverWeight": 0,
153+
"meanWait": 1431.305,
154+
"waitP50": 1501,
155+
"waitP99": 2623,
156+
"waitMax": 2638
157+
}
158+
]
159+
},
124160
{
125161
"treatment": "sfq",
126162
"lightWait": {
@@ -228,6 +264,42 @@
228264
"waitMax": 2638
229265
}
230266
]
267+
},
268+
{
269+
"treatment": "total+perKey+sfq",
270+
"lightWait": {
271+
"mean": 1290.6516666666669,
272+
"min": 1173.15,
273+
"max": 1431.305
274+
},
275+
"worstWait": {
276+
"mean": 1290.6516666666669,
277+
"min": 1173.15,
278+
"max": 1431.305
279+
},
280+
"makespan": {
281+
"mean": 2507,
282+
"min": 2344,
283+
"max": 2638
284+
},
285+
"contentionWorst": {
286+
"mean": 1,
287+
"min": 1,
288+
"max": 1
289+
},
290+
"detailSeed0": [
291+
{
292+
"groupId": "heavy",
293+
"dequeued": 200,
294+
"weight": 1,
295+
"share": 1,
296+
"contentionShareOverWeight": 0,
297+
"meanWait": 1431.305,
298+
"waitP50": 1501,
299+
"waitP99": 2623,
300+
"waitMax": 2638
301+
}
302+
]
231303
}
232304
]
233305
}

internal-packages/run-engine/src/run-queue/fairness-spike-ck/results/caps-ckSkew.json

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,86 @@
257257
}
258258
]
259259
},
260+
{
261+
"treatment": "total+perKey",
262+
"lightWait": {
263+
"mean": 2840.2000000000003,
264+
"min": 2670.3333333333335,
265+
"max": 3138.4666666666667
266+
},
267+
"worstWait": {
268+
"mean": 2973.6222222222223,
269+
"min": 2766.9333333333334,
270+
"max": 3138.4666666666667
271+
},
272+
"makespan": {
273+
"mean": 3947.3333333333335,
274+
"min": 3532,
275+
"max": 4290
276+
},
277+
"contentionWorst": {
278+
"mean": 0.21268177618484008,
279+
"min": 0.1858108108108108,
280+
"max": 0.23411371237458192
281+
},
282+
"detailSeed0": [
283+
{
284+
"groupId": "heavy",
285+
"dequeued": 240,
286+
"weight": 1,
287+
"share": 0.8,
288+
"contentionShareOverWeight": 4.013377926421405,
289+
"meanWait": 1770.1916666666666,
290+
"waitP50": 1765,
291+
"waitP99": 3293,
292+
"waitMax": 3309
293+
},
294+
{
295+
"groupId": "light-1",
296+
"dequeued": 15,
297+
"weight": 1,
298+
"share": 0.05,
299+
"contentionShareOverWeight": 0.2508361204013378,
300+
"meanWait": 3138.4666666666667,
301+
"waitP50": 3102,
302+
"waitP99": 3367,
303+
"waitMax": 3367
304+
},
305+
{
306+
"groupId": "light-2",
307+
"dequeued": 15,
308+
"weight": 1,
309+
"share": 0.05,
310+
"contentionShareOverWeight": 0.23411371237458192,
311+
"meanWait": 2885.3333333333335,
312+
"waitP50": 2913,
313+
"waitP99": 3380,
314+
"waitMax": 3380
315+
},
316+
{
317+
"groupId": "light-3",
318+
"dequeued": 15,
319+
"weight": 1,
320+
"share": 0.05,
321+
"contentionShareOverWeight": 0.2508361204013378,
322+
"meanWait": 2674,
323+
"waitP50": 2769,
324+
"waitP99": 3374,
325+
"waitMax": 3374
326+
},
327+
{
328+
"groupId": "light-4",
329+
"dequeued": 15,
330+
"weight": 1,
331+
"share": 0.05,
332+
"contentionShareOverWeight": 0.2508361204013378,
333+
"meanWait": 2852.0666666666666,
334+
"waitP50": 2841,
335+
"waitP99": 3400,
336+
"waitMax": 3400
337+
}
338+
]
339+
},
260340
{
261341
"treatment": "sfq",
262342
"lightWait": {
@@ -496,6 +576,86 @@
496576
"waitMax": 44
497577
}
498578
]
579+
},
580+
{
581+
"treatment": "total+perKey+sfq",
582+
"lightWait": {
583+
"mean": 51.888888888888886,
584+
"min": 45.06666666666667,
585+
"max": 55.4
586+
},
587+
"worstWait": {
588+
"mean": 2363.016666666667,
589+
"min": 2045.6083333333333,
590+
"max": 2573.3708333333334
591+
},
592+
"makespan": {
593+
"mean": 3939.3333333333335,
594+
"min": 3564,
595+
"max": 4238
596+
},
597+
"contentionWorst": {
598+
"mean": 0.8028035775713794,
599+
"min": 0.588235294117647,
600+
"max": 0.9868421052631579
601+
},
602+
"detailSeed0": [
603+
{
604+
"groupId": "heavy",
605+
"dequeued": 240,
606+
"weight": 1,
607+
"share": 0.8,
608+
"contentionShareOverWeight": 0.588235294117647,
609+
"meanWait": 2573.3708333333334,
610+
"waitP50": 2694,
611+
"waitP99": 4222,
612+
"waitMax": 4238
613+
},
614+
{
615+
"groupId": "light-1",
616+
"dequeued": 15,
617+
"weight": 1,
618+
"share": 0.05,
619+
"contentionShareOverWeight": 1.102941176470588,
620+
"meanWait": 55.2,
621+
"waitP50": 32,
622+
"waitP99": 149,
623+
"waitMax": 149
624+
},
625+
{
626+
"groupId": "light-2",
627+
"dequeued": 15,
628+
"weight": 1,
629+
"share": 0.05,
630+
"contentionShareOverWeight": 1.102941176470588,
631+
"meanWait": 23.333333333333332,
632+
"waitP50": 18,
633+
"waitP99": 55,
634+
"waitMax": 55
635+
},
636+
{
637+
"groupId": "light-3",
638+
"dequeued": 15,
639+
"weight": 1,
640+
"share": 0.05,
641+
"contentionShareOverWeight": 1.102941176470588,
642+
"meanWait": 14.4,
643+
"waitP50": 11,
644+
"waitP99": 44,
645+
"waitMax": 44
646+
},
647+
{
648+
"groupId": "light-4",
649+
"dequeued": 15,
650+
"weight": 1,
651+
"share": 0.05,
652+
"contentionShareOverWeight": 1.102941176470588,
653+
"meanWait": 21.6,
654+
"waitP50": 17,
655+
"waitP99": 65,
656+
"waitMax": 65
657+
}
658+
]
499659
}
500660
]
501661
}

0 commit comments

Comments
 (0)