[{"data":1,"prerenderedAt":1153},["ShallowReactive",2],{"navigation":3,"\u002Foperations\u002Fflow-control":155,"\u002Foperations\u002Fflow-control-surround":1148},[4,36,57,113,144],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":35},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20,25,30],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Install","\u002Fgetting-started\u002Finstall","1.getting-started\u002F2.install","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F3.quickstart","i-lucide-zap",{"title":26,"path":27,"stem":28,"icon":29},"Testing","\u002Fgetting-started\u002Ftesting","1.getting-started\u002F4.testing","i-lucide-flask-conical",{"title":31,"path":32,"stem":33,"icon":34},"CLI","\u002Fgetting-started\u002Fcli","1.getting-started\u002F5.cli","i-lucide-terminal",false,{"title":37,"icon":38,"path":39,"stem":40,"children":41,"page":35},"Concepts","i-lucide-boxes","\u002Fconcepts","2.concepts",[42,47,52],{"title":43,"path":44,"stem":45,"icon":46},"SQL-first contract","\u002Fconcepts\u002Fsql-first-contract","2.concepts\u002F1.sql-first-contract","i-lucide-database",{"title":48,"path":49,"stem":50,"icon":51},"Jobs and status","\u002Fconcepts\u002Fjobs-and-status","2.concepts\u002F2.jobs-and-status","i-lucide-list-checks",{"title":53,"path":54,"stem":55,"icon":56},"Fencing and settles","\u002Fconcepts\u002Ffencing-and-settles","2.concepts\u002F3.fencing-and-settles","i-lucide-shield",{"title":58,"icon":59,"path":60,"stem":61,"children":62,"page":35},"API","i-lucide-code-2","\u002Fapi","3.api",[63,68,73,78,83,88,93,98,103,108],{"title":64,"path":65,"stem":66,"icon":67},"Enqueue","\u002Fapi\u002Fenqueue","3.api\u002F1.enqueue","i-lucide-plus-circle",{"title":69,"path":70,"stem":71,"icon":72},"Trusted Effects","\u002Fapi\u002Ftrusted-effects","3.api\u002F10.trusted-effects","i-lucide-lock-keyhole",{"title":74,"path":75,"stem":76,"icon":77},"Claim and heartbeat","\u002Fapi\u002Fclaim-and-heartbeat","3.api\u002F2.claim-and-heartbeat","i-lucide-hand",{"title":79,"path":80,"stem":81,"icon":82},"Worker and handlers","\u002Fapi\u002Fworker-and-handlers","3.api\u002F3.worker-and-handlers","i-lucide-cpu",{"title":84,"path":85,"stem":86,"icon":87},"HTTP facade","\u002Fapi\u002Fhttp-facade","3.api\u002F4.http-facade","i-lucide-globe",{"title":89,"path":90,"stem":91,"icon":92},"Authorization","\u002Fapi\u002Fauthorization","3.api\u002F5.authorization","i-lucide-key-round",{"title":94,"path":95,"stem":96,"icon":97},"Follow-ups","\u002Fapi\u002Ffollow-ups","3.api\u002F6.follow-ups","i-lucide-git-branch",{"title":99,"path":100,"stem":101,"icon":102},"Workflows and schedules","\u002Fapi\u002Fworkflows-and-schedules","3.api\u002F7.workflows-and-schedules","i-lucide-workflow",{"title":104,"path":105,"stem":106,"icon":107},"Durable admission","\u002Fapi\u002Fdurable-admission","3.api\u002F8.durable-admission","i-lucide-ticket",{"title":109,"path":110,"stem":111,"icon":112},"Workflow Continuations","\u002Fapi\u002Fworkflow-continuations","3.api\u002F9.workflow-continuations","i-lucide-git-fork",{"title":114,"icon":115,"path":116,"stem":117,"children":118,"page":35},"Operations","i-lucide-activity","\u002Foperations","4.operations",[119,124,129,134,139],{"title":120,"path":121,"stem":122,"icon":123},"Standalone Scheduler","\u002Foperations\u002Fstandalone-scheduler","4.operations\u002F0.standalone-scheduler","i-lucide-calendar-clock",{"title":125,"path":126,"stem":127,"icon":128},"Housekeeping","\u002Foperations\u002Fhousekeeping","4.operations\u002F1.housekeeping","i-lucide-timer",{"title":130,"path":131,"stem":132,"icon":133},"Redrive and dead letters","\u002Foperations\u002Fredrive-and-dlq","4.operations\u002F2.redrive-and-dlq","i-lucide-rotate-ccw",{"title":135,"path":136,"stem":137,"icon":138},"Flow control","\u002Foperations\u002Fflow-control","4.operations\u002F3.flow-control","i-lucide-gauge",{"title":140,"path":141,"stem":142,"icon":143},"Queue Boundaries","\u002Foperations\u002Fqueue-boundaries","4.operations\u002F4.queue-boundaries","i-lucide-panels-top-left",{"title":145,"icon":146,"path":147,"stem":148,"children":149,"page":35},"Reference","i-lucide-book-marked","\u002Freference","5.reference",[150],{"title":151,"path":152,"stem":153,"icon":154},"Configuration","\u002Freference\u002Fconfiguration","5.reference\u002F1.configuration","i-lucide-settings",{"id":156,"title":135,"body":157,"description":1141,"extension":1142,"links":1143,"meta":1144,"navigation":1145,"path":136,"seo":1146,"stem":137,"__hash__":1147},"docs\u002F4.operations\u002F3.flow-control.md",{"type":158,"value":159,"toc":1127},"minimark",[160,183,197,206,211,222,229,271,467,489,518,540,548,555,564,567,571,578,608,636,645,649,658,662,684,688,717,741,750,754,757,797,806,819,824,828,838,842,861,946,969,974,1003,1041,1056,1066,1076,1103,1112,1118,1123],[161,162,163,164,168,169,173,174,178,179,182],"p",{},"The flow-control plane (SQL contracts 0.4.0–0.6.6) regulates ",[165,166,167],"em",{},"how fast and how much"," work a queue takes on, and stops the fleet from hammering a failing downstream. Every feature is ",[170,171,172],"strong",{},"off by default and configured per queue"," — a queue that sets nothing gets the same control behavior as before: nothing gates, throttles, ages, or breaks its work. When a gate declines work it returns a typed ",[175,176,177],"code",{},"throttled"," claim verdict carrying ",[175,180,181],{},"retry_after_seconds",", so workers sleep exactly as told rather than guessing.",[184,185,186],"note",{},[161,187,188,189,192,193,196],{},"All operator verbs require the ",[175,190,191],{},"taskq_operator"," role. Over the CLI they are also mutation-safety gated (",[175,194,195],{},"--expected-environment \u003Cenv>","). Examples below show both the CLI and the raw SQL.",[184,198,199],{},[161,200,201,202,205],{},"One mechanical caveat, not a control change: since contract 0.4 every queue keeps an exact ",[175,203,204],{},"queue_counters"," accounting row updated on each status transition — the bookkeeping the health verdicts read. A single queue under many simultaneous settles can briefly serialize on that counter row. It is not a gate, and an unconfigured queue is never declined work.",[207,208,210],"h2",{"id":209},"circuit-breaker","Circuit breaker",[161,212,213,214,217,218,221],{},"Trips a queue ",[170,215,216],{},"open"," after N ",[165,219,220],{},"consecutive terminal failures",", so the whole fleet stops claiming from a dying downstream instead of each worker burning jobs (and paid proxy spend) rediscovering the outage. After a cooldown it admits exactly one probe; a success closes it (and slow-starts via the ramp), a failure re-opens it.",[161,223,224,225,228],{},"A configured breaker trips on ",[170,226,227],{},"any"," of three triggers:",[230,231,232,247,257],"ul",{},[233,234,235,238,239,242,243,246],"li",{},[170,236,237],{},"Streak"," (always on) — N ",[165,240,241],{},"consecutive"," terminal failures. Fast; catches a hard-down downstream. A single success resets it, so it never catches a ",[165,244,245],{},"flaky"," one.",[233,248,249,252,253,256],{},[170,250,251],{},"Rate"," (optional, 0.6.3) — a sustained failure ",[165,254,255],{},"ratio"," over a rolling window. Add it for downstreams that fail intermittently.",[233,258,259,262,263,266,267,270],{},[170,260,261],{},"Latency"," (optional, 0.6.4) — a rolling-window ",[165,264,265],{},"average execution latency"," over a threshold. The one that catches a downstream that is ",[170,268,269],{},"slow but still succeeding","; a slow success counts toward the window even at a 0% failure rate.",[272,273,278],"pre",{"className":274,"code":275,"language":276,"meta":277,"style":277},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","taskq queue set-breaker mail --failure-threshold 5 --cooldown-seconds 30 --half-open-successes 1\n# Optional rate trip: >=50% of the last 60s' settles failed, min 20 settles.\ntaskq queue set-breaker-rate mail --failure-ratio 0.5 --window-seconds 60 --min-volume 20\ntaskq queue set-breaker-rate mail --off   # remove the rate trip (keep streak)\n# Optional latency trip: avg execution latency over the last 60s >= 2000ms, min 10 settles.\ntaskq queue set-breaker-latency mail --threshold-ms 2000 --window-seconds 60 --min-volume 10\ntaskq queue set-breaker-latency mail --off  # remove the latency trip (keep streak\u002Frate)\ntaskq queue trip-breaker mail             # force open (planned downstream maintenance)\ntaskq queue close-breaker mail            # force closed + slow-start recovery\ntaskq queue set-breaker mail --off        # disable the whole breaker\n","bash","",[175,279,280,318,325,355,372,378,405,421,436,451],{"__ignoreMap":277},[281,282,285,289,293,296,299,302,306,309,312,315],"span",{"class":283,"line":284},"line",1,[281,286,288],{"class":287},"sBMFI","taskq",[281,290,292],{"class":291},"sfazB"," queue",[281,294,295],{"class":291}," set-breaker",[281,297,298],{"class":291}," mail",[281,300,301],{"class":291}," --failure-threshold",[281,303,305],{"class":304},"sbssI"," 5",[281,307,308],{"class":291}," --cooldown-seconds",[281,310,311],{"class":304}," 30",[281,313,314],{"class":291}," --half-open-successes",[281,316,317],{"class":304}," 1\n",[281,319,321],{"class":283,"line":320},2,[281,322,324],{"class":323},"sHwdD","# Optional rate trip: >=50% of the last 60s' settles failed, min 20 settles.\n",[281,326,328,330,332,335,337,340,343,346,349,352],{"class":283,"line":327},3,[281,329,288],{"class":287},[281,331,292],{"class":291},[281,333,334],{"class":291}," set-breaker-rate",[281,336,298],{"class":291},[281,338,339],{"class":291}," --failure-ratio",[281,341,342],{"class":304}," 0.5",[281,344,345],{"class":291}," --window-seconds",[281,347,348],{"class":304}," 60",[281,350,351],{"class":291}," --min-volume",[281,353,354],{"class":304}," 20\n",[281,356,358,360,362,364,366,369],{"class":283,"line":357},4,[281,359,288],{"class":287},[281,361,292],{"class":291},[281,363,334],{"class":291},[281,365,298],{"class":291},[281,367,368],{"class":291}," --off",[281,370,371],{"class":323},"   # remove the rate trip (keep streak)\n",[281,373,375],{"class":283,"line":374},5,[281,376,377],{"class":323},"# Optional latency trip: avg execution latency over the last 60s >= 2000ms, min 10 settles.\n",[281,379,381,383,385,388,390,393,396,398,400,402],{"class":283,"line":380},6,[281,382,288],{"class":287},[281,384,292],{"class":291},[281,386,387],{"class":291}," set-breaker-latency",[281,389,298],{"class":291},[281,391,392],{"class":291}," --threshold-ms",[281,394,395],{"class":304}," 2000",[281,397,345],{"class":291},[281,399,348],{"class":304},[281,401,351],{"class":291},[281,403,404],{"class":304}," 10\n",[281,406,408,410,412,414,416,418],{"class":283,"line":407},7,[281,409,288],{"class":287},[281,411,292],{"class":291},[281,413,387],{"class":291},[281,415,298],{"class":291},[281,417,368],{"class":291},[281,419,420],{"class":323},"  # remove the latency trip (keep streak\u002Frate)\n",[281,422,424,426,428,431,433],{"class":283,"line":423},8,[281,425,288],{"class":287},[281,427,292],{"class":291},[281,429,430],{"class":291}," trip-breaker",[281,432,298],{"class":291},[281,434,435],{"class":323},"             # force open (planned downstream maintenance)\n",[281,437,439,441,443,446,448],{"class":283,"line":438},9,[281,440,288],{"class":287},[281,442,292],{"class":291},[281,444,445],{"class":291}," close-breaker",[281,447,298],{"class":291},[281,449,450],{"class":323},"            # force closed + slow-start recovery\n",[281,452,454,456,458,460,462,464],{"class":283,"line":453},10,[281,455,288],{"class":287},[281,457,292],{"class":291},[281,459,295],{"class":291},[281,461,298],{"class":291},[281,463,368],{"class":291},[281,465,466],{"class":323},"        # disable the whole breaker\n",[272,468,472],{"className":469,"code":470,"language":471,"meta":277,"style":277},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","SELECT taskq.set_breaker_config('mail', 5, 30, 1, 'operator:admin');\nSELECT taskq.set_breaker_rate('mail', 0.5, 60, 20, 'operator:admin');\nSELECT taskq.set_breaker_latency('mail', 2000, 60, 10, 'operator:admin');\n","sql",[175,473,474,479,484],{"__ignoreMap":277},[281,475,476],{"class":283,"line":284},[281,477,478],{},"SELECT taskq.set_breaker_config('mail', 5, 30, 1, 'operator:admin');\n",[281,480,481],{"class":283,"line":320},[281,482,483],{},"SELECT taskq.set_breaker_rate('mail', 0.5, 60, 20, 'operator:admin');\n",[281,485,486],{"class":283,"line":327},[281,487,488],{},"SELECT taskq.set_breaker_latency('mail', 2000, 60, 10, 'operator:admin');\n",[490,491,492],"tip",{},[161,493,494,495,498,499,502,503,506,507,510,511,514,515,517],{},"The ",[170,496,497],{},"latency"," trigger is what catches a slow-but-succeeding downstream that streak and rate both miss. The ",[175,500,501],{},"breaker_opened"," event's ",[175,504,505],{},"reason"," field says which trigger fired (",[175,508,509],{},"streak",", ",[175,512,513],{},"rate",", or ",[175,516,497],{},").",[161,519,520,521,524,525,528,529,532,533,535,536,539],{},"Start with a ",[170,522,523],{},"conservative"," streak threshold (5–10) — too low false-trips a heterogeneous queue. For the rate trip, keep ",[175,526,527],{},"min-volume"," high enough (≥10–20) that a quiet queue's few failures don't trip it. For the latency trip, set ",[175,530,531],{},"threshold-ms"," well above the downstream's healthy p50 so normal jitter doesn't trip it, and keep ",[175,534,527],{}," ≥10 — it measures ",[165,537,538],{},"average"," latency per settle, so sustained slowness trips it while one slow job among many won't.",[207,541,543,544,547],{"id":542},"in-flight-cap-max_running","In-flight cap (",[175,545,546],{},"max_running",")",[161,549,550,551,554],{},"Caps concurrently-running jobs per queue, across the whole fleet. Use it — not worker concurrency — when the limit belongs to the ",[165,552,553],{},"downstream"," (a connection pool, an API that 429s past N in-flight).",[272,556,558],{"className":469,"code":557,"language":471,"meta":277,"style":277},"SELECT taskq.ensure_queue('mail', '{\"max_running\": 8}'::jsonb, 'operator:admin');\n",[175,559,560],{"__ignoreMap":277},[281,561,562],{"class":283,"line":284},[281,563,557],{},[161,565,566],{},"The cap is advisory under concurrency: a simultaneous burst can briefly overshoot, then settles. It is exact in aggregate.",[207,568,570],{"id":569},"rate-limits","Rate limits",[161,572,573,574,577],{},"Two independent keyspaces, both metering ",[170,575,576],{},"claims"," (work starts):",[230,579,580,594],{},[233,581,582,585,586,589,590,593],{},[170,583,584],{},"Queue-level"," — ",[175,587,588],{},"claim_rate_per_minute"," \u002F ",[175,591,592],{},"claim_burst"," on the queue profile (GCRA).",[233,595,596,599,600,603,604,607],{},[170,597,598],{},"Key-level"," — a ",[175,601,602],{},"flow_key"," that jobs carry, orthogonal to the queue. Unknown keys are ",[170,605,606],{},"unlimited"," (a politeness limiter must not serialize the world by default).",[272,609,611],{"className":274,"code":610,"language":276,"meta":277,"style":277},"taskq queue set-flow-limit provider.acme --rate-per-minute 120 --burst 10\n",[175,612,613],{"__ignoreMap":277},[281,614,615,617,619,622,625,628,631,634],{"class":283,"line":284},[281,616,288],{"class":287},[281,618,292],{"class":291},[281,620,621],{"class":291}," set-flow-limit",[281,623,624],{"class":291}," provider.acme",[281,626,627],{"class":291}," --rate-per-minute",[281,629,630],{"class":304}," 120",[281,632,633],{"class":291}," --burst",[281,635,404],{"class":304},[272,637,639],{"className":469,"code":638,"language":471,"meta":277,"style":277},"SELECT taskq.set_flow_limit('provider.acme', 120, 10, 'operator:admin');\n",[175,640,641],{"__ignoreMap":277},[281,642,643],{"class":283,"line":284},[281,644,638],{},[207,646,648],{"id":647},"slow-start-ramp","Slow-start ramp",[161,650,651,654,655,657],{},[175,652,653],{},"ramp_seconds"," on the profile: after a resume (or a breaker close), the queue's effective ",[175,656,546],{}," and claim rate scale from near-zero to full over the window, so a just-recovered downstream isn't stampeded. It composes automatically with the breaker.",[207,659,661],{"id":660},"job-ttl","Job TTL",[161,663,664,667,668,671,672,675,676,679,680,683],{},[175,665,666],{},"default_ttl_seconds"," on the profile, or ",[175,669,670],{},"p_ttl_seconds"," at enqueue. Expired ",[175,673,674],{},"queued","\u002F",[175,677,678],{},"blocked"," jobs are settled ",[175,681,682],{},"cancelled \u002F outcome='expired_ttl'"," by the tick. Running jobs are never TTL-killed — the lease governs in-flight work. Use it for work that is worthless if stale.",[207,685,687],{"id":686},"smear-anti-stampede","Smear (anti-stampede)",[230,689,690,707],{},[233,691,692,585,695,698,699,702,703,706],{},[170,693,694],{},"Redrive smear",[175,696,697],{},"taskq.redrive_failed(queue, limit, actor, smear_seconds)"," spreads redriven jobs' ",[175,700,701],{},"scheduled_at"," across ",[175,704,705],{},"[now, now+smear)"," instead of releasing them all at once.",[233,708,709,712,713,716],{},[170,710,711],{},"Schedule smear"," — a deterministic per-schedule firing offset so co-scheduled jobs (many cron entries at ",[175,714,715],{},":00",") de-align instead of stampeding.",[272,718,720],{"className":274,"code":719,"language":276,"meta":277,"style":277},"taskq schedule set-smear nightly-report --smear-seconds 300\n",[175,721,722],{"__ignoreMap":277},[281,723,724,726,729,732,735,738],{"class":283,"line":284},[281,725,288],{"class":287},[281,727,728],{"class":291}," schedule",[281,730,731],{"class":291}," set-smear",[281,733,734],{"class":291}," nightly-report",[281,736,737],{"class":291}," --smear-seconds",[281,739,740],{"class":304}," 300\n",[272,742,744],{"className":469,"code":743,"language":471,"meta":277,"style":277},"SELECT taskq.set_schedule_smear('nightly-report', 300, 'operator:admin');\n",[175,745,746],{"__ignoreMap":277},[281,747,748],{"class":283,"line":284},[281,749,743],{},[207,751,753],{"id":752},"priority-aging","Priority aging",[161,755,756],{},"A waiting job's effective claim priority improves with age, so a sustained high-priority flood cannot starve low-priority work forever. Opt-in per queue; fresh work is never inverted.",[272,758,760],{"className":274,"code":759,"language":276,"meta":277,"style":277},"taskq queue set-aging render --aging-seconds 60   # +1 priority step per 60s waited\ntaskq queue set-aging render --off                # strict priority\n",[175,761,762,782],{"__ignoreMap":277},[281,763,764,766,768,771,774,777,779],{"class":283,"line":284},[281,765,288],{"class":287},[281,767,292],{"class":291},[281,769,770],{"class":291}," set-aging",[281,772,773],{"class":291}," render",[281,775,776],{"class":291}," --aging-seconds",[281,778,348],{"class":304},[281,780,781],{"class":323},"   # +1 priority step per 60s waited\n",[281,783,784,786,788,790,792,794],{"class":283,"line":320},[281,785,288],{"class":287},[281,787,292],{"class":291},[281,789,770],{"class":291},[281,791,773],{"class":291},[281,793,368],{"class":291},[281,795,796],{"class":323},"                # strict priority\n",[272,798,800],{"className":469,"code":799,"language":471,"meta":277,"style":277},"SELECT taskq.set_priority_aging('render', 60, 'operator:admin');\n",[175,801,802],{"__ignoreMap":277},[281,803,804],{"class":283,"line":284},[281,805,799],{},[184,807,808],{},[161,809,810,811,814,815,818],{},"Aging applies to the ",[170,812,813],{},"normal claim path only"," — workflow ",[165,816,817],{},"continuation"," claims keep strict priority.",[184,820,821],{},[161,822,823],{},"Aging changes a configured queue's claim ordering from a bare index scan to a sort over the ready backlog, so each claim's cost grows with backlog depth (O(ready depth)). Unconfigured queues are unaffected — they keep the index-backed claim. Enable aging where fairness matters more than raw claim throughput on a deep queue.",[207,825,827],{"id":826},"notify-mode","Notify mode",[161,829,830,833,834,837],{},[175,831,832],{},"notify_mode = 'on_idle_transition'"," on the profile fires the wake-up NOTIFY only when the queue was idle before an enqueue, cutting notify volume on busy queues. Leave it ",[175,835,836],{},"'always'"," (default) unless NOTIFY volume is itself a problem.",[207,839,841],{"id":840},"observing-flow-control","Observing flow control",[161,843,844,845,848,849,852,853,856,857,860],{},"Since 0.6.2 a tripped breaker surfaces as the ",[175,846,847],{},"breaker_open"," ",[170,850,851],{},"health verdict"," (with breaker state in the health ",[175,854,855],{},"detail",") and as ",[170,858,859],{},"job events"," on each automatic transition. Use the health surface for \"is it open now\" and events for \"when did it trip.\"",[272,862,864],{"className":469,"code":863,"language":471,"meta":277,"style":277},"-- Is any breaker open right now? (verdict + breaker detail)\nSELECT queue, verdict, detail -> 'breaker' AS breaker\nFROM taskq.queue_health(NULL) WHERE verdict = 'breaker_open';\n\n-- Breaker timeline (automatic transitions): opened \u002F reopened \u002F closed.\nSELECT e.created_at, e.event_type, e.data\nFROM taskq.job_events e JOIN taskq.jobs j ON j.id = e.job_id\nWHERE j.queue = 'mail' AND e.event_type LIKE 'breaker_%'\nORDER BY e.created_at DESC;\n\n-- Full flow state for a queue; live levels + throughput.\nSELECT breaker_state, breaker_failure_streak, breaker_opened_total, breaker_tripped_at,\n       priority_aging_seconds, ramp_started_at\nFROM taskq.queue_flow WHERE queue = 'mail';\nSELECT * FROM taskq.queue_counters WHERE queue = 'mail';\n",[175,865,866,871,876,881,887,892,897,902,907,912,916,922,928,934,940],{"__ignoreMap":277},[281,867,868],{"class":283,"line":284},[281,869,870],{},"-- Is any breaker open right now? (verdict + breaker detail)\n",[281,872,873],{"class":283,"line":320},[281,874,875],{},"SELECT queue, verdict, detail -> 'breaker' AS breaker\n",[281,877,878],{"class":283,"line":327},[281,879,880],{},"FROM taskq.queue_health(NULL) WHERE verdict = 'breaker_open';\n",[281,882,883],{"class":283,"line":357},[281,884,886],{"emptyLinePlaceholder":885},true,"\n",[281,888,889],{"class":283,"line":374},[281,890,891],{},"-- Breaker timeline (automatic transitions): opened \u002F reopened \u002F closed.\n",[281,893,894],{"class":283,"line":380},[281,895,896],{},"SELECT e.created_at, e.event_type, e.data\n",[281,898,899],{"class":283,"line":407},[281,900,901],{},"FROM taskq.job_events e JOIN taskq.jobs j ON j.id = e.job_id\n",[281,903,904],{"class":283,"line":423},[281,905,906],{},"WHERE j.queue = 'mail' AND e.event_type LIKE 'breaker_%'\n",[281,908,909],{"class":283,"line":438},[281,910,911],{},"ORDER BY e.created_at DESC;\n",[281,913,914],{"class":283,"line":453},[281,915,886],{"emptyLinePlaceholder":885},[281,917,919],{"class":283,"line":918},11,[281,920,921],{},"-- Full flow state for a queue; live levels + throughput.\n",[281,923,925],{"class":283,"line":924},12,[281,926,927],{},"SELECT breaker_state, breaker_failure_streak, breaker_opened_total, breaker_tripped_at,\n",[281,929,931],{"class":283,"line":930},13,[281,932,933],{},"       priority_aging_seconds, ramp_started_at\n",[281,935,937],{"class":283,"line":936},14,[281,938,939],{},"FROM taskq.queue_flow WHERE queue = 'mail';\n",[281,941,943],{"class":283,"line":942},15,[281,944,945],{},"SELECT * FROM taskq.queue_counters WHERE queue = 'mail';\n",[184,947,948],{},[161,949,494,950,953,954,957,958,961,962,675,965,968],{},[170,951,952],{},"automatic"," breaker timeline lives in ",[175,955,956],{},"job_events"," (above). ",[170,959,960],{},"Operator"," actions — manual ",[175,963,964],{},"trip",[175,966,967],{},"close"," and every config change — live in the queue audit log (below), attributed to the actor who made them.",[970,971,973],"h3",{"id":972},"operator-audit-log","Operator audit log",[161,975,976,977,510,980,510,983,510,986,510,989,510,992,995,996,999,1000,1002],{},"Since 0.6.5, every queue-scoped operator verb (",[175,978,979],{},"set-breaker",[175,981,982],{},"set-breaker-rate",[175,984,985],{},"set-breaker-latency",[175,987,988],{},"trip-breaker",[175,990,991],{},"close-breaker",[175,993,994],{},"set-aging",") records an append-only audit row with its actor and a ",[175,997,998],{},"{before, after}"," detail — config-history plus the manual-trip trail that ",[175,1001,956],{}," never captured. A failed verb writes nothing (the row rolls back with the action).",[272,1004,1006],{"className":274,"code":1005,"language":276,"meta":277,"style":277},"taskq queue audit mail                    # last 50 operator actions, newest first\ntaskq queue audit mail --before-id 1234   # page: entries with an id below 1234\n",[175,1007,1008,1022],{"__ignoreMap":277},[281,1009,1010,1012,1014,1017,1019],{"class":283,"line":284},[281,1011,288],{"class":287},[281,1013,292],{"class":291},[281,1015,1016],{"class":291}," audit",[281,1018,298],{"class":291},[281,1020,1021],{"class":323},"                    # last 50 operator actions, newest first\n",[281,1023,1024,1026,1028,1030,1032,1035,1038],{"class":283,"line":320},[281,1025,288],{"class":287},[281,1027,292],{"class":291},[281,1029,1016],{"class":291},[281,1031,298],{"class":291},[281,1033,1034],{"class":291}," --before-id",[281,1036,1037],{"class":304}," 1234",[281,1039,1040],{"class":323},"   # page: entries with an id below 1234\n",[272,1042,1044],{"className":469,"code":1043,"language":471,"meta":277,"style":277},"SELECT id, event_type, actor, detail, created_at\nFROM taskq.list_queue_audit('mail', 50, NULL);\n",[175,1045,1046,1051],{"__ignoreMap":277},[281,1047,1048],{"class":283,"line":284},[281,1049,1050],{},"SELECT id, event_type, actor, detail, created_at\n",[281,1052,1053],{"class":283,"line":320},[281,1054,1055],{},"FROM taskq.list_queue_audit('mail', 50, NULL);\n",[161,1057,1058,1059,1061,1062,1065],{},"Read access is ",[175,1060,191],{}," + ",[175,1063,1064],{},"taskq_observer",".",[161,1067,1068,1069,1072,1073,1075],{},"The log is append-only; cap its growth with the maintenance prune verb (",[175,1070,1071],{},"taskq_housekeeper"," or ",[175,1074,191],{},"), safe to run on a schedule:",[272,1077,1079],{"className":274,"code":1078,"language":276,"meta":277,"style":277},"taskq maintenance prune-audit --older-than-hours 2160 --yes   # destructive: --yes required\n",[175,1080,1081],{"__ignoreMap":277},[281,1082,1083,1085,1088,1091,1094,1097,1100],{"class":283,"line":284},[281,1084,288],{"class":287},[281,1086,1087],{"class":291}," maintenance",[281,1089,1090],{"class":291}," prune-audit",[281,1092,1093],{"class":291}," --older-than-hours",[281,1095,1096],{"class":304}," 2160",[281,1098,1099],{"class":291}," --yes",[281,1101,1102],{"class":323},"   # destructive: --yes required\n",[272,1104,1106],{"className":469,"code":1105,"language":471,"meta":277,"style":277},"SELECT taskq.prune_queue_audit(2160);  -- returns the number of rows removed\n",[175,1107,1108],{"__ignoreMap":277},[281,1109,1110],{"class":283,"line":284},[281,1111,1105],{},[161,1113,1114,1115,1117],{},"Worker-side, ",[175,1116,177],{}," verdicts (from any gate) surface as throttle counts in the worker snapshot — they are not errors and do not trip the claim-error backoff.",[490,1119,1120],{},[161,1121,1122],{},"Roll out one queue and one feature at a time: read the baseline, set a conservative value, watch through a real failure or load cycle, then tighten. Never set an aggressive value on a queue you have not watched.",[1124,1125,1126],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":277,"searchDepth":284,"depth":320,"links":1128},[1129,1130,1132,1133,1134,1135,1136,1137,1138],{"id":209,"depth":320,"text":210},{"id":542,"depth":320,"text":1131},"In-flight cap (max_running)",{"id":569,"depth":320,"text":570},{"id":647,"depth":320,"text":648},{"id":660,"depth":320,"text":661},{"id":686,"depth":320,"text":687},{"id":752,"depth":320,"text":753},{"id":826,"depth":320,"text":827},{"id":840,"depth":320,"text":841,"children":1139},[1140],{"id":972,"depth":327,"text":973},"Circuit breaker, rate limits, in-flight caps, slow-start ramps, TTL, smear, and priority aging — everything off by default, per queue.","md",null,{},{"icon":138},{"title":135,"description":1141},"19kSM31rmnzIOQlMgVW45cwORzG58TO2zJ1aGmb8p_g",[1149,1151],{"title":130,"path":131,"stem":132,"description":1150,"icon":133,"children":-1},"Failed jobs, lineage, and operator redrive.",{"title":140,"path":141,"stem":142,"description":1152,"icon":143,"children":-1},"Design queues as independent operational bulkheads without confusing them with database or task-type boundaries.",1787515776866]