Repository navigation
Apply logs timeout and count globally across concurrent targets - #60058
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
✅ Ponytail Reviewer completed successfully! Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "ab.chatgpt.com"See Network Configuration for more information.
|
|
Warning Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding. What happenedThe threat detection engine failed to produce results. Review the workflow run logs for details.
|
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff
|
|
|
🧠 Matt Pocock Skills Reviewer failed during the skills-based review. Warning Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding. What happenedThe threat detection engine failed to produce results. Review the workflow run logs for details.
|
|
@copilot make the sure cancelation context is propagated correctly through all the layers and workers |
There was a problem hiding this comment.
Ponytail review: a few opportunities to trim wrapper and duplicate-limit plumbing without changing behavior.
net: -12 lines possible.
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
ab.chatgpt.com
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
network:
allowed:
- defaults
- "ab.chatgpt.com"See Network Configuration for more information.
Generated by ✂️ Ponytail Reviewer for #60058 · codex · gpt53codex · 3.13 AIC · ⌖ 4.12 AIC · ⊞ 12.8K
Comment /ponytail to run again
| return shouldStopAfterWorkflowRunBatch(batch, opts.Verbose), nil | ||
| } | ||
|
|
||
| func markSharedLogsCountReached(state *logsCollectionState, fetchAllInRange bool, limit *logsCountLimit) bool { |
There was a problem hiding this comment.
L340: shrink: markSharedLogsCountReached only wraps limit.isReached() and assignment. Inline the two lines at call sites; delete helper.
There was a problem hiding this comment.
Keeping markSharedLogsCountReached as a helper: it's called from two sites (collectProcessedWorkflowRuns and fetchAndProcessLogsBatch/finishLogsBatch), so inlining would duplicate the isReached()/assignment logic rather than simplify it.
| if len(processedRuns) >= opts.count { | ||
| continue | ||
| } | ||
| if !opts.countLimit.tryAdd() { |
There was a problem hiding this comment.
L582: shrink: double-guard (len(processedRuns) < opts.count and countLimit.tryAdd) duplicates budget checks. Keep shared countLimit.tryAdd gate only; remove local length check.
There was a problem hiding this comment.
Not removing the local length check: opts.countLimit is nil in single-target mode (only set via newLogsCountLimit in the multi-target path), and countLimit.tryAdd() returns true unconditionally for a nil receiver. The len(processedRuns) < opts.count guard is the only enforcement of --count for single-target downloads, so dropping it would break that case.
| rateLimitFirstRequest bool | ||
| maxConcurrentDownloads int | ||
| storageLimit *logsStorageLimit | ||
| countLimit *logsCountLimit |
There was a problem hiding this comment.
L64: yagni: carrying both Count and countLimit in options creates parallel limit mechanisms. Keep one source of truth (countLimit) and derive Count at boundary only.
There was a problem hiding this comment.
Keeping both fields: Count is the public per-command option set for every download (including single-target, where countLimit stays nil), while countLimit is an internal cross-goroutine atomic budget only allocated for multi-target runs. They aren't parallel copies of the same limit — countLimit doesn't exist unless multiple targets share one budget.
🏗️ ADR required — draft added for PR #60058I enforced the design-decision gate for this PR because it adds more than 100 lines in business-logic directories ( Evidence used
OutcomeI added a draft ADR at:
Next actionPlease review and refine the draft ADR so the decision rationale and trade-offs match maintainer intent before merge.
|
There was a problem hiding this comment.
🟡 Changes recommended
Global selection and continuation handling have correctness gaps under concurrent limits.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Applies count and timeout limits globally across concurrent multi-target log downloads.
Changes:
- Adds shared deadline and atomic run-count budgeting.
- Preserves partial results and adds continuation handling.
- Updates tests, CLI help, and documentation.
File summaries
| File | Description |
|---|---|
pkg/cli/logs_orchestrator.go |
Preserves partial runs and expands continuation behavior. |
pkg/cli/logs_orchestrator_unit_test.go |
Tests zero-progress count continuations. |
pkg/cli/logs_orchestrator_types.go |
Adds shared count-limit state. |
pkg/cli/logs_orchestrator_download.go |
Enforces the shared count budget during collection. |
pkg/cli/logs_multi.go |
Coordinates global timeout, count, sorting, and partial results. |
pkg/cli/logs_multi_test.go |
Tests global limits and partial-result merging. |
pkg/cli/logs_command.go |
Clarifies global flag semantics. |
docs/src/content/docs/setup/cli.md |
Documents multi-target limits. |
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 3
- Review effort level: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| if !opts.countLimit.tryAdd() { | ||
| continue |
There was a problem hiding this comment.
This is a real trade-off, not a bug we're silently accepting: enforcing strict CreatedAt ordering across concurrent targets would require buffering/merging candidate metadata before any artifact download starts, which conflicts with the concurrent early-stop design this PR relies on to bound total work under the shared timeout. That trade-off (global admission-order fairness vs. concurrent early-stopping) is captured in docs/adr/60058-apply-global-limits-to-multi-target-logs.md for maintainer review. Not changing the admission strategy in this pass; happy to revisit if the ADR discussion lands on stricter ordering.
| allAPIRateLimits := startGitHubAPIRateLimitReports(activeCtx, logsTargetRateLimitHosts(targets)) | ||
| results := collectLogsTargets(activeCtx, opts, targets) |
There was a problem hiding this comment.
Fixed in b56d1da: a target still waiting on the semaphore when the shared context is canceled no longer returns a bare error. queuedLogsTargetResult builds a resumable continuation from the target's own options (preserving its cursor and timeout flag) instead. Covered by TestCollectLogsTargetsEmitsContinuationForQueuedTarget.
| if markSharedLogsCountReached(state, runtime.fetchAllInRange, opts.countLimit) { | ||
| return true, nil |
There was a problem hiding this comment.
Fixed in e572154: the allRunsConsumed cursor advance now runs before the shared count-limit check in fetchAndProcessLogsBatch (extracted into finishLogsBatch), so a continuation built after the shared budget is exhausted mid-batch resumes from the correct pagination boundary instead of re-scanning the batch. Added TestFetchAndProcessLogsBatchAdvancesCursorWhenSharedCountLimitReached to cover it.
|
@copilot run pr-finisher skill |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Fixed in 30ddcb5 (or the latest push): a target that is still waiting on the worker semaphore when the shared deadline/cancellation fires no longer returns a bare |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
🎉 This pull request is included in a new release. Release: |
Multi-target
logsdownloads applied--timeoutand--countindependently per target. These limits should bound the combined concurrent operation.Timeout
Count
Partial results
Documentation
This returns up to 100 runs combined and limits the overall download to 10 minutes.