Skip to content

fix(engine): round-robin workers with equal label affinity - #4697

Merged
grutt merged 4 commits into
mainfrom
fix/scheduler-equal-label-round-robin
Aug 14, 2026
Merged

fix(engine): round-robin workers with equal label affinity#4697
grutt merged 4 commits into
mainfrom
fix/scheduler-equal-label-round-robin

Conversation

@grutt

@grutt grutt commented Aug 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Label ranking was resetting the assignment ring to offset 0, so workers with the same desired_worker_labels score packed onto the first UUID until its slots filled.
  • Keep higher affinity first, but round-robin within the highest-rank tied group (and skip the sort when every worker is tied).
  • Regression test assigns two equal-affinity workers with spare slots and asserts they are not packed onto the same worker.

Test plan

  • go test ./pkg/scheduling/v1/
  • TestScheduler_AssignSingleton_EqualLabelsRoundRobin fails against main packing behavior and passes on this branch
  • Confirm sticky HARD/SOFT still prefer the desired worker
  • Spot-check a labeled fleet: consecutive tasks should cycle workers instead of filling worker 1..N

Label ranking reset the assignment ring to offset 0, so equal-score workers packed onto the first UUID. Rotate only the highest-rank tied group so the fleet is cycled without skipping a better match.
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hatchet-docs Ready Ready Preview Aug 14, 2026 3:49pm

Request Review

@github-actions github-actions Bot added the engine Related to the core Hatchet engine label Aug 14, 2026
@grutt
grutt requested review from juliusgeo and mrkaye97 August 14, 2026 14:52
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=false). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

Comment thread pkg/scheduling/v1/scheduler_test.go Outdated

regionLabel := &sqlcv1.ListManyWorkerLabelsRow{
Key: "region",
StrValue: pgtype.Text{String: "us-east-1", Valid: true},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small nit, but I've been trying to use sqlchelpers.TextFromStr instead (when I have time to do stuff, I want to keep removing more uses of pgtype.Foobar from everywhere

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pre-existing condition on the other tests. this is a good candidate for a linter rule 😅 because afik no one else is reviewing for this.

Comment on lines +527 to +532
first := assignOne(t, s, a, testQI(tenantId, "A", 1), desired, defaultRequest(), nil, nil)
require.True(t, first.succeeded)

second := assignOne(t, s, a, testQI(tenantId, "A", 2), desired, defaultRequest(), nil, nil)
require.True(t, second.succeeded)
require.NotEqual(t, first.workerId, second.workerId, "equal-affinity workers with free slots must not pack onto the same worker")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggle with these types of tests a bit because there are actually more cases here. There's the (alleged) old case, which was us always scheduling on the first worker (although unclear to me if "first" is coming from db ordering, in-memory ordering, etc.). Then there's the new one which is where we try to round robin. But there's also a possibility where we would assign semi-randomly, in which case this test would sometimes pass if it just happens that we get round robin behavior because e.g. the ordering changes for some reason.

This is the same issue we have in e.g. the tests for dynamic label assign, and so I basically wrote those tests to be "probabilistic," meaning that if we run N tasks with label X provided on two workers with labels X and Y, the probability of all N running on a single worker by random chance (the second case) is (1/2)^N, so then we just make N sufficiently big to be confident the feature is working.

I think we should do that here too

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this path is not probabilistic and this covers the case on main (and fails). this wont cover reliably if we decide to make this path probabilistic in the future, but it should be fine for today

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to be clear, the other path I was describing also isn't probabilistic. But the question is 1) if it becomes probabilistic, this test will be flaky instead of reliably failing and 2) how confident are you that it's not probabilistic?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. yes, but do we expect to make this path probabilistic?
  2. 100%

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I'm misunderstanding 😅 these methods: 1) loop over things from the database and 2) loop over maps. At the very least, map iteration in Go is intentionally non-deterministic, so are we really 100% sure?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay I see the thing I was missing - there's a byte sort of the worker ids on line 6something that gets rid of the risk from the maps here, so I think this is fine 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just double checked to be extra certain.

the replenish step is where the nondeterminism is possible, but we sort there.
we're not looping over a map on this path

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github race, yes

Comment thread pkg/scheduling/v1/scheduler.go Outdated
candidates := a.workerIds
offset := a.ringOffset
a.ringOffset++
tiedLen := len(candidates)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it took me like 10 mins of reading the code to understand that this was the number of workers that are tied for "first place" in the rank order - I feel like naming it something to do with that would be good?

Comment thread pkg/scheduling/v1/scheduler.go Outdated
Comment on lines +1038 to +1039
for i := 0; i < tiedLen; i++ {
workerId := candidates[(offset+i)%tiedLen]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by the modulus math here - to me, this read like it'll still select the first candidate? maybe I'm misunderstanding why this works

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some clarifying comments here

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Benchmark results

goos: linux
goarch: amd64
pkg: github.com/hatchet-dev/hatchet/pkg/scheduling/v1
cpu: AMD Ryzen 9 7950X3D 16-Core Processor          
                                                                    │ /tmp/old.txt  │             /tmp/new.txt             │
                                                                    │    sec/op     │    sec/op      vs base               │
BatchSchedulerWorstCaseMemory-8                                         1.558 ±  5%     1.540 ±  4%        ~ (p=0.699 n=6)
RateLimiter-8                                                          48.67µ ± 14%    53.63µ ± 25%        ~ (p=0.093 n=6)
Scheduler_ConcurrentAssignReplenishSnapshot-8                          344.4µ ±  7%    253.7µ ± 12%  -26.34% (p=0.002 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8           5.351m ±  4%    4.728m ± 10%  -11.64% (p=0.002 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8           6.046m ±  3%    5.464m ± 12%   -9.62% (p=0.026 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8           12.75m ±  3%    11.55m ±  3%   -9.37% (p=0.002 n=6)
Scheduler_InventoryShape_ReplenishMust/small_dense-8                   42.66µ ± 10%    40.34µ ±  5%   -5.44% (p=0.041 n=6)
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8      8.166m ±  6%    7.703m ±  7%   -5.67% (p=0.015 n=6)
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8      1.597m ±  7%    1.392m ±  9%  -12.84% (p=0.002 n=6)
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8             488.3µ ±  9%    421.6µ ± 10%  -13.66% (p=0.009 n=6)
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                2.506m ±  7%    2.227m ±  3%  -11.11% (p=0.002 n=6)
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                 11.97µ ±  8%    10.99µ ±  2%   -8.20% (p=0.002 n=6)
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8    4.193m ± 16%    3.718m ±  2%  -11.32% (p=0.002 n=6)
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8    56.93µ ±  6%    56.43µ ±  3%        ~ (p=0.937 n=6)
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8           64.94µ ±  5%    67.95µ ±  6%        ~ (p=0.065 n=6)
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8              970.0µ ±  1%   1010.7µ ±  3%   +4.20% (p=0.004 n=6)
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                  56.75µ ±  5%    53.17µ ± 42%        ~ (p=1.000 n=6)
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8    160.20µ ±  6%    82.81µ ± 11%  -48.31% (p=0.002 n=6)
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8    125.19µ ±  3%    86.27µ ±  8%  -31.09% (p=0.002 n=6)
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8            23.31µ ±  4%    33.91µ ± 17%  +45.45% (p=0.002 n=6)
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8               57.72µ ± 10%    38.75µ ± 13%  -32.87% (p=0.002 n=6)
Scheduler_Replenish_DenseSharedActions-8                               5.013m ±  5%    4.724m ±  6%   -5.76% (p=0.009 n=6)
geomean                                                                592.9µ          532.4µ        -10.21%

                                │ /tmp/old.txt │         /tmp/new.txt          │
                                │  bytes/item  │ bytes/item  vs base           │
BatchSchedulerWorstCaseMemory-8     70.17 ± 0%   70.17 ± 0%  ~ (p=1.000 n=6) ¹
¹ all samples are equal

                                │ /tmp/old.txt  │           /tmp/new.txt           │
                                │ worst_case_MB │ worst_case_MB  vs base           │
BatchSchedulerWorstCaseMemory-8     1.403k ± 0%     1.403k ± 0%  ~ (p=1.000 n=6) ¹
¹ all samples are equal

                                │   /tmp/old.txt   │           /tmp/new.txt            │
                                │ worst_case_bytes │ worst_case_bytes  vs base         │
BatchSchedulerWorstCaseMemory-8        1.403G ± 0%        1.403G ± 0%  ~ (p=0.939 n=6)

                                                                    │ /tmp/old.txt │             /tmp/new.txt             │
                                                                    │     B/op     │     B/op      vs base                │
BatchSchedulerWorstCaseMemory-8                                       6.400Gi ± 0%   6.400Gi ± 0%       ~ (p=0.455 n=6)
RateLimiter-8                                                         137.7Ki ± 0%   137.7Ki ± 0%       ~ (p=0.552 n=6)
Scheduler_ConcurrentAssignReplenishSnapshot-8                         256.8Ki ± 0%   259.0Ki ± 1%  +0.88% (p=0.002 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8          2.263Mi ± 0%   2.263Mi ± 0%  -0.00% (p=0.009 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8          2.663Mi ± 0%   2.663Mi ± 0%       ~ (p=0.260 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8          4.426Mi ± 0%   4.426Mi ± 0%       ~ (p=0.563 n=6)
Scheduler_InventoryShape_ReplenishMust/small_dense-8                  28.03Ki ± 0%   28.03Ki ± 0%       ~ (p=0.162 n=6)
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8     3.732Mi ± 0%   3.733Mi ± 0%       ~ (p=0.699 n=6)
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8     3.132Mi ± 0%   3.132Mi ± 0%       ~ (p=0.937 n=6)
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8            665.1Ki ± 0%   665.1Ki ± 0%       ~ (p=1.000 n=6)
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8               1.872Mi ± 0%   1.872Mi ± 0%       ~ (p=0.468 n=6)
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  168.0 ± 0%     168.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8     168.0 ± 0%     168.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8     168.0 ± 0%     168.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8            168.0 ± 0%     168.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8               168.0 ± 0%     168.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                 39.31Ki ± 0%   39.31Ki ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8    151.6Ki ± 0%   151.6Ki ± 0%       ~ (p=0.455 n=6)
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8    119.6Ki ± 0%   119.6Ki ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8           13.72Ki ± 0%   13.72Ki ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8              40.61Ki ± 0%   40.61Ki ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_Replenish_DenseSharedActions-8                              2.166Mi ± 0%   2.166Mi ± 0%       ~ (p=0.102 n=6)
geomean                                                               105.3Ki        105.3Ki       +0.04%
¹ all samples are equal

                                                                    │ /tmp/old.txt │            /tmp/new.txt             │
                                                                    │  allocs/op   │  allocs/op   vs base                │
BatchSchedulerWorstCaseMemory-8                                        3.720k ± 0%   3.721k ± 0%       ~ (p=0.524 n=6)
RateLimiter-8                                                          1.022k ± 0%   1.022k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_ConcurrentAssignReplenishSnapshot-8                          1.065k ± 1%   1.067k ± 1%       ~ (p=0.327 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8           6.684k ± 0%   6.683k ± 0%       ~ (p=0.061 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8           13.58k ± 0%   13.58k ± 0%       ~ (p=1.000 n=6)
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8           41.70k ± 0%   41.70k ± 0%       ~ (p=1.000 n=6)
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    333.0 ± 0%    333.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8      13.51k ± 0%   13.51k ± 0%       ~ (p=1.000 n=6)
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8      56.57k ± 0%   56.57k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8             6.196k ± 0%   6.196k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                15.21k ± 0%   15.21k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8     4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8     4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8            4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8               4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8      430.0 ± 0%    430.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8      430.0 ± 0%    430.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8             277.0 ± 0%    277.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8                430.0 ± 0%    430.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_Replenish_DenseSharedActions-8                               4.891k ± 0%   4.890k ± 0%       ~ (p=0.242 n=6)
geomean                                                                 604.4         604.5       +0.01%
¹ all samples are equal

                                                                    │ /tmp/old.txt │            /tmp/new.txt             │
                                                                    │ action_rows  │ action_rows  vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                          10.00k ± 0%   10.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8           50.00k ± 0%   50.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8           50.00k ± 0%   50.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8           50.00k ± 0%   50.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    200.0 ± 0%    200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8      86.20k ± 0%   86.20k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8      1.200k ± 0%   1.200k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8             1.000k ± 0%   1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                20.00k ± 0%   20.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  200.0 ± 0%    200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8    86.20k ± 0%   86.20k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8    1.200k ± 0%   1.200k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8           1.000k ± 0%   1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8              20.00k ± 0%   20.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   200.0 ± 0%    200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8     86.20k ± 0%   86.20k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8     1.200k ± 0%   1.200k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8            1.000k ± 0%   1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8               20.00k ± 0%   20.00k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                5.421k        5.421k       +0.00%
¹ all samples are equal

                                                                    │ /tmp/old.txt │            /tmp/new.txt             │
                                                                    │   actions    │   actions    vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                           200.0 ± 0%    200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8            500.0 ± 0%    500.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8            500.0 ± 0%    500.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8            500.0 ± 0%    500.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    20.00 ± 0%    20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8      1.724k ± 0%   1.724k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8       30.00 ± 0%    30.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8             2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  20.00 ± 0%    20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8    1.724k ± 0%   1.724k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8     30.00 ± 0%    30.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8           2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8              2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   20.00 ± 0%    20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8     1.724k ± 0%   1.724k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8      30.00 ± 0%    30.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8            2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8               2.000k ± 0%   2.000k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                 346.2         346.2       +0.00%
¹ all samples are equal

                                                                   │ /tmp/old.txt │            /tmp/new.txt            │
                                                                   │  batch_size  │ batch_size  vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                          64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                  64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8     64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8     64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8            64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8               64.00 ± 0%   64.00 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                64.00        64.00       +0.00%
¹ all samples are equal

                                                                    │ /tmp/old.txt │            /tmp/new.txt            │
                                                                    │  slot_types  │ slot_types  vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                           1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8            1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8            4.000 ± 0%   4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8            16.00 ± 0%   16.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8       1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8       1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8              1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                 1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8     1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8     1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8            1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8               1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8      1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8      1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8             1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8                1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                 1.245        1.245       +0.00%
¹ all samples are equal

                                                                    │   /tmp/old.txt   │               /tmp/new.txt               │
                                                                    │ slots_per_worker │ slots_per_worker  vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                               20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8                20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8                20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8                20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                        4.000 ± 0%         4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8           20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8          1.400k ± 0%        1.400k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8                  20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                     20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                      4.000 ± 0%         4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8         20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8        1.400k ± 0%        1.400k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8                20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8                   20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                       4.000 ± 0%         4.000 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8          20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8         1.400k ± 0%        1.400k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8                 20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8                    20.00 ± 0%         20.00 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                     30.34              30.34       +0.00%
¹ all samples are equal

                                                                    │ /tmp/old.txt │             /tmp/new.txt             │
                                                                    │ unique_slots │ unique_slots  vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                          1.000k ± 0%    1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8           2.000k ± 0%    2.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8           8.000k ± 0%    8.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8           32.00k ± 0%    32.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    40.00 ± 0%     40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8      1.000k ± 0%    1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8      56.00k ± 0%    56.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8             4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  40.00 ± 0%     40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8    1.000k ± 0%    1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8    56.00k ± 0%    56.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8           4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8              4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   40.00 ± 0%     40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8     1.000k ± 0%    1.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8     56.00k ± 0%    56.00k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8            4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8               4.000k ± 0%    4.000k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                2.444k         2.444k       +0.00%
¹ all samples are equal

                                                                    │ /tmp/old.txt │            /tmp/new.txt            │
                                                                    │   workers    │  workers    vs base                │
Scheduler_ConcurrentAssignReplenishSnapshot-8                           50.00 ± 0%   50.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_01-8            100.0 ± 0%   100.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_04-8            100.0 ± 0%   100.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_SlotTypeCardinality/slot_types_16-8            100.0 ± 0%   100.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/small_dense-8                    10.00 ± 0%   10.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_action_fanout-8       50.00 ± 0%   50.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/dense_high_slot_capacity-8       40.00 ± 0%   40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/sparse_low_fanout-8              200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ReplenishMust/partitioned_20-8                 200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/small_dense-8                  10.00 ± 0%   10.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_action_fanout-8     50.00 ± 0%   50.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/dense_high_slot_capacity-8     40.00 ± 0%   40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/sparse_low_fanout-8            200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_ActiveCountScan/partitioned_20-8               200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/small_dense-8                   10.00 ± 0%   10.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_action_fanout-8      50.00 ± 0%   50.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/dense_high_slot_capacity-8      40.00 ± 0%   40.00 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/sparse_low_fanout-8             200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
Scheduler_InventoryShape_TryAssignBatch/partitioned_20-8                200.0 ± 0%   200.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                 64.71        64.71       +0.00%
¹ all samples are equal

Compared against main (ca5bb59)

@grutt
grutt merged commit 4253c86 into main Aug 14, 2026
61 checks passed
@grutt
grutt deleted the fix/scheduler-equal-label-round-robin branch August 14, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine Related to the core Hatchet engine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants