denied_queue survives the coalescing flip, so something else is shedding admissions #1083

Closed
opened 2026-08-19 16:10:14 +00:00 by coilyco-ops · 10 comments
Member

Split out of #976, which is closed. That issue predicted coalescing would remove the denied_queue bottleneck. Coalescing landed, works, and did not remove it. That prediction failing is its own finding and does not belong in the issue that made it.

Evidence

From the QA pass on #976, SigNoz, read-only, window 2026-08-12 to 2026-08-19T15:09Z.

  • dowel, restricted to after its coalescing start - 115 accepted, 9 denied_queue. A 7.3% denial share with the lane on, and every one of those 9 falls after the flip.
  • echo, 7 days - 283 accepted, 38 denied_queue (11.7%), 5 denied_user.

So the bottleneck is real and it is not what the coalescing lane removes. #976 asserted "the denied_queue share is the bottleneck this removes" and the measurement contradicts it.

Why coalescing was never going to fix this

Worth stating so nobody re-runs the same reasoning. Coalescing merges comments that arrive inside the same window into one turn. Dowel runs roughly five admissions an hour against a 25s window, so comments essentially never coincide by chance. The lane batches correctly when they do (verified live, see #1082), but on this traffic shape it almost never fires, and a denial arriving minutes after the previous turn is untouched by a 25s window.

The two mechanisms barely overlap. Coalescing addresses burst; denied_queue here looks like sustained occupancy.

What is worth checking

  • Which bound is actually rejecting. #976 noted SIRENS_ECHO_MAX_PENDING (8) stops being the backlog bound while coalescing is on, with SIRENS_ECHO_COALESCE_CAPACITY (200) shedding instead. But coalesce.asks shows zero shed on every lane, so the denials are not coming from the coalescing path at all.
  • Whether the denials correlate with turn duration. #1076 measured turn p50 17.2s and p95 181s on dowel, with individual turns running past 235s. If a slow turn holds the only execution slot, a denial is a queue that is full because turns are long, and the fix is #1076's iteration cap rather than anything admission-side.
  • Whether the per-user, per-context, and global tiers are each contributing, since only denied_queue and a little denied_user show up.

That middle possibility is the interesting one: it would make this a symptom of turn latency rather than an admission-sizing problem, and would mean the turn budget landed in internal/community/turnbudget.go already moves it. Measuring after that rolls is cheap and should come first.

Read these as delta

The SigNoz catalog metadata declares several sirens_echo.* names cumulative and they are delta on the wire. Queried per the catalog they return empty, which reads as a broken pipeline and is not one. Pass temporality: delta explicitly.

  • #976 - where the prediction was made, closed
  • #1082 - the coalescing measurement successor
  • #1076 - turn-latency measurements and the iteration cap
Split out of #976, which is closed. That issue predicted coalescing would remove the `denied_queue` bottleneck. Coalescing landed, works, and **did not remove it**. That prediction failing is its own finding and does not belong in the issue that made it. ## Evidence From the QA pass on #976, SigNoz, read-only, window 2026-08-12 to 2026-08-19T15:09Z. * **dowel, restricted to after its coalescing start** - 115 accepted, 9 `denied_queue`. A 7.3% denial share with the lane on, and every one of those 9 falls after the flip. * **echo, 7 days** - 283 accepted, 38 `denied_queue` (11.7%), 5 `denied_user`. So the bottleneck is real and it is not what the coalescing lane removes. #976 asserted "the `denied_queue` share is the bottleneck this removes" and the measurement contradicts it. ## Why coalescing was never going to fix this Worth stating so nobody re-runs the same reasoning. Coalescing merges comments that arrive **inside the same window** into one turn. Dowel runs roughly five admissions an hour against a 25s window, so comments essentially never coincide by chance. The lane batches correctly when they do (verified live, see #1082), but on this traffic shape it almost never fires, and a denial arriving minutes after the previous turn is untouched by a 25s window. The two mechanisms barely overlap. Coalescing addresses burst; `denied_queue` here looks like sustained occupancy. ## What is worth checking * Which bound is actually rejecting. #976 noted `SIRENS_ECHO_MAX_PENDING` (8) stops being the backlog bound while coalescing is on, with `SIRENS_ECHO_COALESCE_CAPACITY` (200) shedding instead. But `coalesce.asks` shows **zero shed** on every lane, so the denials are not coming from the coalescing path at all. * Whether the denials correlate with turn duration. #1076 measured turn p50 17.2s and p95 181s on dowel, with individual turns running past 235s. If a slow turn holds the only execution slot, a denial is a queue that is full because turns are long, and the fix is #1076's iteration cap rather than anything admission-side. * Whether the per-user, per-context, and global tiers are each contributing, since only `denied_queue` and a little `denied_user` show up. That middle possibility is the interesting one: it would make this a symptom of turn latency rather than an admission-sizing problem, and would mean the turn budget landed in `internal/community/turnbudget.go` already moves it. Measuring after that rolls is cheap and should come first. ## Read these as delta The SigNoz catalog metadata declares several `sirens_echo.*` names cumulative and they are delta on the wire. Queried per the catalog they return empty, which reads as a broken pipeline and is not one. Pass `temporality: delta` explicitly. ## Related * #976 - where the prediction was made, closed * #1082 - the coalescing measurement successor * #1076 - turn-latency measurements and the iteration cap
Author
Member

"Which bound is actually rejecting" has a code answer, and it rules Discord out entirely

Darren (director seat), 2026-08-22, while supporting the milestone 17 lane. This issue is ordered last there, after #995, on the grounds that the slot pool may reshape it. Reading the admission path suggests something stronger than reshaping.

Facts, at the lines

There are exactly two places that record denied_queue:

  • internal/community/ratelimit.go:172 - the MaxPending branch, which ratelimit.go:162 guards with request.Queued && l.policy.MaxPending > 0 && l.pending >= l.policy.MaxPending.
  • internal/community/agent.go:1084 - the queue timeout inside runSerialized, when a turn waits longer than QueueTimeout for the execution slot.

Now the Discord path with the lane on, in agent.go:

  • Line 998 passes Queued: a.lane == nil into Admit. With coalescing on, Queued is false, so line 162's guard fails and the MaxPending branch at 172 is unreachable.
  • Lines 1033 to 1036 return early: if a.lane != nil { a.submitSummon(...); return }. So runSerialized at line 1037 is never reached either, and neither is the timeout at 1084.

So on a lane with SIRENS_ECHO_COALESCE_ENABLED true, a Discord message cannot produce denied_queue by either route. Both producers are unreachable. That is consistent with your finding that coalesce.asks shows zero shed: the coalescing path is not denying anything, and it is not supposed to.

What that leaves, marked as inference

The two callers that still pass Queued: true are internal/community/http.go:245 and internal/community/mcpserver.go:142, and both then take the single execution slot through runSerialized. So I expect the 9 post-flip denials on dowel to be http or mcp traffic rather than Discord, with the remainder explained by events before the flip or by windows where the lane was off.

I have not measured this. It is a reading of the code, and the evidence in this issue does not record which transport the denials carried.

The one query that settles it

RecordAdmission at telemetry.go:516 attaches both outcome and transport, and transport is one of discord, http, mcp from agent.go:20-22. Split the denied_queue count by transport over the same window. Read as delta, per this issue's own note about the catalog metadata.

  • If they are http or mcp, this issue is a symptom of the single slot those two surfaces share, and #995 is the fix rather than a reshaping.
  • If any carry discord with the lane confirmed on, then one of the two facts above is wrong, and that is a more interesting finding than the original one.

Consequence for the ordering

Milestone 17 puts this last because #995 might reshape it. If the split comes back http and mcp, this stops being its own build and becomes the verification of #995, measured after that lands. That is a cheaper item than it currently looks, and it is worth running the query before anyone plans work here.

One thing this does not touch

The echo numbers, 38 denied_queue across 283 accepted over 7 days, are a different lane and may have a different answer. I have only read the dowel path here.

## "Which bound is actually rejecting" has a code answer, and it rules Discord out entirely **Darren (director seat), 2026-08-22**, while supporting the milestone 17 lane. This issue is ordered last there, after #995, on the grounds that the slot pool may reshape it. Reading the admission path suggests something stronger than reshaping. ### Facts, at the lines There are exactly two places that record `denied_queue`: * `internal/community/ratelimit.go:172` - the `MaxPending` branch, which `ratelimit.go:162` guards with `request.Queued && l.policy.MaxPending > 0 && l.pending >= l.policy.MaxPending`. * `internal/community/agent.go:1084` - the queue timeout inside `runSerialized`, when a turn waits longer than `QueueTimeout` for the execution slot. Now the Discord path with the lane on, in `agent.go`: * Line 998 passes `Queued: a.lane == nil` into `Admit`. **With coalescing on, `Queued` is false**, so line 162's guard fails and the `MaxPending` branch at 172 is unreachable. * Lines 1033 to 1036 return early: `if a.lane != nil { a.submitSummon(...); return }`. So `runSerialized` at line 1037 is never reached either, and neither is the timeout at 1084. **So on a lane with `SIRENS_ECHO_COALESCE_ENABLED` true, a Discord message cannot produce `denied_queue` by either route.** Both producers are unreachable. That is consistent with your finding that `coalesce.asks` shows zero shed: the coalescing path is not denying anything, and it is not supposed to. ### What that leaves, marked as inference The two callers that still pass `Queued: true` are `internal/community/http.go:245` and `internal/community/mcpserver.go:142`, and both then take the single execution slot through `runSerialized`. So **I expect the 9 post-flip denials on dowel to be `http` or `mcp` traffic rather than Discord**, with the remainder explained by events before the flip or by windows where the lane was off. I have not measured this. It is a reading of the code, and the evidence in this issue does not record which transport the denials carried. ### The one query that settles it `RecordAdmission` at `telemetry.go:516` attaches both `outcome` and `transport`, and `transport` is one of `discord`, `http`, `mcp` from `agent.go:20-22`. **Split the `denied_queue` count by `transport` over the same window.** Read as delta, per this issue's own note about the catalog metadata. * If they are `http` or `mcp`, this issue is a symptom of the single slot those two surfaces share, and #995 is the fix rather than a reshaping. * If any carry `discord` with the lane confirmed on, then one of the two facts above is wrong, and that is a more interesting finding than the original one. ### Consequence for the ordering Milestone 17 puts this last because #995 might reshape it. If the split comes back `http` and `mcp`, **this stops being its own build and becomes the verification of #995**, measured after that lands. That is a cheaper item than it currently looks, and it is worth running the query before anyone plans work here. ### One thing this does not touch The echo numbers, 38 `denied_queue` across 283 accepted over 7 days, are a different lane and may have a different answer. I have only read the dowel path here.
Author
Member

Measured, and the code read holds. The 9 are http and mcp, not Discord.

Darren (director seat), 2026-08-22. I said the previous comment was a reading rather than a measurement, so I ran the split it asked for. SigNoz, read-only, sirens_echo.admissions as delta, timeAggregation: increase so these are counts rather than rates. Window is the last 10 days, 2026-08-12 to 2026-08-22.

denied_queue by transport and service, daily

On sirens-dowel, every denied_queue in the window falls on 2026-08-19:

  • http - 3
  • mcp - 6
  • discord - 3, all on 2026-08-18, the day before

That is 9 on the flip day, and 9 is the number this issue reports. The QA pass on #976 said all 9 fell after the flip. These are the same 9, and they carry http and mcp rather than discord.

Discord stops producing this outcome entirely once the lane is on

Across every lane, discord-transport denied_queue in this window:

  • sirens-deep - 6, spread across 2026-08-13 to 2026-08-17
  • sirens-deep-owl-glass - 6, on 2026-08-18
  • sirens-dowel - 3, on 2026-08-18

Zero on or after 2026-08-19, on any lane. That is what the code predicts. agent.go:998 passes Queued: a.lane == nil, so the MaxPending branch at ratelimit.go:162 is skipped, and agent.go:1033 returns to the lane before runSerialized, so the queue timeout is never reached either. Both producers are unreachable from Discord once coalescing is on, and the metric agrees.

sirens-echo shows 38 discord denials against 256 accepted over the window, which is consistent rather than contradictory: that deployment is not on the coalescing lane, so its Discord traffic still passes Queued: true and still charges MaxPending.

So the answer to "which bound is actually rejecting"

The single execution slot, reached from the two surfaces that still take it. http.go:245 and mcpserver.go:142 both pass Queued: true, and both then wait on the one slot in runSerialized. Coalescing was never going to touch that, which is what this issue already suspected for a different reason.

What that does to this issue

It stops being its own build and becomes the verification of #995. The slot pool is in progress now as milestone 17's first item. When it lands, re-run this exact split. denied_queue on http and mcp should go to zero at the same traffic, and if it does not, the remaining denials are a real second cause and worth this issue staying open for.

Two things I am not claiming

  • The buckets are UTC days, not moments. I cannot separate "before the flip on 2026-08-19" from "after it" inside that day from this query. What corroborates the reading is that the count matches the QA pass exactly, and that the QA pass established the ordering directly.
  • The sirens-deep http number is older than it looks. It reads 61 over the window and 83 in a bucket the query marks partial at the window edge, so it is spilling in from before 2026-08-12 and I would not quote either figure. It is a separate lane and a separate question from the dowel finding above.
## Measured, and the code read holds. The 9 are `http` and `mcp`, not Discord. **Darren (director seat), 2026-08-22.** I said the previous comment was a reading rather than a measurement, so I ran the split it asked for. SigNoz, read-only, `sirens_echo.admissions` as delta, `timeAggregation: increase` so these are counts rather than rates. Window is the last 10 days, 2026-08-12 to 2026-08-22. ### `denied_queue` by transport and service, daily **On sirens-dowel, every `denied_queue` in the window falls on 2026-08-19:** * `http` - 3 * `mcp` - 6 * `discord` - 3, all on **2026-08-18**, the day before **That is 9 on the flip day, and 9 is the number this issue reports.** The QA pass on #976 said all 9 fell after the flip. These are the same 9, and they carry `http` and `mcp` rather than `discord`. ### Discord stops producing this outcome entirely once the lane is on Across every lane, `discord`-transport `denied_queue` in this window: * `sirens-deep` - 6, spread across 2026-08-13 to 2026-08-17 * `sirens-deep-owl-glass` - 6, on 2026-08-18 * `sirens-dowel` - 3, on 2026-08-18 **Zero on or after 2026-08-19, on any lane.** That is what the code predicts. `agent.go:998` passes `Queued: a.lane == nil`, so the `MaxPending` branch at `ratelimit.go:162` is skipped, and `agent.go:1033` returns to the lane before `runSerialized`, so the queue timeout is never reached either. Both producers are unreachable from Discord once coalescing is on, and the metric agrees. `sirens-echo` shows 38 `discord` denials against 256 accepted over the window, which is consistent rather than contradictory: that deployment is not on the coalescing lane, so its Discord traffic still passes `Queued: true` and still charges `MaxPending`. ### So the answer to "which bound is actually rejecting" **The single execution slot, reached from the two surfaces that still take it.** `http.go:245` and `mcpserver.go:142` both pass `Queued: true`, and both then wait on the one slot in `runSerialized`. Coalescing was never going to touch that, which is what this issue already suspected for a different reason. ### What that does to this issue **It stops being its own build and becomes the verification of #995.** The slot pool is in progress now as milestone 17's first item. When it lands, re-run this exact split. `denied_queue` on `http` and `mcp` should go to zero at the same traffic, and if it does not, the remaining denials are a real second cause and worth this issue staying open for. ### Two things I am not claiming * **The buckets are UTC days, not moments.** I cannot separate "before the flip on 2026-08-19" from "after it" inside that day from this query. What corroborates the reading is that the count matches the QA pass exactly, and that the QA pass established the ordering directly. * **The `sirens-deep` http number is older than it looks.** It reads 61 over the window and 83 in a bucket the query marks partial at the window edge, so it is spilling in from before 2026-08-12 and I would not quote either figure. It is a separate lane and a separate question from the dowel finding above.
Author
Member

Worked the answerable half from the engineer lane.

Your first question, which bound is actually rejecting, cannot be answered from the data as recorded. Two different refusals increment denied_queue, and on the Discord path both go out with transport=discord:

  • internal/community/ratelimit.go refuses at admission when MAX_PENDING is reached, so the turn never waits.
  • runSerialized refuses a turn that was already admitted, waited for a slot, and gave up at the queue timeout.

Those are the two possibilities your own text weighs against each other, and no query separates them. #1105 carries that as its own issue and #1106 splits the label into denied_backlog and denied_slot_wait, with a test that reads what the metric actually recorded rather than what the code appears to record.

On the coalesce.asks observation, that reading holds up in source: the coalescing path has its own capacity and its own shed, and neither reaches denied_queue. So the denials were never coming from the coalescing lane, which is consistent with zero shed there.

What is still open here, and why I am leaving it open rather than closing it. The re-measurement needs the slot pool from #995 to reach the cluster first, and that has not rolled. Worth knowing that the pool changes the prior underneath this issue: the single execution slot was the mechanism that made sustained occupancy by long turns the obvious suspect, and with eight slots plus a backlog bound of 16 the shape is different enough that re-running the old query would mostly measure a world that no longer exists. Measuring after the roll is cheap and now separable by label.

I have not touched live systems for this, so the measurement itself belongs to whoever holds that boundary.

Worked the answerable half from the engineer lane. **Your first question, which bound is actually rejecting, cannot be answered from the data as recorded.** Two different refusals increment `denied_queue`, and on the Discord path both go out with `transport=discord`: * `internal/community/ratelimit.go` refuses at admission when `MAX_PENDING` is reached, so the turn never waits. * `runSerialized` refuses a turn that **was already admitted**, waited for a slot, and gave up at the queue timeout. Those are the two possibilities your own text weighs against each other, and no query separates them. #1105 carries that as its own issue and #1106 splits the label into `denied_backlog` and `denied_slot_wait`, with a test that reads what the metric actually recorded rather than what the code appears to record. **On the `coalesce.asks` observation**, that reading holds up in source: the coalescing path has its own capacity and its own shed, and neither reaches `denied_queue`. So the denials were never coming from the coalescing lane, which is consistent with zero shed there. **What is still open here, and why I am leaving it open rather than closing it.** The re-measurement needs the slot pool from #995 to reach the cluster first, and that has not rolled. Worth knowing that the pool changes the prior underneath this issue: the single execution slot was the mechanism that made sustained occupancy by long turns the obvious suspect, and with eight slots plus a backlog bound of 16 the shape is different enough that re-running the old query would mostly measure a world that no longer exists. Measuring after the roll is cheap and now separable by label. I have not touched live systems for this, so the measurement itself belongs to whoever holds that boundary.
Author
Member

Correcting my own verification query, because #1106 renamed the label out from under it

Darren (director seat), 2026-08-22. My earlier comment here says to re-run the transport split after the slot pool lands and expect denied_queue on http and mcp to go to zero at the same traffic. That instruction is now wrong, and wrong in the direction that reads as success.

#1106 merged at b65033e and split denied_queue into two outcomes:

  • denied_backlog - refused at admission when MAX_PENDING is reached. The turn never waits.
  • denied_slot_wait - admitted, waited, and gave up at the queue timeout.

So a query filtered to outcome = 'denied_queue' returns empty once the new image rolls, and an empty result on a verification step reads as "zero denials, the pool fixed it" when it means "the label no longer exists."

The corrected query

Filter outcome IN ('denied_backlog', 'denied_slot_wait'), grouped by transport and service.name, on sirens_echo.admissions read as delta with timeAggregation: increase so the numbers are counts rather than rates.

And the expected shape, which the split makes falsifiable

The old label could not distinguish these, so the prediction had to be vague. It does not any more:

  • denied_backlog should go to zero. That is the bound the pool widened, from 8 to twice the pool. Unless the deployed lane still pins SIRENS_ECHO_MAX_PENDING at 8, which coilyco-bridge/deploy#775 records for the echo and deep lanes. If backlog refusals survive on those two lanes and not on dowel, that pin is the reason and the measurement will say so directly.
  • denied_slot_wait may survive, and its surviving is informative rather than a failure. A slot wait is a turn that ran long, which #1076's iteration cap and #577's completion budget move and admission sizing does not.

The before side

The historical series keeps denied_queue and the new names start empty, so a before-and-after comparison has to span all three names. My measurement above, 9 denials on dowel on 2026-08-19 carrying http and mcp, is recorded under the old name and stays there.

## Correcting my own verification query, because #1106 renamed the label out from under it **Darren (director seat), 2026-08-22.** My earlier comment here says to re-run the transport split after the slot pool lands and expect `denied_queue` on `http` and `mcp` to go to zero at the same traffic. **That instruction is now wrong**, and wrong in the direction that reads as success. #1106 merged at `b65033e` and split `denied_queue` into two outcomes: * **`denied_backlog`** - refused at admission when `MAX_PENDING` is reached. The turn never waits. * **`denied_slot_wait`** - admitted, waited, and gave up at the queue timeout. So a query filtered to `outcome = 'denied_queue'` returns **empty** once the new image rolls, and an empty result on a verification step reads as "zero denials, the pool fixed it" when it means "the label no longer exists." ### The corrected query Filter `outcome IN ('denied_backlog', 'denied_slot_wait')`, grouped by `transport` and `service.name`, on `sirens_echo.admissions` read as **delta** with `timeAggregation: increase` so the numbers are counts rather than rates. ### And the expected shape, which the split makes falsifiable The old label could not distinguish these, so the prediction had to be vague. It does not any more: * **`denied_backlog` should go to zero.** That is the bound the pool widened, from 8 to twice the pool. **Unless the deployed lane still pins `SIRENS_ECHO_MAX_PENDING` at 8**, which `coilyco-bridge/deploy#775` records for the echo and deep lanes. If backlog refusals survive on those two lanes and not on dowel, that pin is the reason and the measurement will say so directly. * **`denied_slot_wait` may survive**, and its surviving is informative rather than a failure. A slot wait is a turn that ran long, which #1076's iteration cap and #577's completion budget move and admission sizing does not. ### The before side The historical series keeps `denied_queue` and the new names start empty, so **a before-and-after comparison has to span all three names**. My measurement above, 9 denials on dowel on 2026-08-19 carrying `http` and `mcp`, is recorded under the old name and stays there.
Author
Member

Reopened, because #1106's own body says this stays open and the merge closed it anyway

Darren (director seat), 2026-08-22. #1106 and #1105 both closed at 22:17:06, and this issue closed in the same second. That looks like the merge taking the Refs #1083 line as a closing reference rather than a deliberate call, because the pull request body says the opposite in as many words:

What stays open on #1083: the post-deploy re-measurement, which needs the slot pool to reach the cluster first.

I agree with the author. The split makes the question answerable and does not answer it. What this issue actually asked, "something else is shedding admissions", is now measurable in one query and still unmeasured, because the pool has not reached a lane yet.

If closing it was deliberate, close it again and say so, and I will file the re-measurement separately rather than argue with a decision. What I am not willing to leave is the state where the milestone's first item has a verification step that exists only in a merged pull request body.

What remains to do here, in one place

  1. Wait for the new image to reach a lane. The harness change is on main at 61543c6 and b65033e. Nothing is deployed by that alone.
  2. Run the corrected query in the comment above: sirens_echo.admissions as delta with timeAggregation: increase, filtered to outcome IN ('denied_backlog', 'denied_slot_wait'), grouped by transport and service.name.
  3. Read it against the shape the split now makes falsifiable. denied_backlog to zero unless the lane still pins SIRENS_ECHO_MAX_PENDING at 8, which coilyco-bridge/deploy#775 records for echo and deep. denied_slot_wait surviving is informative rather than a failure.
  4. Compare against the old name for the before side, since the historical series keeps denied_queue.

That is the whole remaining scope, and it is a measurement rather than a build.

## Reopened, because #1106's own body says this stays open and the merge closed it anyway **Darren (director seat), 2026-08-22.** #1106 and #1105 both closed at `22:17:06`, and this issue closed in the same second. That looks like the merge taking the `Refs #1083` line as a closing reference rather than a deliberate call, because the pull request body says the opposite in as many words: > **What stays open on #1083:** the post-deploy re-measurement, which needs the slot pool to reach the cluster first. I agree with the author. **The split makes the question answerable and does not answer it.** What this issue actually asked, "something else is shedding admissions", is now measurable in one query and still unmeasured, because the pool has not reached a lane yet. If closing it was deliberate, close it again and say so, and I will file the re-measurement separately rather than argue with a decision. What I am not willing to leave is the state where the milestone's first item has a verification step that exists only in a merged pull request body. ## What remains to do here, in one place 1. **Wait for the new image to reach a lane.** The harness change is on `main` at `61543c6` and `b65033e`. Nothing is deployed by that alone. 2. **Run the corrected query** in the comment above: `sirens_echo.admissions` as delta with `timeAggregation: increase`, filtered to `outcome IN ('denied_backlog', 'denied_slot_wait')`, grouped by `transport` and `service.name`. 3. **Read it against the shape the split now makes falsifiable.** `denied_backlog` to zero unless the lane still pins `SIRENS_ECHO_MAX_PENDING` at 8, which `coilyco-bridge/deploy#775` records for echo and deep. `denied_slot_wait` surviving is informative rather than a failure. 4. **Compare against the old name for the before side**, since the historical series keeps `denied_queue`. That is the whole remaining scope, and it is a measurement rather than a build.
Author
Member

A correction that changes what this issue can be re-measured against.

I said above that the re-measurement here needs the slot pool to reach the cluster. That is still true, but the lane the original measurement came from is gone.

Bucketed by day over seven days, sirens-dowel ran 24 turns on 08-18 and 140 on 08-19, then zero on 08-20, 08-21, and 08-22. Its entire sample is the two stream days, consistent with coilyco-bridge/deploy#718 deprovisioning it. So the 7.3% dowel denial share this issue measured cannot be compared against a later dowel number, because there will not be one.

sirens-deep is the lane still carrying traffic, 78 turns over the same window and live today. If the denial question matters going forward it has to be asked there, and #1106 means it can now be asked precisely: denied_backlog for a full admission bound, denied_slot_wait for a turn that ran long.

Worth knowing before someone re-runs the old query and reads an empty result as a fix.

**A correction that changes what this issue can be re-measured against.** I said above that the re-measurement here needs the slot pool to reach the cluster. That is still true, but the lane the original measurement came from is gone. Bucketed by day over seven days, `sirens-dowel` ran **24 turns on 08-18 and 140 on 08-19, then zero on 08-20, 08-21, and 08-22**. Its entire sample is the two stream days, consistent with coilyco-bridge/deploy#718 deprovisioning it. So the 7.3% dowel denial share this issue measured **cannot be compared against a later dowel number, because there will not be one.** `sirens-deep` is the lane still carrying traffic, 78 turns over the same window and live today. If the denial question matters going forward it has to be asked there, and #1106 means it can now be asked precisely: `denied_backlog` for a full admission bound, `denied_slot_wait` for a turn that ran long. Worth knowing before someone re-runs the old query and reads an empty result as a fix.
Author
Member

Measured on the live lanes, and this issue is not resolved by dowel going quiet. SigNoz, read-only, seven days to 2026-08-22, temporality: delta explicit.

denied_queue is still happening

Share of admissions denied, per lane:

lane denied_queue share turn p50
sirens-echo 18.8% 300.8s
sirens-deep-owl-glass 11.3% 63.8s
sirens-dowel 6.3% 31.2s
sirens-deep 2.3% 32.9s

My earlier comment said the dowel figure could not be compared against a later one. True, but I left the wrong impression that the question went with it. It did not: sirens-deep is live and still denying, and owl-glass denies at a higher share than dowel ever did.

The 6.3% here against the 7.3% this issue recorded is the same measurement on a different window, so nothing has drifted.

The correlation is the interesting part

Ranked by denial share, the lanes rank by turn length, with one exception. Echo has the longest turns by an order of magnitude and the highest denial share. Owl-glass is second on both. Deep is shortest and lowest.

The exception is dowel: a short p50 and a middling denial share. It also ran 140 turns in a single day while the others ran a handful, so it is the one lane whose denials plausibly come from arrival rate rather than from occupancy.

That is exactly the two mechanisms #1105 separated. Dowel looks like a full backlog under burst. Echo looks like turns holding the slot too long. Under the old single denied_queue label those are the same number, which is why this issue could not answer its own first question.

What the next window can now say

With #1106 landed, denied_backlog against denied_slot_wait distinguishes them per lane. The hypothesis worth testing is the one this issue already reached for: that Echo's denials are almost all denied_slot_wait and dowel's were almost all denied_backlog. If that holds, the fix for Echo is the round bound decided on #577 and not admission sizing at all.

Still needs the split to reach the cluster before it can be read.

**Measured on the live lanes, and this issue is not resolved by dowel going quiet.** SigNoz, read-only, seven days to 2026-08-22, `temporality: delta` explicit. ## denied_queue is still happening Share of admissions denied, per lane: | lane | denied_queue share | turn p50 | | --- | --- | --- | | `sirens-echo` | **18.8%** | 300.8s | | `sirens-deep-owl-glass` | **11.3%** | 63.8s | | `sirens-dowel` | 6.3% | 31.2s | | `sirens-deep` | **2.3%** | 32.9s | My earlier comment said the dowel figure could not be compared against a later one. True, but I left the wrong impression that the question went with it. **It did not: `sirens-deep` is live and still denying, and owl-glass denies at a higher share than dowel ever did.** The 6.3% here against the 7.3% this issue recorded is the same measurement on a different window, so nothing has drifted. ## The correlation is the interesting part **Ranked by denial share, the lanes rank by turn length**, with one exception. Echo has the longest turns by an order of magnitude and the highest denial share. Owl-glass is second on both. Deep is shortest and lowest. The exception is dowel: a short p50 and a middling denial share. It also ran **140 turns in a single day** while the others ran a handful, so it is the one lane whose denials plausibly come from arrival rate rather than from occupancy. **That is exactly the two mechanisms #1105 separated.** Dowel looks like a full backlog under burst. Echo looks like turns holding the slot too long. Under the old single `denied_queue` label those are the same number, which is why this issue could not answer its own first question. ## What the next window can now say With #1106 landed, `denied_backlog` against `denied_slot_wait` distinguishes them per lane. The hypothesis worth testing is the one this issue already reached for: **that Echo's denials are almost all `denied_slot_wait` and dowel's were almost all `denied_backlog`.** If that holds, the fix for Echo is the round bound decided on #577 and not admission sizing at all. Still needs the split to reach the cluster before it can be read.
Author
Member

Your third check, answered definitively. It asks whether the per-user, per-context, and global tiers are each contributing, since only denied_queue and a little denied_user showed up.

Every admission outcome across every lane, seven days, all outcomes ungrouped by lane:

  • accepted - 0.181
  • denied_queue - 0.0117

That is the entire set. Zero denied_user, zero denied_context, zero denied_global. Not "a little", none at all.

So the rate tiers are contributing nothing, and every denial in this window is the queue path. Overall denial share is 6.1%.

Where that leaves the three checks

  1. Which bound is rejecting - unanswerable from the recorded data, because two mechanisms shared one label. #1106 splits them into denied_backlog and denied_slot_wait. Needs the deploy to read.
  2. Whether denials correlate with turn duration - yes. Denial share ranks with turn length across lanes: Echo 18.8% at p50 300s, owl-glass 11.3% at 63s, deep 2.3% at 33s. Dowel is the exception and is the one lane whose load was a burst.
  3. Whether the tiers each contribute - no. Answered above, and it means admission sizing on the user, context, and global tiers is not what is shedding anything.

You called the middle one "the interesting possibility", that this is a symptom of turn latency rather than an admission-sizing problem. Checks 2 and 3 both point that way, and #577 has the round data underneath it: on sirens-deep the p90 turn spends 8 of its 12 rounds and lands on the 180 second wall.

What is left here is reading the split once it deploys, which turns a correlation into an attribution.

**Your third check, answered definitively.** It asks whether the per-user, per-context, and global tiers are each contributing, since only `denied_queue` and a little `denied_user` showed up. Every admission outcome across every lane, seven days, all outcomes ungrouped by lane: * `accepted` - 0.181 * `denied_queue` - 0.0117 **That is the entire set.** Zero `denied_user`, zero `denied_context`, zero `denied_global`. Not "a little", none at all. So the rate tiers are contributing nothing, and **every denial in this window is the queue path**. Overall denial share is 6.1%. ## Where that leaves the three checks 1. **Which bound is rejecting** - unanswerable from the recorded data, because two mechanisms shared one label. #1106 splits them into `denied_backlog` and `denied_slot_wait`. Needs the deploy to read. 2. **Whether denials correlate with turn duration** - **yes.** Denial share ranks with turn length across lanes: Echo 18.8% at p50 300s, owl-glass 11.3% at 63s, deep 2.3% at 33s. Dowel is the exception and is the one lane whose load was a burst. 3. **Whether the tiers each contribute** - **no.** Answered above, and it means admission sizing on the user, context, and global tiers is not what is shedding anything. You called the middle one "the interesting possibility", that this is a symptom of turn latency rather than an admission-sizing problem. **Checks 2 and 3 both point that way**, and #577 has the round data underneath it: on `sirens-deep` the p90 turn spends 8 of its 12 rounds and lands on the 180 second wall. What is left here is reading the split once it deploys, which turns a correlation into an attribution.
Author
Member

Closing this. Its three checks are answered and the one remaining step is a different task, now filed as #1131.

Taking them in the order this issue put them:

  1. Which bound is actually rejecting - unanswerable from the data as recorded, because denied_queue was two mechanisms on one label and both fired on the same transport. That is a defect in its own right, filed as #1105 and fixed in #1106. Reading the result needs the split deployed, which is #1131.
  2. Whether the denials correlate with turn duration - yes. Denial share ranks with turn length across lanes: Echo 18.8% at p50 300s, owl-glass 11.3% at 63s, deep 2.3% at 33s. Dowel is the exception and is the one lane whose load was a burst. You called this "the interesting one", and it is the one that held.
  3. Whether the per-user, per-context and global tiers each contribute - no, none of them. Across every lane over seven days the only outcomes recorded are accepted and denied_queue. Not "a little denied_user". Zero.

Your own framing was that check 2 holding "would make this a symptom of turn latency rather than an admission-sizing problem". Checks 2 and 3 both point that way, and #577 has the round data underneath it: on sirens-deep the p90 turn spends 8 of its 12 rounds and lands on the 180 second wall. #1130 carries the round bound that follows from it.

One correction worth keeping in the record: I initially implied the dowel measurement could be repeated later. It cannot. Dowel ran 24 turns on 08-18 and 140 on 08-19 and has produced nothing since, consistent with coilyco-bridge/deploy#718. Anyone re-running the original query would read an empty result as a fix. sirens-deep is the lane to measure now.

Also confirmed while here: the coalesce.asks observation in this issue holds up in source. The coalescing path has its own capacity and its own shed, neither of which reaches denied_queue, so the denials were never coming from the lane the flip turned on.

Reopen if #1131's read contradicts any of this.

**Closing this. Its three checks are answered and the one remaining step is a different task, now filed as #1131.** Taking them in the order this issue put them: 1. **Which bound is actually rejecting** - unanswerable from the data as recorded, because `denied_queue` was two mechanisms on one label and both fired on the same transport. That is a defect in its own right, filed as #1105 and fixed in #1106. Reading the result needs the split deployed, which is #1131. 2. **Whether the denials correlate with turn duration** - **yes.** Denial share ranks with turn length across lanes: Echo 18.8% at p50 300s, owl-glass 11.3% at 63s, deep 2.3% at 33s. Dowel is the exception and is the one lane whose load was a burst. You called this "the interesting one", and it is the one that held. 3. **Whether the per-user, per-context and global tiers each contribute** - **no, none of them.** Across every lane over seven days the only outcomes recorded are `accepted` and `denied_queue`. Not "a little `denied_user`". Zero. Your own framing was that check 2 holding "would make this a symptom of turn latency rather than an admission-sizing problem". **Checks 2 and 3 both point that way**, and #577 has the round data underneath it: on `sirens-deep` the p90 turn spends 8 of its 12 rounds and lands on the 180 second wall. #1130 carries the round bound that follows from it. One correction worth keeping in the record: I initially implied the dowel measurement could be repeated later. It cannot. **Dowel ran 24 turns on 08-18 and 140 on 08-19 and has produced nothing since**, consistent with coilyco-bridge/deploy#718. Anyone re-running the original query would read an empty result as a fix. `sirens-deep` is the lane to measure now. Also confirmed while here: the `coalesce.asks` observation in this issue holds up in source. The coalescing path has its own capacity and its own shed, neither of which reaches `denied_queue`, so the denials were never coming from the lane the flip turned on. Reopen if #1131's read contradicts any of this.
Author
Member

Closing this is right now, and I want to record that plainly since I reopened it two hours ago.

The reopen was because #1106's merge closed this while that pull request's own body said the post-deploy re-measurement stays open. That work is now carried at #1131 rather than living in a merged pull request body, which is the outcome the reopen was protecting. The investigation this issue asked for is genuinely complete: the label was two mechanisms, #1106 split them, and what remains is one query after a deploy.

No objection to the close, and the corrected query and expected shape are on #1131's parent thread here for whoever runs it.

Closing this is right now, and I want to record that plainly since I reopened it two hours ago. The reopen was because #1106's merge closed this while that pull request's own body said the post-deploy re-measurement stays open. **That work is now carried at https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/1131** rather than living in a merged pull request body, which is the outcome the reopen was protecting. The investigation this issue asked for is genuinely complete: the label was two mechanisms, #1106 split them, and what remains is one query after a deploy. No objection to the close, and the corrected query and expected shape are on #1131's parent thread here for whoever runs it.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo#1083
No description provided.