What it is
Batching allows Trunk Merge Queue to test multiple pull requests together as a single unit, rather than testing them one at a time. When batching is enabled, Trunk intelligently groups compatible PRs and runs your test suite once for the entire batch. If the batch passes, all PRs in the batch merge together, dramatically reducing total test time.Why use it
- Reduce total test time by 60-80% - Instead of running your full test suite 10 times for 10 PRs, you run it 2-3 times for the same PRs grouped into batches. More PRs merged with less CI time.
- Increase merge throughput - Process 3-5x more PRs per hour compared to testing individually. A queue that handled 20 PRs/hour can now handle 60-100 PRs/hour with batching.
- Lower CI costs - Fewer test runs means lower CI/CD infrastructure costs. Teams report 50-70% reduction in CI minutes consumed by merge queue testing.
- Faster time-to-production - PRs spend less time waiting in queue. What used to take hours can now take minutes, getting features and fixes to production faster.
How to enable
Batching is disabled by default and must be explicitly enabled.
Configuration options
With Batching enabled, you can configure two options:- Maximum wait time - The maximum amount of time the Merge Queue should wait to fill the target batch size before beginning testing. A higher maximum wait time will cause the Time-In-Queue metric to increase but have the net effect of reducing CI costs per pull request.
- Target batch size - The largest number of entries in the queue that will be tested in a single batch. A larger target batch size will help reduce CI cost per pull request but require more work to be performed when progressive failures necessitate bisection.
A good place to start is with the defaults, Maximum wait time set to 5 (minutes) and Target batch size set to 4 (PRs).
Excluding PRs from Batching
Sometimes you need a specific PR to test in isolation, even when batching is enabled for your queue. You can prevent individual PRs from batching without changing your overall batching configuration.When to use this
- High-risk changes — Infrastructure updates, database migrations, or changes that could affect other PRs in unpredictable ways
- Debugging batch failures — Isolate a suspected problematic PR to confirm it tests correctly on its own
- Critical hotfixes — Make sure a time-sensitive fix isn’t delayed or affected by other PRs in a batch
- Flaky PR isolation — Test a PR with known flaky behavior separately to avoid impacting other PRs
How to exclude a PR from batching
Option 1: Using the/trunk merge command
Add the --no-batch flag when submitting your PR:
noBatch: true when calling the /submitPullRequest endpoint:
How it works
When a PR is submitted with no-batch:- Queue position is unchanged — The PR maintains its position in the queue based on when it was submitted
- No restarts triggered — Submitting a no-batch PR doesn’t restart testing for other PRs already in the queue
- Tests in isolation — The PR is guaranteed to test by itself, not grouped with other PRs
- Other PRs unaffected — Batching continues normally for all other PRs in the queue
Excluding a PR from batching only affects that specific PR. Your queue’s batching settings and other PRs remain unaffected.
Bisection Concurrency
When a batch fails, Trunk automatically splits it apart (bisects) to identify which PR caused the failure. You can configure a separate, higher concurrency limit specifically for these bisection tests to isolate failures faster without impacting your main queue.
Why separate bisection concurrency?
By default, bisection tests use the same concurrency limit as your main queue. This means:- Bisection can slow down other PRs waiting to merge
- Developers wait longer to learn which PR broke the batch
- Your main queue’s throughput decreases during failure investigation
- Speed up failure isolation - Run bisection tests at higher concurrency to identify problems faster
- Maintain queue throughput - Keep your main queue running at optimal capacity during bisection
- Optimize each workflow independently - Be aggressive about isolating failures without impacting successful PR flow
How it works
When you set a higher bisection concurrency:- Main queue concurrency controls how many PRs test simultaneously in the normal queue
- Bisection concurrency controls how many PRs test simultaneously during failure isolation
- Both run independently - bisection tests don’t count against your main queue limit
Example scenario:
Example scenario:
- Main queue concurrency: 5
- Bisection concurrency: 15
- Batch
ABCDfails and needs to be split
Configuring bisection concurrency
Navigate to Merge Queue → [your repository] → Settings → Batching:- Enable Batching (if not already enabled)
- Find the Bisection Concurrency setting
- Set a value higher than your main Testing Concurrency for faster failure isolation
- Monitor your CI resource usage and adjust as needed
Recommended settings
- Conservative approach
- Balanced approach
- Aggressive approach
- Main queue concurrency: 5
- Bisection concurrency: 10
- Good for: Teams managing CI costs carefully
When to use higher bisection concurrency
Consider increasing bisection concurrency if:- Developers frequently wait for bisection results to know what to fix
- Your CI system has spare capacity during failure investigation
- Large batches fail and take a long time to isolate the culprit
- Fast feedback on failures is critical to your workflow
Monitoring and optimization
Track these metrics to optimize your bisection concurrency:- Time to isolate failures - How long it takes to identify which PR broke a batch
- CI resource usage during bisection - Are you maxing out your runners?
- Developer wait time - How long developers wait for failure feedback
- Main queue throughput during bisection - Is bisection slowing down other PRs?
Start with bisection concurrency 2x your main queue concurrency, monitor the impact, and adjust based on your team’s priorities and CI capacity.
Best practices
- Set bisection concurrency higher than main queue - This is the whole point of the feature
- Monitor CI costs - Higher bisection concurrency means more runners during failures
- Start conservative - Begin with 2x main concurrency and increase gradually
- Combine with other optimizations - Works best alongside Pending Failure Depth and Anti-flake Protection
- Don’t set too high - Extremely high bisection concurrency can overwhelm CI systems
- Don’t set lower than main queue - This defeats the purpose and slows down bisection
Test Caching During Bisection
When a batch fails and Trunk splits it apart to find the PR that broke it, the same combination of PRs can come up for testing more than once. With Skip Redundant Bisection Tests enabled, Trunk doesn’t test a combination again if it has already failed during the same bisection. It reuses that failure instead, which saves a CI run and speeds up finding the failing PR.How it works
Bisection detaches the failed batch from the rest of the queue, so each test in a bisection runs a group of PRs on top of the PRs ahead of it in the bisection only. Before starting one, Trunk checks whether an earlier test in the current bisection already failed with the same set of PRs. A failed test of a larger set also counts once its extra PRs have merged. For example, ifAB failed and A has since merged, testing B alone would repeat that failure. If an earlier failure covers the test, Trunk skips the CI run and reuses that failure. What happens next depends on whether the failure already shows which PR caused it:
- A single PR with nothing ahead of it: the reused failure already names it. This is usually the case where the other PRs in the failed test have merged. The PR fails with
TEST_RUN_FAILED_BY_OPTIMIZATIONand is removed from the queue. - A single PR with tests still running ahead of it: it’s re-tested on its own straight away, without the PRs ahead of it.
- A group of PRs: it’s split and re-tested straight away, alongside the rest of the bisection, instead of waiting for the tests ahead of it to finish.
Example bisection with test caching
Example bisection with test caching
- Batch
ABCDfails testing (main ← ABCD). - Trunk splits the batch into
ABandCD.ABis tested asmain ← AB, andCDwould be tested on top of it, asmain ← AB ← CD. main ← AB ← CDis the same set of PRs asmain ← ABCD, which already failed. Trunk doesn’t run it.- Instead of waiting for
AB’s result, Trunk splitsCDright away.main ← Candmain ← C ← Dstart testing alongsidemain ← AB. - If
ABthen fails too, it’s split intomain ← Aandmain ← A ← B. The second is the same set of PRs as the failedmain ← AB, so it’s skipped, andBis re-tested on its own straight away, asmain ← B.
Benefits
Lower CI spend: Every reused failure is a CI run that doesn’t happen. This matters most for large batches and expensive test suites. Faster failure isolation: Bisection doesn’t wait on a test whose result is already known, and the PRs it skipped start their next tests right away, so developers learn sooner which PR broke the batch.When test caching applies
A test is skipped only when all of these are true:- Skip Redundant Bisection Tests is enabled for the queue
- A batch has failed and is being bisected
- An earlier test in the same bisection failed with the same set of PRs, or with a larger set whose extra PRs have all merged since
- The base hasn’t changed since that failed test. Trunk discards a reused failure if another PR has merged through the queue since then, or if the bisection’s PRs were restarted. Restarts include a push to the target branch, a manual or API restart, and a PR merged outside the queue.
- Initial batch testing, before any failure
- Combinations that passed. Passing results are never reused.
- Results from a different bisection
Enabling it
Test caching is off unless you turn it on. In the Merge Queue tab, select your queue, click Settings, and enable Skip Redundant Bisection Tests under Batching. You can also set it withoptimizationMode: bisectionSkipRedundantTests when you update a queue through the API, or with optimization_mode = "BISECTION_SKIP_REDUNDANT_TESTS" in Terraform.
How this works with other features
- Bisection Concurrency - Run the bisection tests that are needed faster, and skip the ones that aren’t
- Pending Failure Depth - Keep more PRs in the queue during failure recovery
- Optimistic Merging - Merge successful batches while bisection runs in the background
Fine tuning batch sizes
Signs your batch size is too large:- Batches frequently fail and need to be split
- Long wait times to form full batches
- Test suite times out or becomes unstable
- Not seeing significant throughput improvement
- Batches form immediately (could handle more PRs)
- Still consuming lots of CI resources
- Test suite speed (faster tests = larger batches)
- Test stability (more flaky tests = smaller batches)
- PR submission rate (more PRs = larger batches)
Tradeoffs and considerations
The downsides here are very limited. Since batching combines multiple pull requests into one, you essentially give up the proof that every pull request in complete isolation can safely be merged into your protected branch. In the unlikely case that you have to revert a change from your protected branch or do a rollback, you will need to retest that revert or submit it to the queue to make sure nothing has broken. In practice, this re-testing is required in almost any case, regardless of how it was originally merged, and the downsides are fairly limited.Common misconceptions
- Misconception: “Batching merges multiple PRs into a single commit”
- Reality: No! Each PR is still merged as a separate commit. Batching only affects testing, not merging.
- Misconception: “If a batch fails, all PRs in the batch fail”
- Reality: Trunk automatically splits the batch and retests to identify only the failing PR(s). Passing PRs still merge.
- Misconception: “Batching always makes the queue faster”
- Reality: Batching is most effective with stable tests and high PR volume. For low-traffic repos or flaky tests, the overhead may outweigh benefits.
Related features
Batching works exceptionally well with these optimizations: Predictive testing - Batching builds on predictive testing. Batches are tested against the projected future state of main, just like individual PRs. These features complement each other perfectly. Optimistic merging - While a batch is testing, the next batch can begin forming and testing optimistically. Combining batching with optimistic merging provides maximum throughput. Configure both for best results. Pending failure depth - When a batch fails, pending failure depth controls how many successor test runs the system waits on before transitioning the failed batch. Combined with optimistic merging, this can prevent premature bisection of a batch that only failed due to a transient issue. Anti-flake protection - Essential companion to batching. Reduces false batch failures caused by flaky tests, making batching more reliable and efficient.How batching interacts with parallel queues and PFD
Batching, parallel queues, pending failure depth, and optimistic merging each shape the queue independently — but they also interact in ways that are easy to miss when reading each page in isolation. This section covers the cross-feature behaviors customers most often ask about.Batching is another form of lane
It’s tempting to think of batching as something that happens within a parallel-queue lane. It’s clearer to think of a batch as itself another form of lane: a temporary testing-lane shape that groups PRs together for a single test run. Parallel queues split the queue into lanes based on impacted targets. Batching groups PRs into a single test unit. Both produce a “thing the queue tests as one.” The two are orthogonal — you can run batching inside a parallel-queue setup, and the graph view shows the resulting lane shapes.Use the merge queue graph view to see how batches and parallel-queue lanes overlap for your queue’s current state.
PR batch eligibility
Trunk groups PRs into a batch based on:- FIFO order — PRs are considered for batching in the order they were submitted to the queue.
- Target overlap — In parallel mode, PRs only batch together if they share enough impacted-target overlap to belong to the same lane. PRs with disjoint target sets stay in separate lanes and don’t batch with each other.
- Maximum wait time and target batch size — A batch forms when either the target batch size fills, or the maximum wait time elapses with at least one PR present.
--no-batchflag — PRs submitted with--no-batch(ornoBatch: truevia API) test in isolation, regardless of the batching configuration.
An ALL-impacting PR serializes every downstream PR
A PR that impacts ALL targets becomes a prerequisite of every PR queued behind it. Even a downstream PR that shares no targets with it is serialized behind it and cannot test in parallel until the ALL-impacting PR resolves. Declaring ALL impact means the PR could affect anything, so Trunk cannot safely test anything queued after it in parallel. Concretely: if PR-A impacts ALL targets and PR-B impacts onlydocs, PR-B cannot test in parallel behind PR-A. PR-B waits until PR-A finishes. No parallel optimization is possible past an ALL-impacting PR.
ALL impact is a serialization point: every PR behind it depends on it regardless of target overlap, so parallel queues collapse to a single lane until the ALL-impacting PR clears.
Transitive dependents are usually captured by impacted targets
A common worry: “PR-1 changes target X, PR-2 introduces a feature that depends on X’s old behavior. Will they merge in parallel and break main?” If the two PRs share zero impacted targets, the queue treats them as parallel-safe. In practice this is rare: most impact-detection tools (Bazel, Nx, and similar) include transitive dependents when computing impacted targets. PR-2 would typically list target X as well, putting them in the same lane. If your impact-detection setup misses transitive dependents, you’ll see false-parallel merges. That’s a signal to widen your impact graph, not to disable parallel mode.Bisection splits in half, not one-by-one
When a batch fails and the queue needs to find the culprit, it splits the batch in half rather than peeling off individual PRs. A batch of 5 doesn’t bisect into 5 isolated tests. It bisects into 2 sub-batches, retests, and recurses on whichever sub-batch failed. Your Bisection Concurrency is the concurrency limit for those sub-batch test runs — not the number of individual PRs being retested at once. This matters when you size bisection concurrency: setting it equal to your batch size is more than you need. The bisection process is logarithmic in batch size.PFD’s downstream-PR delay
Pending failure depth waits for predecessor groups to finish (always) and successor groups to finish (up to the configured depth). One consequence customers hit:Example. PFD is set to 1. PR-A fails. PR-B is testing behind it. A new PR-C arrives that shares impacted targets with PR-A. The queue will wait for PR-C to finish testing before it transitions PR-A out of Pending Failure — even though PR-C wasn’t in the queue when PR-A failed.This is by design. PFD’s correctness signal depends on observing how successor PRs that include the failed group’s changes behave. A newly-arrived overlapping PR is a valid successor test — its result is informative about whether PR-A’s failure was a flake or a real failure. Waiting for it produces a stronger signal. The trade-off: high PFD values combined with frequent new submissions can extend the time before a known-failed PR is kicked from the queue.
If you need an upper bound on how many successor conclusions (not just queue positions) PFD waits on, that’s an active area of design — share your use case with support.
Disabling optimistic merge causes batch-removal restarts
Optimistic merging lets downstream batches keep testing against a projected future state of main while an upstream batch is still being resolved. If you disable optimistic merging, the queue can no longer reuse those downstream test results when the upstream batch changes shape. So if a batch ahead of yours is removed (for example, because it failed bisection and one PR was kicked), your batch may need to re-test from scratch — even if it had already passed. Customers who disabled optimistic merging have reported PRs going from “ready to merge” back to “testing” specifically because of this restart behavior.If you’re seeing unexplained re-tests of already-passing batches, check whether optimistic merging is disabled.
MQ-only failures aren’t yet flakiness signals
A test that fails in the merge queue but passes on main isn’t currently fed into Trunk Flaky Tests as a flakiness signal. Similarly, bisection test runs aren’t surfaced as flakiness data today. In practice, the queue’s anti-flake protection (optimistic merging + PFD) catches a lot of these transient failures without needing a separate flakiness signal. But if you’re trying to understand why an MQ-only flake doesn’t show up in your flaky-test dashboard, that’s why.Event-side view
For the webhook lifecycle of these batch events — including thepending_failure event that fires when a batch enters the hold state — see the webhooks reference.
Batching + Optimistic Merging and Pending Failure Depth
Enabling batching along with Pending Failure Depth and Optimistic Merging can help you realize the major cost savings of batching while still reaping the anti-flake protection of optimistic merging and pending failure depth.example of testing pull requests in batches of 3
| event | queue |
|---|---|
| Enqueue A, B, C, D, E, F, G | main <- ABC <- DEF +abc |
| Batch ABC fails | main <- ABC |
| pending failure depth keeps ABC from being evicted while DEF | main <- ABC (hold) <- DEF+abc |
| DEF passes | main <- ABC <- DEF+abc |
| optimistic merging allows ABC and DEF to merge | merge ABC, DEF |
Next steps
Start with batching:- Enable batching with conservative settings (batch size: 3-5)
- Monitor for a few days and observe behavior
- Gradually increase batch size as you gain confidence
- Check Metrics and monitoring to measure impact
- Optimistic merging - Combine with batching for maximum throughput
- Anti-flake protection - Reduce false batch failures
- Pending failure depth - Tune behavior during batch failures
- Metrics and monitoring - Track throughput improvements and CI cost savings
- Watch batch failure rate (should be <10%)
- Measure time-to-merge improvements
- If batches fail frequently → Lower batch size or enable Anti-flake protection
- If not seeing improvements → Check PR volume and test stability
- For detailed help → Troubleshooting