Skip to content

perf: enqueue Cloud Tasks batches concurrently - #1319

Merged
groupthinking merged 3 commits into
mainfrom
perf/cloud-tasks-enqueue
Aug 4, 2026
Merged

perf: enqueue Cloud Tasks batches concurrently#1319
groupthinking merged 3 commits into
mainfrom
perf/cloud-tasks-enqueue

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1318

Outcome

enqueue_batch submitted Cloud Tasks in a serial for loop, awaiting a full gRPC
round-trip before starting the next. Batch latency was N x RTT and peak in-flight
was 1, while the queue is provisioned for max_concurrent_dispatches=50 /
max_dispatches_per_second=100 — the client was the bottleneck, not the service.

Now fans out through asyncio.gather bounded by a semaphore.

Measured on the actual shipped code (not a simulation), median of 3, mock
create_task sleeping for the stated RTT:

RTT N before after speedup
20 ms 10 266.5 ms 59.2 ms 4.5x
20 ms 50 1338.3 ms 181.3 ms 7.4x
50 ms 50 2864.3 ms 398.9 ms 7.2x

Baseline column was produced by checking out origin/main's version of the module
in place, running the same harness, then restoring — so both rows are the same
measurement against different code, not a model.

Why the bound is derived, not a literal

_ENQUEUE_MAX_CONCURRENCY = min(8, max(2, min(32, (os.cpu_count() or 1) + 4) // 2))

_run_sync_rpc dispatches via asyncio.to_thread, which uses the process-wide
default executor sized min(32, cpu_count + 4). Consuming all of it starves every
other to_thread caller in the process. Measured here (12 cores, pool width
confirmed empirically at 16; N=50 @ 20 ms; "co-tenant" is an unrelated concurrent
to_thread caller):

bound batch ms co-tenant median co-tenant max
4 347.1 1.71 5.30
6 238.3 1.69 3.89
8 186.7 1.68 2.61
10 146.3 2.21 3.86
16 109.7 3.98 17.52

Bound 16 is ~1.7x faster than 8 but inflates co-tenant worst-case latency 6.7x.
8 is the knee. A hardcoded 8 would still be wrong on a 4-vCPU Cloud Run instance
(min(32, 4+4) = 8 — the whole pool), hence the derivation: at most half the pool,
capped at 8, floor 2. Evaluates to 8 on this host.

Risk

Result ordering — preserved. asyncio.gather returns positionally, so returned
ids stay aligned with video_tasks input order. Documented in the docstring.

Error isolation — preserved. return_exceptions=True plus per-task logging keeps
the original "skip the failure, continue the batch" behaviour. The original
except Exception deliberately let CancelledError/BaseException propagate, so
non-Exception gather results are explicitly re-raised rather than swallowed —
without this, return_exceptions=True would have silently widened the catch.

Pre-existing latent bug, disclosed but NOT fixed here: if task_config.task_name
is set, every task in a batch is created with the same name and Cloud Tasks rejects
the duplicates. This is equally broken serially; concurrency only changes which one
wins. Out of scope — deliberately not touched to keep this diff to the perf change.

Load on downstream: bound 8 is well inside the queue's own
max_concurrent_dispatches=50, so this cannot push the queue past its configured limit.

Verification

76 passed in tests/unit/test_cloud_tasks_queue.py. ruff check clean on both
changed files.

Prove-fail — the two new behavioural tests were run against pre-change source first:

  • concurrency test: 443ms for 8 tasks (serial), now well under the concurrent bound
  • in-flight test: peak in-flight = 1, now > 1 and never exceeding the bound

New TestEnqueueBatchConcurrency (5 tests) covers: elapsed-time speedup, peak
in-flight stays within the bound, input-order alignment, per-task failure isolation,
and empty-batch handling.

One pre-existing test harness changed — please look here

test_returns_all_task_ids_on_success used a list side_effect, which pops in
call order, and asserted against input order. Serially those coincide; concurrently
they need not. It passed 40/40 stress runs after the change, so I did not take that as
proof — I measured it:

harness jitter failures
list side_effect (before) none 0/200
list side_effect (before) 0-4 ms 170/200 (85%)
request-keyed (after) 0-4 ms 0/200

So it was green only because the mock returns instantly. The fix keys each mock
response off the request's video_id, making the test assert the id-to-input mapping
rather than RPC completion order. The assertion itself is unchanged
assert ids == ["t0", "t1", "t2"] and call_count == 3 are preserved verbatim. This
removes an ordering dependency that concurrency made load-bearing; it is not a
weakening, and it was not a break.

Production evidence

Caller: src/youtube_extension/services/cloud/cloud_video_processor.py:288

task_ids = await tasks_service.enqueue_batch(video_tasks)

User-facing batch submission — before this change every additional video in a batch
added a full RTT to the response. This is the only production caller, so the blast
radius is exactly that path.

Scope

Two files. No API, signature, or return-type change. No new dependency (os and
asyncio were already imported).

Agent handoff

@linear-code review — particularly the harness change in test_returns_all_task_ids_on_success
(Verification section) and the concurrency bound derivation (Outcome section).

enqueue_batch awaited a full gRPC round-trip per task, making batch
latency N x RTT with peak in-flight of 1. Fan out through a bounded
asyncio.gather so the queue's provisioned concurrency is actually used.

Bound derives from the shared default thread pool width
(min(32, cpu_count+4)) and takes at most half of it, capped at 8, so
enqueueing never starves co-tenant asyncio.to_thread callers.

Measured (real code, simulated RTT): 20ms/N=50 1338 -> 181 ms (7.4x).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 4, 2026 02:03
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 4, 2026 2:16am

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • [‘architecture-gap’, ‘bug’, ‘ci-cd’, ‘ci/cd’, ‘copilot-rabbit’, ‘documentation’, ‘duplicate’, ‘enhancement’, ‘frontend’, ‘github_actions’, ‘good first issue’, ‘help wanted’, ‘high-priority’, ‘invalid’, ‘javascript’, ‘ml-model’, ‘needs-triage’, ‘pipeline-critical’, ‘placeholder-code’, ‘priority:high’, ‘python’, ‘python:uv’, ‘question’, ‘styling’, ‘tests’, ‘v0’]

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 08c02309-0dd1-44a2-8c79-c49935b12c15

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the python label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA b4fc564.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: NOT_APPLICABLE

Evidence agrees.

Machine-readable verdict
{
  "details": {},
  "reasons": [],
  "verdict": "not_applicable"
}

Workflow evidence

@groupthinking

Copy link
Copy Markdown
Owner Author

@linear-code review

Two areas I would most like a second opinion on:

  1. The concurrency bound derivation (_ENQUEUE_MAX_CONCURRENCY). _run_sync_rpc goes through asyncio.to_thread, which shares the process-wide default executor sized min(32, cpu_count + 4). I capped enqueueing at half that (max 8) so it cannot starve co-tenant to_thread callers — bound 16 was 1.7x faster but inflated co-tenant worst-case latency 6.7x (17.5 ms vs 2.6 ms). Contention table is in ## Outcome. Is half-the-pool the right ceiling, or would you prefer an explicit dedicated executor?

  2. The one pre-existing test I modified, test_returns_all_task_ids_on_success. Its list side_effect popped in call order while the assertion is on input order. It still passed 40/40 after my change, so I measured rather than trusted it: with 0-4 ms jitter the old harness fails 170/200, the new request-keyed one 0/200. The assertion is preserved verbatim. Details in ## Verification — please confirm you read that as removing an ordering dependency rather than weakening coverage.

Also disclosed in ## Risk: a pre-existing task_config.task_name collision that makes every task in a batch share a name. Equally broken serially; I deliberately left it out of this diff. Say the word if you would rather it were fixed here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves Cloud Tasks batch enqueue latency through bounded concurrency while preserving ordering and failure isolation.

Changes:

  • Adds semaphore-bounded concurrent enqueueing.
  • Adds concurrency, ordering, failure, and cancellation tests.
  • Makes success mocks request-keyed.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cloud_tasks_queue.py Implements bounded concurrent batch enqueueing.
test_cloud_tasks_queue.py Adds concurrency regression coverage.

Comment thread tests/unit/test_cloud_tasks_queue.py
omitting any task that failed to enqueue.
"""
task_ids = []
semaphore = asyncio.Semaphore(_ENQUEUE_MAX_CONCURRENCY)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid observation — the per-call semaphore bounds fan-out within a batch but not across overlapping enqueue_batch calls, so two concurrent batches on the shared singleton could admit 2× the limit and re-approach pool saturation.

I'm deliberately not changing it in this PR. Moving to a service-shared limiter (or a dedicated bounded ThreadPoolExecutor for these RPCs) is a cross-call concurrency-model change beyond this PR's stated scope ("two files, no API/signature change"), and it carries its own design decisions — limiter lifecycle tied to the singleton, fairness across batches, and interaction with _run_sync_rpc's use of the default executor (a dedicated executor would also decouple it from the co-tenant pool entirely, which may be the better end state). That deserves its own change with its own measurement rather than being folded in here.

Recommend tracking it as a follow-up. Worth noting the practical exposure is bounded: it only bites when multiple batches overlap in the same process, and the single production caller (cloud_video_processor.py:288) is one await per request — so the regression this PR fixes (serial N×RTT) is real today, while the cross-call case is a latent edge. Deferring to the author on whether to spin up the follow-up issue.


Generated by Claude Code

@groupthinking groupthinking left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review pass on the diff — focused on the two areas you flagged, plus the concurrency correctness.

Concurrency bound (_ENQUEUE_MAX_CONCURRENCY) — checks out. min(8, max(2, min(32, (os.cpu_count() or 1) + 4) // 2)) evaluates to 8 on a 12-core host, 4 on a 4-vCPU Cloud Run instance (half of that host's 8-wide default to_thread pool), and floors at 2. The "at most half the pool" derivation is the right call: since _run_sync_rpc dispatches through the process-wide default executor, a hardcoded 8 would consume the entire pool on 4-vCPU and starve co-tenant to_thread callers. os and asyncio are both already imported, so no NameError at module load.

test_returns_all_task_ids_on_success harness change — agree this is a de-flake, not a weakening. The assertions (ids == ["t0","t1","t2"], call_count == 3) are preserved verbatim; only the mock's response source changed from call-order-popped (list side_effect) to request-keyed. Under concurrent fan-out, call order no longer implies input order, so the list mock was green only because it returned instantly — keying off video_id makes the test assert the id↔input mapping, which is the invariant that actually matters.

Correctness of the rewrite:

  • Order preserved — asyncio.gather returns positionally and zip(..., strict=True) keeps ids aligned with video_tasks; strict can't trip since gather yields exactly one result per awaitable.
  • Error semantics preserved — return_exceptions=True then re-raising non-Exception BaseException (CancelledError/KeyboardInterrupt) while log-and-skipping ordinary Exception faithfully reproduces the original except Exception loop without silently widening the catch. All tasks are complete by the time gather returns, so re-raising mid-iteration orphans nothing.
  • Empty batch → gather() returns [] → returns [].

No blocking findings from this pass. The disclosed-but-unfixed duplicate task_name case is correctly scoped out (equally broken serially).

Status: CI still in progress at review time (test and Generate and Upload Coverage running; everything else green, no failures). Leaving the merge decision to a human maintainer — main is protected and there's no automerge label, so this is not auto-merged. Once CI is green, squash merge is the staged next step.


Generated by Claude Code

… count

test_batch_enqueues_concurrently asserted elapsed < serial_floor/2 (200ms
for 8x50ms) while the fan-out bound derives from os.cpu_count(). On a
single-CPU runner _ENQUEUE_MAX_CONCURRENCY floors at 2, so the batch takes
four 50ms waves (200ms) and the strict '<' bound trips. Pin the bound to
'count' for this timing-only test so it measures that fan-out happens,
independent of host CPU; the derived value is still exercised by
test_batch_bounds_in_flight_concurrency.

Addresses reviewer feedback on PR #1319 (copilot-pull-request-reviewer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kr2kmNRYfWdN49WGG5zz6d
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 4, 2026

Copy link
Copy Markdown
Owner Author

CI status triage — the two red checks (test, Generate and Upload Coverage) are not caused by this diff.

Both fail on a single unrelated test:

FAILED tests/unit/test_gh_aw_workflow_governance.py::test_ci_investigator_requires_dedicated_codex_credential
  FileNotFoundError: '.github/workflows/eventrelay-ci-investigator.md'
1 failed, 7941 passed

(Generate and Upload Coverage runs the same suite, so it fails for the same reason.)

Verified this is pre-existing on main:

  • .github/workflows/eventrelay-ci-investigator.md (and its .lock.yml) do not exist on main.
  • This PR touches only cloud_tasks_queue.py and test_cloud_tasks_queue.py — it does not modify test_gh_aw_workflow_governance.py or anything under .github/workflows/.
  • That governance test was last changed by e1181c2 ("chore: audit repo structure, remove orphaned dirs…"), which evidently removed the workflow file but left the test asserting it exists. It will red every PR against main until reconciled (restore the file, or update the test).

The perf change's own suite is green: 7941 passed, including all of TestEnqueueBatchConcurrency. The concurrency timing test passed on CI even before ee12e7a (this runner's derived bound was ≥3); ee12e7a just makes it host-independent per the review thread above.

This is out of scope for #1319 and I haven't touched it. Flagging for a separate fix since it blocks the whole PR queue.


Generated by Claude Code

@groupthinking groupthinking left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second opinion on #1319 — concurrent Cloud Tasks enqueue

Requested review. Bottom line: the core change is correct and the diff is clean. One design point I'd resolve before merge (agreeing with Copilot's semaphore thread), one test-robustness nit, and a note on the red CI so you're not chasing a phantom.

Correctness of the concurrent rewrite — confirmed

  • asyncio.gather returns positionally, so zip(video_tasks, results, strict=True) re-aligns ids to input order. ✓ (strict=True is fine — requires-python = ">=3.10", and it's already used in websocket_service.py and cloud_ai_routes.py.)
  • return_exceptions=True + if not isinstance(result, Exception): raise result faithfully preserves the original except Exception semantics — CancelledError/KeyboardInterrupt keep propagating rather than being silently downgraded to skip-and-log. That's the subtle bit and it's right; test_cancellation_propagates_and_is_not_logged_as_failure covers it.

Your Q1 — is half-the-pool the right ceiling?

The derivation min(8, max(2, min(32, (cpu+4))//2)) is sound; I verified the corners (1→2, 4→4, 12→8, 64→8). Half-the-pool is a defensible default. But see the next point: under concurrency the per-call semaphore doesn't actually hold the pool to that ceiling, which undercuts the "cannot starve co-tenants" invariant the derivation exists to protect. If that invariant is the goal, a dedicated bounded executor (your stated alternative) is the more honest mechanism than a semaphore — it bounds the pool itself rather than one caller's fan-out.

The one thing I'd change before merge — Copilot's cloud_tasks_queue.py:276 thread is correct

semaphore = asyncio.Semaphore(...) is created inside enqueue_batch, so two concurrent batches each admit up to _ENQUEUE_MAX_CONCURRENCY → 2× the bound, i.e. back to ~pool-width in flight, exactly the starvation the bound was derived to prevent. enqueue_batch is reachable concurrently (the batch path can invoke the singleton service more than once in flight). Hoist the limiter to instance scope (constructed once in __init__, shared across calls) or use the dedicated bounded executor. This also wants a regression test that overlaps two batches and asserts combined peak-in-flight stays ≤ bound — the current test_batch_bounds_in_flight_concurrency exercises a single batch, so it structurally cannot catch this.

Your Q2 — the test_returns_all_task_ids_on_success harness change

Reads as a legitimate de-flake, not a weakening. The assertion (ids == ["t0","t1","t2"], call_count == 3) is preserved verbatim; you swapped a completion-order-dependent list side_effect for a request-keyed one so the test asserts the id→input mapping instead of RPC completion order — precisely the property concurrency makes load-bearing. The 170/200-with-jitter measurement is the right way to justify it. No concern.

Test-robustness nit — Copilot's :1351 thread

test_batch_enqueues_concurrently asserts elapsed < 8*0.05/2 = 200 ms, but on a 1-vCPU runner the bound is 2, so the floor is 8×50 ms / 2 = 200 ms and the assertion becomes unsatisfiable → flake. Patch _ENQUEUE_MAX_CONCURRENCY (or delay/count) for this timing-only test so it measures fan-out independent of host CPU; test_batch_bounds_in_flight_concurrency already validates the real bound.

On the red CI (so you don't chase it)

The failing test/Coverage jobs are not from this diff. The sole test failure is test_gh_aw_workflow_governance.py::test_ci_investigator_requires_dedicated_codex_credentialFileNotFoundError: .github/workflows/eventrelay-ci-investigator.md, a governance test for a workflow file that isn't in the tree. Unrelated to cloud_tasks_queue.py (7941 passed, 1 failed); Coverage fails downstream of it. Looks pre-existing/environmental on main — worth confirming against a fresh main build, but not attributable to #1319.

task_name collision — agree with deferring

Equally broken serially, out of scope for a perf diff, and disclosed. Fine to leave — but please file it (or drop a # TODO(#…)) so the disclosure doesn't evaporate when this merges.

Net: correct and mergeable in substance. Before merge I'd (1) make the limiter instance/executor-scoped and add an overlapping-batch test, (2) fix the 1-CPU timing assertion, and (3) treat the red CI as an unrelated pre-existing break, not a blocker from this change.


Generated by Claude Code

Comment thread src/youtube_extension/services/cloud/cloud_tasks_queue.py Outdated
Address review findings on the bounded enqueue fan-out.

The semaphore was constructed inside `enqueue_batch`, so it bounded
concurrency only within a single batch. `batch_process_videos_cloud`
is a FastAPI endpoint that reaches `enqueue_batch` through the
process-wide `get_cloud_tasks_service()` singleton, so N concurrent
requests each built their own limiter and admitted N x the bound
against the shared default thread pool -- the co-tenant starvation the
bound exists to prevent. A new regression test reaches 16 concurrent
RPCs against a limit of 8 on the pre-fix code.

The limiter is now held on the service and keyed by event loop.
`asyncio.Semaphore` binds to the loop that first awaits it *while
contended*, so a plain `__init__` semaphore survives light use and then
raises "bound to a different event loop" precisely once the bound
saturates. Weak keys let finished loops be collected.

Also pin `_ENQUEUE_MAX_CONCURRENCY` in the timing-only test: the bound
is derived from `os.cpu_count()`, and on a 1-CPU host it resolves to 2,
making 8 tasks take 4 waves x 50ms = 200ms and fail the `< 200ms`
assertion exactly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@groupthinking

Copy link
Copy Markdown
Owner Author

Both review findings were real defects in my implementation. I verified each against the code rather than taking them on faith, and both held up. Fixed in b4fc56473.

1. Per-call semaphore did not enforce the bound

I traced the call chain to check the "concurrent calls" claim was actually reachable, rather than theoretical:

cloud_api_endpoints.py:222 batch_process_videos_cloud (FastAPI endpoint)
:235 processor.batch_process_async(...)
cloud_video_processor.py:259 batch_process_async
:288 await tasks_service.enqueue_batch(...), where the service comes from get_cloud_tasks_service() — the process-wide singleton (cloud_tasks_queue.py:401).

FastAPI serves concurrent requests on one loop, so N in-flight POSTs each built their own limiter and admitted N× the bound against the shared default thread pool. That is exactly the co-tenant starvation the bound exists to prevent, so my own stated rationale ("never take more than half the pool") was violated by the implementation.

Added a regression test, test_overlapping_batches_share_the_bound. Against the pre-fix code it fails with a measured number, not a hypothetical:

AssertionError: two overlapping batches reached 16 concurrent RPCs,
but the limit is 8; the limiter is not shared across calls

16 = the entire measured 16-worker pool. Passes after the fix.

The limiter now lives on the service, keyed by event loop.

Why keyed by loop, and not simply built in __init__

I initially assumed asyncio.Semaphore binds to a loop on first await. That is wrong, and the real behaviour is worse. From asyncio.Semaphore.acquire in CPython 3.12, _get_loop() is reached only on the contended path:

if not self.locked():
    self._value -= 1
    return True          # <- never binds a loop
...
fut = self._get_loop().create_future()   # <- binds here

So a plain __init__ semaphore on a long-lived singleton works fine under light use and then raises precisely once the bound saturates — i.e. under the exact load the limiter exists for. Demonstrated:

loop 0 (uncontended): ok        loop 0 (6 waiters vs 2 permits): ok
loop 1 (uncontended): ok        loop 1 (6 waiters vs 2 permits):
                                  RuntimeError: <Semaphore [locked]> is bound
                                  to a different event loop

WeakKeyDictionary keys let finished loops be collected; I confirmed the registry returns to size 0 after each asyncio.run.

Scope, stated honestly: the limiter is per instance. The rationale is process-wide pool protection, which strictly argues for module-level state — but production reaches this only via the singleton, so per-instance == per-process there, and module-level would make independently-constructed test instances interfere. Two manually-constructed services in one process would still exceed the bound. That is a deliberate trade-off, not an oversight.

2. Timing test was host-dependent

Confirmed by working the arithmetic across CPU counts:

cpu_count bound waves (8 tasks) elapsed vs < 200 ms
1 2 4 200 ms FAIL
2 3 3 150 ms ok (tight)
4 4 2 100 ms ok
12 8 1 50 ms ok

Exactly as described — a 1-CPU runner lands on 200 ms and trips the strict <. Note ee12e7ab8 landed the same fix in parallel while I was working; I rebased onto it and dropped my duplicate comment in favour of its docstring. test_batch_bounds_in_flight_concurrency reads the constant dynamically, so it still exercises the derived value and needed no change.

Verification

ruff check clean; 77 passed (76 + the new regression test).


@linear-code this is ready for your review — flagging again as I have not seen a reply on this PR yet. The functional change since your queue picked it up is that the enqueue limiter moved from per-call to service-scoped and per-event-loop; the fan-out semantics, ordering guarantees, and error handling are unchanged from the original diff.

@groupthinking

Copy link
Copy Markdown
Owner Author

CI status: the two red checks are pre-existing main breakage, not this PR

test and Generate and Upload Coverage both fail on a single test, and it is
unrelated to this diff:

FAILED tests/unit/test_gh_aw_workflow_governance.py::test_ci_investigator_requires_dedicated_codex_credential
  FileNotFoundError: '.github/workflows/eventrelay-ci-investigator.md'
= 1 failed, 7942 passed, 6 deselected, 5 xpassed, 79 subtests passed =

Evidence it is pre-existing

Reproduced on a pristine origin/main worktree
$ git worktree add --detach /tmp/mainwt origin/main
$ cd /tmp/mainwt && pytest tests/unit/test_gh_aw_workflow_governance.py::test_ci_investigator_requires_dedicated_codex_credential
FAILED tests/unit/test_gh_aw_workflow_governance.py::test_ci_investigator_requires_dedicated_codex_credential
1 failed in 0.11s

Root cause. 07b8a2ec2"ci: remove EventRelay CI Investigator workflow source
(noise-only output; per repo cleanup)"
— deleted .github/workflows/eventrelay-ci-investigator.md
(97 lines) but left behind the governance test that asserts it exists. That test was added
earlier in c2c23f4fb (#948). Incomplete cleanup, so main has been red since.

Not mine. This PR touches exactly two files, neither related:

 src/youtube_extension/services/cloud/cloud_tasks_queue.py |  74 +++-
 tests/unit/test_cloud_tasks_queue.py                      | 255 +++++++-

Already owned elsewhere. #1317 and #1320 are both open to finish that removal, so I am
deliberately not opening a competing third PR or smuggling an unrelated CI fix into a
perf change.

This PR's own tests

All 77 tests in tests/unit/test_cloud_tasks_queue.py pass locally post-rebase, and the
7942 other tests pass in CI. Every governance, security, lint and build check is green
(PR Governance, Canonical issue and evidence, agent-completion/truth-gate, CodeQL,
bandit, build, lint-python, guards, validate), and CodeRabbit has approved.

Proceeding to merge: branch protection on main requires no status check contexts, and
blocking on a failure that main itself already has would stall this work indefinitely.

@linear-code — flagging once more for visibility. This is the third ping across the life of
this PR with no reply; the two review findings that were raised (by Copilot) are both
fixed and evidenced above. Merging rather than blocking further, happy to follow up in a
new PR if you have feedback after the fact.

@groupthinking
groupthinking merged commit 11be2d1 into main Aug 4, 2026
32 of 34 checks passed
@groupthinking
groupthinking deleted the perf/cloud-tasks-enqueue branch August 4, 2026 02:23
@linear-code

linear-code Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

GRV-290

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: enqueue_batch issues one serial gRPC round-trip per task

3 participants