Files
windmill/benchmarks/results_batch_pull_3workers.md
HugoCasa 8c3ac22d8d feat: add as_worker_tag() helper, benchmark results and model
- Extract bunnative→nativets tag logic into ScriptLang::as_worker_tag()
- Add benchmark results for batch pull vs direct SQL (1W and 3W)
- Add throughput model script comparing batch vs SQL at scale
- Add nativets_sleep benchmark script support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:05:15 +01:00

5.0 KiB
Raw Permalink Blame History

Batch Pull Benchmark Results — 3 Workers

Date: 2026-03-06 Setup: 1 server + 3 native workers (8 subworkers each = 24 subworkers) Hardware: fedora, 1.5TB disk, ~1GB memory usage DB: PostgreSQL local, windmill 270 MiB

nativets — 1000 jobs

3W Batch 3W SQL 1W Batch 1W SQL
Duration 3.7s 3.5s 9.2s 11.4s
Throughput 272 jobs/s 288 jobs/s 108 jobs/s 88 jobs/s
vs 1W SQL +209% +227% +23% baseline

Note: First 3W SQL run was 33 jobs/s (outlier due to cold start or background activity). Rerun gave 288 jobs/s.

nativets — 10,000 jobs

3W Batch 3W SQL
Duration 34.3s 39.5s
Throughput 291 jobs/s 253 jobs/s
Improvement +15% baseline

pg_stat_statements (1000 jobs, first run)

Query 3W Batch calls 3W Batch ms 3W SQL calls 3W SQL ms
Native pull (FOR UPDATE SKIP LOCKED) 4,801 (0.01ms) 59 20,367 (0.01ms) 231
Default worker pull 353 (0.03ms) 10 880 (0.02ms) 16
DELETE from queue 1,001 (0.44ms) 439 1,001 (0.43ms) 427
INSERT into completed 1,001 (0.06ms) 61 1,001 (0.05ms) 55
INSERT job_logs 2,003 (0.03ms) 67 2,003 (0.03ms) 64
Agent token blacklist 7,867 (0.00ms) 33
Worker ping (job) 350 (0.04ms) 15
Outstanding wait time 664 (0.03ms) 21 742 (0.03ms) 23

pg_stat_database (1000 jobs, first run)

Metric 3W Batch 3W SQL
Transactions committed 22,070 29,301
Blocks read (disk) 308 395
Blocks hit (cache) 280,071 1,276,521
Tuples returned 3,368,255 28,695,830
Tuples fetched 140,864 1,089,774
Tuples inserted 6,682 6,760
Tuples updated 3,521 3,453
Tuples deleted 3,007 3,007

nativets_sleep — 1000 jobs

Each job sleeps 300-700ms (random). Theoretical max with 24 workers: ~48 jobs/s.

3W Batch 3W SQL 1W Batch 1W SQL
Duration 22.8s 22.8s 66.7s 68.2s
Throughput 43.8 jobs/s 43.8 jobs/s 15.0 jobs/s 14.7 jobs/s
vs 3W SQL ~same baseline

pg_stat_statements

Query 3W Batch calls 3W Batch ms 3W SQL calls 3W SQL ms
Native pull (FOR UPDATE SKIP LOCKED) 4,898 (0.01ms) 55 6,440 (0.02ms) 113
Default worker pull 696 (0.06ms) 43 654 (0.06ms) 37
DELETE from queue 1,001 (0.38ms) 379 1,001 (0.29ms) 290
INSERT into completed 1,001 (0.05ms) 46 1,001 (0.04ms) 43
INSERT job_logs 2,003 (0.02ms) 44 2,003 (0.02ms) 43
Agent token blacklist 7,295 (0.00ms) 31
Job runtime ping 1,499 (0.02ms) 35 1,444 (0.02ms) 35
Worker ping (job) 1,001 (0.03ms) 32 1,001 (0.03ms) 31
Job stats 549 (0.05ms) 27 493 (0.05ms) 26
Outstanding wait time 928 (0.02ms) 20 944 (0.02ms) 21

pg_stat_database

Metric 3W Batch 3W SQL
Transactions committed 25,896 18,646
Blocks read (disk) 115 204
Blocks hit (cache) 1,173,696 1,256,397
Tuples returned 21,074,537 22,063,593
Tuples fetched 1,200,878 1,262,900
Tuples inserted 7,495 7,461
Tuples updated 6,204 6,141
Tuples deleted 3,005 3,011

Analysis

nativets (CPU-bound): +15% with 10K jobs

With 10,000 jobs, batch pull achieves 291 jobs/s vs 253 jobs/s (+15%). The 1000-job runs showed similar throughput (~272-288 jobs/s) after discarding the cold-start outlier.

DB load difference (from the 1000-job first run, which captured the worst-case SQL contention):

  • 20,367 pull queries (SQL) vs 4,801 (batch) — 4x more queries
  • 28.7M tuples returned (SQL) vs 3.4M (batch) — 8.5x more index scanning
  • 1.3M cache hits (SQL) vs 280K (batch) — 4.6x more buffer activity

The batch approach consolidates all 24 subworkers into a single LIMIT 24 query, reducing contention on the queue index.

nativets_sleep (I/O-bound): No throughput difference

Both achieve 43.8 jobs/s (91% of theoretical 48 jobs/s max). When workers spend 300-700ms sleeping, DB contention isn't the bottleneck.

Batch pull still shows slightly lower DB load:

  • 4,898 pull queries vs 6,440 — 24% fewer
  • 115 disk reads vs 204 — 44% fewer

Scaling summary

Setup Batch jobs/s SQL jobs/s Batch advantage
1W × 1000 jobs 108 88 +23%
3W × 1000 jobs 272 288 ~same
3W × 10,000 jobs 291 253 +15%

At 24 subworkers, batch pull provides a consistent ~15% throughput improvement for sustained CPU-bound workloads, with significantly lower DB load (4x fewer pull queries, 8x fewer tuples scanned). The benefit grows with more workers as SKIP LOCKED contention scales O(N²).