feat: parallel issue sync with worker threads#132
Conversation
SummaryThis PR adds Key changes:
What reviewers should knowWhere to start:
Non-obvious decisions:
Gotchas to watch:
|
Spawn 20 worker_threads (eval: true, SEA-compatible) for issue metadata sync when matched pairs >= 500. Each worker runs 5 concurrent API calls using built-in https/http modules, achieving 100 total concurrent requests vs 20 with single-process mapConcurrent. Tested on Angular Framework (5,119 issues): 99.8% sync success rate, ~20 min total. Includes exponential backoff with jitter (6 retries), 5xx/transient error retry, and apiErrors tracking for visibility into silent failures.
6528b41 to
dd815f8
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit dd815f8. Configure here.
| } catch { | ||
| stats.failed++; | ||
| } | ||
| } |
There was a problem hiding this comment.
Duplicated sync logic risks divergence from originals
Low Severity
The WORKER_CODE string reimplements ~130 lines of business logic (mapChangelogDiffToTransition, extractTransitionsFromChangelog, getFallbackTransition, syncIssueStatus, syncIssueAssignment, syncIssueComments, syncIssueTags, addSourceLink, syncOneIssue) that already exist as separate helper files in issue-sync/helpers/. Future bug fixes to the originals (e.g., adding a new transition case) won't automatically propagate to this inline copy, creating silent behavioral drift between the parallel and non-parallel sync paths.
Reviewed by Cursor Bugbot for commit dd815f8. Configure here.
|
|
||
| // -------- Parallel Issue Sync -------- | ||
|
|
||
| const WORKER_CODE = ` |
There was a problem hiding this comment.
Logic duplication: WORKER_CODE reimplements the full issue-sync pipeline — mapChangelogDiffToTransition, extractTransitionsFromChangelog, getFallbackTransition, syncIssueStatus, syncIssueAssignment, syncIssueComments, syncIssueTags, addSourceLink, and syncOneIssue — duplicating the same-named helpers that the sequential path calls from the rest of the codebase.
This is the dominant code path (≥500 pairs = effectively all large migrations), so any bug fix or behaviour change made to the main-process helpers will silently diverge from the parallel path. The Angular Framework run (5,119 pairs) only ever executes the worker copy, not the tested one.
The constraint is real (SEA eval: true workers can't require() bundled modules), but the duplication should be made explicit:
-
Add a prominent comment at the top of
WORKER_CODEstating it mirrors the functions inissue-sync/helpers/and listing which files are mirrored. -
Consider a build-time step (e.g., an
npm run check:worker-syncscript) that asserts the worker string and the source helpers produce identical results for a fixed set of inputs, so drift is caught in CI rather than in production. -
Mark as noise
| }); | ||
| }); | ||
|
|
||
| const allStats = await Promise.all(workerPromises); |
There was a problem hiding this comment.
Bug: Promise.all fails fast — if any single worker crashes (OOM, unexpected exception escaping run().catch(), non-zero exit), the entire parallelSyncIssues() call rejects and all progress from the other 19 workers is discarded. The caller (syncIssues) then surfaces an unhandled exception rather than a partial stats object.
This is the opposite of the sequential path's { settled: true } semantics, which absorbs per-item errors and always returns an aggregated stats object. For a 31K-issue run spread across 20 workers, losing 19 healthy workers' results because one worker OOMs is a significant production risk.
Fix: use Promise.allSettled and merge only the fulfilled results, logging a warning for any rejected workers:
const results = await Promise.allSettled(workerPromises);
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) {
logger.warn(`${failed.length} worker(s) failed: ${failed.map(r => r.reason?.message).join('; ')}`);
}
const allStats = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const merged = mergeStats(allStats);| const allStats = await Promise.all(workerPromises); | |
| const results = await Promise.allSettled(workerPromises); | |
| const failedWorkers = results.filter(r => r.status === 'rejected'); | |
| if (failedWorkers.length > 0) { | |
| logger.warn(`Parallel issue sync: ${failedWorkers.length} worker(s) failed — their issues may be incomplete. Errors: ${failedWorkers.map(r => r.reason?.message).join('; ')}`); | |
| } | |
| const allStats = results.filter(r => r.status === 'fulfilled').map(r => r.value); | |
| const merged = mergeStats(allStats); |
- Mark as noise


Summary
worker_threads-based parallel issue sync for large projects (≥500 matched pairs)mapConcurrent)eval: truewith inline code using only Node.js built-inhttps/httpmodulesapiErrorsstat trackingmapConcurrentpath for <500 matched pairsTest plan
npm run package) — passestransferon Angular Framework (31K+ SQ issues, 5,119 pre-filtered with manual changes)verifycommand for full cross-system validation🤖 Generated with Claude Code
Note
Medium Risk
Changes the behavior and concurrency model of SonarCloud issue write operations (new worker-thread path, inline
eval, retries/backoff), which could affect rate limiting and sync completeness if edge cases differ from the existingmapConcurrentflow.Overview
Enables parallel issue metadata sync for large projects by switching
syncIssues(sq-2025 pipeline) to dispatch matched issue pairs to a newparallelSyncIssues()implementation once a 500-pair threshold is reached.Introduces
src/shared/utils/concurrency/helpers/parallel-issue-sync.js, which spawnsworker_threads(inlineevalcode) to execute issue transitions/assignments/comments/tags/source-linking with per-worker concurrency and built-in retry/backoff for 429/5xx/transient errors, then aggregates per-worker stats (including newapiErrors) and feeds progress back to the parent.Exports
parallelSyncIssuesfrom the shared concurrency index and updates docs/changelog to describe the new worker-thread sync path and its tuning knobs.Reviewed by Cursor Bugbot for commit dd815f8. Bugbot is set up for automated code reviews on this repo. Configure here.