Skip to content

[fix](ci) deduplicate comment-triggered TeamCity builds - #66194

Draft
hello-stephen wants to merge 1 commit into
masterfrom
agent/fix-comment-trigger-queue
Draft

[fix](ci) deduplicate comment-triggered TeamCity builds#66194
hello-stephen wants to merge 1 commit into
masterfrom
agent/fix-comment-trigger-queue

Conversation

@hello-stephen

Copy link
Copy Markdown
Contributor

What changed

  • Narrow the issue_comment job filter to supported TeamCity commands instead of matching every comment containing run.
  • Serialize the same command for one PR so duplicate comments do not race each other.
  • Before canceling or triggering a TeamCity pipeline, check queued and running builds for the same PR, pipeline, and revision.
  • Skip only an exact active duplicate. Preserve the existing cancel-old-revision and trigger-new-revision behavior.
  • Fail open when the duplicate lookup is unavailable so a legitimate CI request is not blocked.

Why

Performance result comments such as Total hot run time matched the generic contains(..., 'run') condition and unnecessarily consumed GitHub-hosted runners. Repeated run buildall comments for the same PR revision could also cancel and recreate identical TeamCity builds.

This reduces false GitHub Actions queue pressure and avoids duplicate TeamCity fan-out without changing valid command semantics.

Validation

  • actionlint .github/workflows/comment-to-trigger-teamcity.yml
  • bash -n regression-test/pipeline/common/teamcity-utils.sh
  • Filter scenarios covering performance reports, run buildall, numbered pipeline runs, and skip commands
  • Mocked TeamCity scenarios covering exact-revision dedupe, new-revision trigger flow, and fail-open lookup errors
  • Read-only verification against active PR [WIP][feature](fe) Keep row binlog tablets colocated with base tablets #66191 builds: FEUT 1008222, BEUT 1008223, CloudUT 1008224, and Compile 1008225 all matched the same PR HEAD revision

@hello-stephen

Copy link
Copy Markdown
Contributor Author

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@hello-stephen

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot 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.

Requesting changes for six correctness issues in the new deduplication path.

  • The raw admission/concurrency grammar disagrees with the established parser, so valid whitespace-normalized commands can be dropped and parser-invalid case variants can displace valid pending requests.
  • Concurrency groups do not cover every workflow run that mutates the same concrete TeamCity pipeline.
  • A current-revision match returns before stale revisions are cleaned up.
  • Repeat count is omitted from request identity, so semantically different runs are skipped.
  • TeamCity pagination is ignored, allowing later-page duplicates to be missed.
  • buildall is tracked only through Compile, not through the downstream fan-out it starts.

Critical checkpoints:

  • Goal and tests: The exact-spelling filter removes the reported generic run false positive, but the deduplication goal is not achieved for the races and lifecycle cases above, and valid command semantics regress. The PR changes no test files; the description reports actionlint, shell syntax, filter, mock, and live checks, but no reusable automated fixtures are present. In this review-only runner, bash -n regression-test/pipeline/common/teamcity-utils.sh passed; builds were prohibited and actionlint was unavailable.
  • Scope and clarity: The two-file scope is focused, but raw admission, GitHub scheduling, TeamCity lookup, cancellation, and fan-out do not share one request model.
  • Concurrency: Applicable and blocking. GitHub groups do not serialize all concrete pipeline mutations, default pending replacement can lose a valid request, and lookup/trigger remains a non-atomic remote sequence.
  • Lifecycle and parallel paths: Applicable and blocking. Mixed old/current revisions and Compile-versus-descendant lifetimes are not handled; direct commands and buildall are parallel paths to the same pipelines.
  • Error handling and observability: HTTP/JSON lookup failures correctly fail open with warnings, and duplicate logs include useful identifiers. Pagination is still an unhandled successful-response case.
  • Compatibility/configuration/persistence/data correctness: No Doris runtime configuration, storage/protocol, transaction, persistence, data-write, FE/BE variable, or static-lifecycle change is involved.
  • Performance: The intended duplicate suppression would reduce CI load, but the uncovered races, fan-out window, and later-page miss still allow redundant expensive builds.
  • Security: The changed files are CI/dev surfaces outside the Doris runtime vulnerability model. No newly introduced runtime security finding is claimed.
  • User focus: No additional user-provided focus was supplied; the whole PR was reviewed.

The review completed the required bounded multi-agent convergence against this frozen comment set, and every candidate was accepted, merged, or dismissed with evidence.

${{
github.event.issue.pull_request &&
(
contains(github.event.comment.body, 'run buildall') ||

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.

[P1] Use one canonical command grammar before entering concurrency

This admission test is not equivalent to the parser below: it examines the raw body with case-insensitive contains, while lines 91/179 normalize whitespace and then shell-match case-sensitively. Thus run p0 (or a line break between the words) previously normalizes to valid run p0 but is now skipped; conversely RUN P0 is admitted and assigned the p0 group, then the parser no-ops. Because the default concurrency queue keeps only one pending job, that no-op can replace a legitimate pending p0 request. Please make admission, grouping, and dispatch use the same canonical command and preserve distinct pending requests.

}}

# Serialize duplicate commands for one PR. The next run then sees the build
# queued by the previous run and can safely skip an identical PR revision.

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.

[P1] Coordinate buildall through the complete fan-out

The helper and GitHub lock cover only Compile even though this workflow skips the downstream steps because Compile triggers them. This fails in both directions: for PR 66196, Compile 1008441 finished while same-revision P0 1008463, External 1008464, Cloud P0 1008465, and NonConcurrent 1008467 were still running, so another buildall starts a new root; while Compile is still active, a direct run p0 can queue P0 before Compile later enqueues the same P0 entirely inside TeamCity, bypassing this helper and every GitHub group. Please enforce exact-request coordination at the TeamCity fan-out boundary, or route both direct and fan-out requests through one atomic coordinator for the full descendant lifetime.

concurrency:
group: >-
comment-to-trigger-teamcity-${{ github.event.issue.number }}-${{
contains(github.event.comment.body, 'run buildall') && 'buildall' ||

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.

[P1] Serialize mutations of the same concrete pipeline

This key does not cover every run that executes the same check-then-trigger operation. run buildall is placed in the buildall group but later calls the helper for FEUT, BEUT, CloudUT, Compile, and Performance, while each direct command uses a different group. Also, run feut run beut is grouped as beut by this fixed precedence but the parser executes the first textual command, feut. Two such jobs can both observe no active FEUT build and then both enqueue it. Please lock by the concrete PR/pipeline mutation (or use a PR-wide/TeamCity-side atomic lock) and derive the key from the same canonical command used for dispatch.

curl -sSf -X GET \
-u OneMoreChance:OneMoreChance \
-H "Accept: application/json" \
"${teamcity_rest_url}/buildQueue?locator=buildType:(id:${PIPELINE})&fields=build(id,branchName,revisions(revision(version)))"

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.

[P2] Follow TeamCity pagination when looking for duplicates

TeamCity collection responses may be partial and advertise the next page through nextHref, but this projection does not request that field and the function performs only one request. Since the queue locator can filter by build type but not by PR branch/revision, a busy pipeline can place the exact duplicate on a later page; this returns “none”, the single-page cancel path misses it too, and line 378 queues another copy. Please follow nextHref (for queued and running collections) until a match is found or the collection is exhausted, with a multi-page fixture.

jq -s -r \
--arg branch "pull/${PULL_REQUEST_NUM}" \
--arg revision "${COMMIT_ID_FROM_TRIGGER}" \
'.[] | .build[]? |

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.

[P1] Include repeat count in the duplicate identity

Repeat count changes the request: the parser preserves it and trigger_build sends it as env.repeat_times_from_trigger, which the regression pipelines use for their iteration count and timeout. This selector compares only PR branch and revision, so while run cloud_p0 is active, a subsequent run cloud_p0 3 on the same head is reported as a duplicate and the requested three passes are never scheduled. Please normalize the empty count to 1 and compare the active build's repeat property as part of duplicate equivalence, while keeping all requests for one PR/pipeline in the same serialization domain.

) || duplicate_lookup_status=$?
if [[ ${duplicate_lookup_status} -eq 0 ]]; then
echo "INFO: active build(s) ${duplicate_build_ids//$'\n'/,} already exist for PR ${PULL_REQUEST_NUM}, pipeline ${COMMENT_TRIGGER_TYPE}, revision ${COMMIT_ID_FROM_TRIGGER}; skip duplicate trigger"
return 0

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.

[P1] Still clean up stale revisions before returning

get_active_builds_of_revision reports only IDs matching the requested revision, so this return says nothing about other active builds for the same PR/pipeline. If a current-revision build is queued while an older revision is still running or queued (for example after an external trigger or a failed/asynchronous cancellation), this branch bypasses both cleanup calls below and leaves the stale build alive. That contradicts the stated preservation of cancel-old-revision behavior. Please classify the full active set, cancel every non-current build, and skip only the new trigger when a current build remains.

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.

1 participant