Skip to content

perf: coalesce task completions into batched transactions#59

Merged
deepjoy merged 1 commit into
mainfrom
batch-completion
Mar 19, 2026
Merged

perf: coalesce task completions into batched transactions#59
deepjoy merged 1 commit into
mainfrom
batch-completion

Conversation

@deepjoy
Copy link
Copy Markdown
Owner

@deepjoy deepjoy commented Mar 19, 2026

  • Introduce a completion coalescing channel (CompletionMsg) so spawned tasks send completions to an unbounded MPSC channel instead of individually committing to SQLite
  • The run loop drains the channel before each dispatch cycle via drain_completions, processing all queued completions in a single BEGIN IMMEDIATE / COMMIT transaction through the new TaskStore::complete_batch_with_resolve method
  • Spawned tasks use a leader-election pattern (try_lock on the shared receiver) to opportunistically drain and process the batch inline, reducing latency under high concurrency
  • Concurrency slots are freed eagerly (module counter decrement + active map removal) before the batch commits, so new work can be dispatched without waiting for the transaction
  • A final drain_completions call during shutdown ensures no completions are lost before the store closes

Spawned tasks now send completions through an unbounded channel instead
of individually committing to SQLite. The run loop drains the channel
before each dispatch cycle, processing all queued completions in a
single BEGIN IMMEDIATE / COMMIT transaction. A leader-election pattern
lets spawned tasks opportunistically drain the batch under high
concurrency. This amortizes SQLite WAL sync overhead when many tasks
complete in a burst.
@deepjoy deepjoy enabled auto-merge (squash) March 19, 2026 06:40
@deepjoy deepjoy merged commit 0c2c28b into main Mar 19, 2026
2 checks passed
@github-actions github-actions Bot mentioned this pull request Mar 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark Comparison

Click to expand
group                                       current
-----                                       -------
backoff_delay/constant                      1.00     50.5±0.60ns        ? ?/sec
backoff_delay/exponential                   1.00    217.7±0.50ns        ? ?/sec
backoff_delay/exponential_jitter            1.00    389.1±1.71ns        ? ?/sec
backoff_delay/linear                        1.00     82.1±0.13ns        ? ?/sec
batch_submit_1000                           1.00     30.4±2.08ms        ? ?/sec
byte_progress/byte_reporting_500            1.00    445.0±5.71ms        ? ?/sec
byte_progress/noop_500                      1.00   431.7±11.87ms        ? ?/sec
byte_progress_snapshot_100_tasks            1.00    127.1±3.59ms        ? ?/sec
concurrency_scaling/1                       1.00    516.4±8.27ms        ? ?/sec
concurrency_scaling/2                       1.00    431.7±8.89ms        ? ?/sec
concurrency_scaling/4                       1.00    429.8±9.37ms        ? ?/sec
concurrency_scaling/8                       1.00   427.9±11.44ms        ? ?/sec
count_by_tags/100                           1.00    122.4±6.41µs        ? ?/sec
count_by_tags/1000                          1.00    195.4±6.59µs        ? ?/sec
count_by_tags/5000                          1.00    542.8±6.88µs        ? ?/sec
dep_chain_dispatch/10                       1.00     14.4±0.42ms        ? ?/sec
dep_chain_dispatch/25                       1.00     34.0±0.64ms        ? ?/sec
dep_chain_dispatch/50                       1.00     67.5±1.04ms        ? ?/sec
dep_chain_submit/10                         1.00      3.1±0.19ms        ? ?/sec
dep_chain_submit/200                        1.00     71.5±4.71ms        ? ?/sec
dep_chain_submit/50                         1.00     16.2±0.83ms        ? ?/sec
dep_fan_in_dispatch/10                      1.00     11.8±0.21ms        ? ?/sec
dep_fan_in_dispatch/100                     1.00     92.9±2.92ms        ? ?/sec
dep_fan_in_dispatch/50                      1.00     48.5±0.79ms        ? ?/sec
dispatch_and_complete_1000                  1.00   862.3±15.92ms        ? ?/sec
dispatch_group_scaling/1                    1.00    486.2±9.38ms        ? ?/sec
dispatch_group_scaling/10                   1.00   487.3±10.29ms        ? ?/sec
dispatch_group_scaling/100                  1.00   495.5±13.09ms        ? ?/sec
dispatch_group_scaling/50                   1.00   493.2±11.57ms        ? ?/sec
dispatch_no_groups_500                      1.00    434.3±9.58ms        ? ?/sec
dispatch_one_group_500                      1.00   486.9±12.10ms        ? ?/sec
dispatch_permanent_failure_500              1.00   451.6±12.84ms        ? ?/sec
history_by_type/100                         1.00  1290.4±28.85µs        ? ?/sec
history_by_type/1000                        1.00  1225.6±12.95µs        ? ?/sec
history_by_type/5000                        1.00  1256.9±44.14µs        ? ?/sec
history_query/100                           1.00    708.4±9.18µs        ? ?/sec
history_query/1000                          1.00    702.2±9.59µs        ? ?/sec
history_query/5000                          1.00    775.2±9.49µs        ? ?/sec
history_stats/100                           1.00    136.7±2.15µs        ? ?/sec
history_stats/1000                          1.00    332.4±8.82µs        ? ?/sec
history_stats/5000                          1.00   1197.0±5.30µs        ? ?/sec
mixed_priority_dispatch_500                 1.00    429.1±9.47ms        ? ?/sec
peek_next/100                               1.00    117.0±5.83µs        ? ?/sec
peek_next/1000                              1.00    114.3±4.29µs        ? ?/sec
peek_next/5000                              1.00    114.1±5.22µs        ? ?/sec
query_by_tags/100                           1.00  1410.8±103.71µs        ? ?/sec
query_by_tags/1000                          1.00     12.6±1.22ms        ? ?/sec
query_by_tags/5000                          1.00     69.5±6.74ms        ? ?/sec
retryable_dead_letter/constant              1.00    210.6±5.04ms        ? ?/sec
retryable_dead_letter/exponential           1.00    209.7±5.25ms        ? ?/sec
retryable_dead_letter/exponential_jitter    1.00    210.2±5.57ms        ? ?/sec
retryable_dead_letter/linear                1.00    211.7±5.26ms        ? ?/sec
submit_1000_tasks                           1.00    169.4±6.24ms        ? ?/sec
submit_dedup_hit_1000                       1.00    219.2±7.26ms        ? ?/sec
submit_with_tags/0                          1.00     85.8±5.39ms        ? ?/sec
submit_with_tags/10                         1.00    212.3±7.44ms        ? ?/sec
submit_with_tags/20                         1.00   350.0±13.00ms        ? ?/sec
submit_with_tags/5                          1.00    149.5±6.14ms        ? ?/sec
tag_values/100                              1.00    129.7±5.17µs        ? ?/sec
tag_values/1000                             1.00    187.9±4.95µs        ? ?/sec
tag_values/5000                             1.00    450.8±5.76µs        ? ?/sec

deepjoy pushed a commit that referenced this pull request Mar 19, 2026
## 🤖 New release

* `taskmill`: 0.5.1 -> 0.5.2 (✓ API compatible changes)

<details><summary><i><b>Changelog</b></i></summary><p>

<blockquote>

## [0.5.2](v0.5.1...v0.5.2)
- 2026-03-19

### Other

- reduce SQL round-trips and CPU overhead in scheduler hot paths
([#60](#60))
- coalesce task completions into batched transactions
([#59](#59))
- reduce SQL round-trips in scheduler hot paths
([#57](#57))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant