fix(try): defer top-level <esi:try> dispatch to drain_queue for parallel select()#55
Merged
Conversation
process_queue ran each top-level Try synchronously via process_try_block, serializing sibling tries (Σ-latency instead of max). Push it back on the queue and break, matching the PendingRequest path; drain_queue then fans all attempts' includes through one select() pool.
kailan
approved these changes
Jun 17, 2026
kailan
left a comment
Member
There was a problem hiding this comment.
This looks good and, based on my understanding, shouldn't have any adverse side effects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Processor::process_queuewas popping eachQueuedElement::Tryat the head of the queue and callingprocess_try_blocksynchronously.process_try_blockwalks the attempt's includes throughexecute_isolatedand then blocks on its own innerdrain_queue, so every top-level<esi:try>ran to completion before the next sibling could start — turning the expectedmax(latency)across sibling tries intoΣ(latency).This change leaves
Tryelements on the queue (push_front+break, mirroring the existingPendingRequestinclude path) so the end-of-streamdrain_queuecan fan all attempts' includes into a singleselect()pool and await them in parallel — which is precisely what itsTryBlockTracker/AttemptTracker/SlotEntrymachinery was built for.Impact
Real-world production trace: an aggregate template with 28 sequential
<esi:try>blocks went from ~4s average latency down to ~600ms after the fix — close to the slowest single<esi:include>in the page, which is the expected lower bound formax(latency)parallel dispatch.Likely origin
a13aef3("fix(try): includes inside attempt blocks now see correct variable state") added the inlineprocess_try_blockcall and strippedselect()out ofdrain_queue. In that world the inline handling was internally consistent.6747f57("refactor(try): parallel try-block dispatch with flat-buf slot tracking") restored a parallelselect()-baseddrain_queueand added aQueuedElement::Tryarm to its Step 1 walk — but did not remove the inline call upstream inprocess_queue. The parallel arm was therefore unreachable: every Try was consumed before it could be queued fordrain_queue.The fix is three lines removed and the rest replaced with a deferral that mirrors the
PendingRequestinclude arm directly above it.Trade-off
Static content emitted by the parser after a Try waits for end-of-input before
drain_queueflushes it, instead of streaming out during parsing. Oncedrain_queuestarts, content flushes incrementally in document order as each slot resolves, so the practical TTFB cost is bounded by how long after the first Try the input stream stays open — typically tens of milliseconds for a template body.The previous inline path flushed that content earlier, but at the cost of
Σ-latency dispatch across sibling tries, which dominates in real templates.