Skip to content

Use listing-scoped concurrency group for PrCard creation on shared submission realm#4251

Open
richardhjtan wants to merge 1 commit intomainfrom
CS-10513-concurrency-group-of-submission-works
Open

Use listing-scoped concurrency group for PrCard creation on shared submission realm#4251
richardhjtan wants to merge 1 commit intomainfrom
CS-10513-concurrency-group-of-submission-works

Conversation

@richardhjtan
Copy link
Contributor

@richardhjtan richardhjtan commented Mar 26, 2026

Summary

  • Fix submission queue bottleneck where all PrCard creation jobs serialized under a single concurrency group because they all target the shared submission realm
  • Scope the PrCard creation job's concurrency group by listing branch name, so different listings process in parallel while same-listing operations remain serialized
  • Add optional concurrencyGroup override to enqueueRunCommandJob for callers that need finer-grained control

Problem

The job queue enforces that only one job per concurrency group runs at a time. All run-command jobs default to command:${realmURL} as their group. The submission flow has 3 sequential jobs:

  1. CreateSubmission — targets the user's realm (already unique per user)
  2. CreatePrCard — targets the shared /submissions/ realm (bottleneck)
  3. PatchCardInstance — targets the user's realm (already unique per user)

Job 2 is the problem: every user's PrCard creation shares command:${submissionRealmURL}, so they all serialize. With a 60s timeout per job and N concurrent submissions, the Nth user waits up to N×60s.

Solution

Override the concurrency group only for Job 2 using the listing's branch name:

command:${submissionRealm}:listing:${branchName}

Jobs 1 and 3 keep the default command:${realmURL} — they already target per-user realms so there's no cross-user contention.

The override is applied in createAndLinkPrCard because that's the earliest point where we have branchName (returned by the GitHub PR creation step). At the enqueueRunCommandJob level, we only have generic RunCommandArgs with no domain-specific context — so the caller decides when and how to override.

Job Realm Concurrency group Cross-user behavior
1 - CreateSubmission User's realm command:{userRealm} (default) Already parallel
2 - CreatePrCard Shared submission realm command:{submissionRealm}:listing:{branch} Was blocked, now parallel
3 - PatchCard User's realm command:{userRealm} (default) Already parallel

Test plan

  • All 13 bot-runner tests pass
  • New assertions verify Job 2 uses listing-scoped group while Jobs 1 and 3 use default
  • Deploy to staging and test concurrent listing submissions from different users
  • Verify same-listing resubmission still serializes correctly

@richardhjtan richardhjtan requested review from a team March 26, 2026 07:52
@github-actions
Copy link

github-actions bot commented Mar 26, 2026

Host Test Results

    1 files  ±0      1 suites  ±0   2h 10m 26s ⏱️ -1s
2 051 tests ±0  2 036 ✅ ±0  15 💤 ±0  0 ❌ ±0 
2 066 runs  ±0  2 051 ✅ ±0  15 💤 ±0  0 ❌ ±0 

Results for commit bb2ecb8. ± Comparison against base commit 724fc8b.

♻️ This comment has been updated with latest results.

@richardhjtan richardhjtan changed the title Use room-scoped concurrency group for submission queue jobs Use listing-scoped concurrency group for PrCard creation on shared submission realm Mar 26, 2026
@richardhjtan richardhjtan force-pushed the CS-10513-concurrency-group-of-submission-works branch from 58acd0e to bb2ecb8 Compare March 26, 2026 09:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a job-queue bottleneck in the submission flow by preventing all CreatePrCard (run-command) jobs targeting the shared /submissions/ realm from serializing under a single concurrency group. It introduces a caller-controlled concurrency group override so CreatePrCard jobs can be scoped by listing branch name, enabling parallel processing across different listings while still serializing same-listing operations.

Changes:

  • Add an optional concurrencyGroup override parameter to enqueueRunCommandJob.
  • Use a listing-scoped concurrency group for the create-pr-card job in the bot-runner submission flow.
  • Extend bot-runner tests to assert the intended concurrency-group behavior across the three-job sequence.

Reviewed changes

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

File Description
packages/runtime-common/jobs/run-command.ts Adds optional concurrency-group override for published run-command jobs.
packages/bot-runner/lib/command-runner.ts Applies listing-scoped concurrency grouping for PR card creation in the shared submissions realm.
packages/bot-runner/tests/command-runner-test.ts Adds assertions verifying default vs listing-scoped concurrency groups across the submission flow jobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

this.queuePublisher,
this.dbAdapter,
userInitiatedPriority,
concurrencyGroup ? { concurrencyGroup } : undefined,
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

enqueueRunCommand drops the override when concurrencyGroup is an empty string because it uses a truthiness check (concurrencyGroup ? ...). Since the parameter type is string | undefined, an empty string is a valid value and should be forwarded (or rejected via validation). Consider switching to an explicit concurrencyGroup !== undefined check (and, if supported, forwarding null as well) so the function doesn’t silently fall back to the default group.

Suggested change
concurrencyGroup ? { concurrencyGroup } : undefined,
concurrencyGroup !== undefined ? { concurrencyGroup } : undefined,

Copilot uses AI. Check for mistakes.
Comment on lines 7 to 17
export async function enqueueRunCommandJob(
args: RunCommandArgs,
queue: QueuePublisher,
_dbAdapter: DBAdapter,
priority: number,
opts?: { concurrencyGroup?: string },
) {
let job = await queue.publish<RunCommandResponse>({
jobType: 'run-command',
concurrencyGroup: `command:${args.realmURL}`,
concurrencyGroup: opts?.concurrencyGroup ?? `command:${args.realmURL}`,
timeout: RUN_COMMAND_JOB_TIMEOUT_SEC,
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

enqueueRunCommandJob now supports a concurrency-group override, but the override type only allows string. The queue API supports concurrencyGroup: string | null, and allowing null here would let callers explicitly opt out of grouping (full parallelism) when needed. Consider widening opts.concurrencyGroup to string | null and passing it through as-is (treating undefined as “use default”).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants