Skip to content

[Backport stable-26-2] PR #49452: [issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext - #50489

Open
ydbot wants to merge 2 commits into
stable-26-2from
cherry-pick-stable-26-2-260819-115457
Open

[Backport stable-26-2] PR #49452: [issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext#50489
ydbot wants to merge 2 commits into
stable-26-2from
cherry-pick-stable-26-2-260819-115457

Conversation

@ydbot

@ydbot ydbot commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Changelog entry

[issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext

Description for reviewers

Original PR(s)

Metadata

Conflicts Require Manual Resolution

This PR contains merge conflicts that require manual resolution.

Files with conflicts:

How to resolve conflicts:

git fetch origin
git checkout --track origin/cherry-pick-stable-26-2-260819-115457
# Resolve conflicts in files
git add .
git commit -m "Resolved merge conflicts"
git push

After resolving conflicts:

  1. Fix the PR title (remove [CONFLICT] if conflicts are resolved)
  2. Mark PR as ready for review

Git Cherry-Pick Log

=== Cherry-picking 709ca02 ===
Auto-merging ydb/core/tx/columnshard/engines/reader/common_reader/iterator/fetching.cpp
Auto-merging ydb/core/tx/columnshard/engines/reader/common_reader/iterator/source.cpp
Auto-merging ydb/core/tx/columnshard/engines/reader/common_reader/iterator/source.h
CONFLICT (modify/delete): ydb/tests/stability/tests/tsan.supp deleted in HEAD and modified in 709ca02856d ([issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext (#49452)).  Version 709ca02856d ([issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext (#49452)) of ydb/tests/stability/tests/tsan.supp left in tree.

error: could not apply 709ca02856d... [issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext (#49452)
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".

PR was created by cherry-pick workflow run

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

2026-08-19 11:56:39 UTC Pre-commit check linux-x86_64-relwithdebinfo for 2c1a098 has started.
2026-08-19 11:56:58 UTC Artifacts will be uploaded here
2026-08-19 11:58:23 UTC ya make is running...
2026-08-19 12:15:03 UTC Check cancelled

@ydbot

ydbot commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

Run Extra Tests

Run additional tests for this PR. You can customize:

  • Test Size: small, medium, large (default: small, medium)
  • Test Targets: any directory path (default: ydb/)
  • Sanitizers: ASAN, MSAN, TSAN
  • Coredumps: enable for debugging (default: off)
  • Additional args: custom ya make arguments

▶  Run tests

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

2026-08-19 11:59:32 UTC Pre-commit check linux-x86_64-release-asan for 2c1a098 has started.
2026-08-19 11:59:52 UTC Artifacts will be uploaded here
2026-08-19 12:01:09 UTC ya make is running...
2026-08-19 12:14:39 UTC Check cancelled

@lberserq lberserq changed the title [CONFLICT] [Backport stable-26-2] PR #49452: [issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext [Backport stable-26-2] PR #49452: [issue-#49169] (-) Make prev-node tracing state lock-free in TExecutionContext Aug 19, 2026
@lberserq
lberserq marked this pull request as ready for review August 19, 2026 12:14
@lberserq

Copy link
Copy Markdown
Contributor

Conflict resolved: dropped the ydb/tests/stability/tests/tsan.supp hunk. That file does not exist on this stable branch (it is main-only and unreferenced by ydb/tests/stability/tests/ya.make), and the original PR only removed the now-obsolete race:SetPrevCategoryName suppression from it — there is nothing to backport.

The C++ part of the backport is byte-identical to the original commit 709ca02; all APIs it relies on (TCompiledGraph::GetNodes, IResourceProcessor::GetSignalCategoryName, TFetchingScriptCursor::GetPrevName returning by value) are unchanged on this branch.

@lberserq
lberserq requested a review from a team as a code owner August 19, 2026 12:14
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

2026-08-19 12:17:41 UTC Pre-commit check linux-x86_64-release-asan for 5fae958 has started.
2026-08-19 12:18:00 UTC Artifacts will be uploaded here
2026-08-19 12:19:17 UTC ya make is running...
🟡 2026-08-19 14:08:17 UTC Some tests failed, follow the links below. This fail is not in blocking policy yet

Ya make output | Test bloat

TESTS PASSED ERRORS FAILED SKIPPED MUTED?
19487 19443 0 26 15 3

🟢 2026-08-19 14:08:26 UTC Build successful.
🟢 2026-08-19 14:08:55 UTC ydbd size 4.1 GiB changed* by +11.2 KiB, which is < 100.0 KiB vs stable-26-2: OK

ydbd size dash stable-26-2: 950195e merge: 5fae958 diff diff %
ydbd size 4 409 097 488 Bytes 4 409 108 968 Bytes +11.2 KiB +0.000%
ydbd stripped size 1 655 598 688 Bytes 1 655 605 600 Bytes +6.8 KiB +0.000%

*please be aware that the difference is based on comparing your commit and the last completed build from the post-commit, check comparation

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

2026-08-19 12:19:10 UTC Pre-commit check linux-x86_64-relwithdebinfo for 5fae958 has started.
2026-08-19 12:19:57 UTC Artifacts will be uploaded here
2026-08-19 12:21:56 UTC ya make is running...
🟡 2026-08-19 14:43:00 UTC Some tests failed, follow the links below. Going to retry failed tests...

Details

Ya make output | Test bloat

TESTS PASSED ERRORS FAILED SKIPPED MUTED?
50698 47240 0 2 3443 13

2026-08-19 14:43:16 UTC ya make is running... (failed tests rerun, try 2)
🟢 2026-08-19 14:45:16 UTC Tests successful.

Ya make output | Test bloat | Test bloat

TESTS PASSED ERRORS FAILED SKIPPED MUTED?
251 (only retried tests) 247 0 0 0 4

🟢 2026-08-19 14:45:19 UTC Build successful.
🟢 2026-08-19 14:45:37 UTC ydbd size 2.5 GiB changed* by +5.1 KiB, which is < 100.0 KiB vs stable-26-2: OK

ydbd size dash stable-26-2: 950195e merge: 5fae958 diff diff %
ydbd size 2 705 331 936 Bytes 2 705 337 168 Bytes +5.1 KiB +0.000%
ydbd stripped size 570 920 856 Bytes 570 923 480 Bytes +2.6 KiB +0.000%

*please be aware that the difference is based on comparing your commit and the last completed build from the post-commit, check comparation

@robot-vibe-db

robot-vibe-db Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

AI Review Summary

Verdict: ✅ No critical issues found

Critical issues

No critical issues found.

Other findings

  • Minor | Medium: Stop() does not clear the Program shared_ptr, creating a lifetime asymmetry with ProgramIterator/ExecutionVisitor -- source.h:62 / source.cpp:36-37
  • Minor | Low: RenderCategoryName and GetPrevCategoryName read the non-atomic StartCategoryName and Program fields; safety relies on the ordering guarantee that Start() fully completes before any Execute(), which is true but not enforced by the atomic protocol itself -- source.h:67,109
  • Nit | High: SetStartCategoryName takes TString&& but step.GetPrevName() returns a value that may be an lvalue reference; the call site SetStartCategoryName(step.GetPrevName()) works because GetPrevName() returns TString by value, but the rvalue-only API is fragile if callers change -- source.h:76

Detailed analysis

The PR correctly addresses the SIGSEGV crash (issue #49169) caused by concurrent TString assignment in the prev-node tracing state. The fix replaces two mutable TString fields (PrevCategoryName and PrevExecutionResult) with a single std::atomic<TPrevNodeState> struct, which is verified at compile time to be lock-free via static_assert.

Correctness of the atomic approach:

  • The TPrevNodeState struct is 8 bytes (ui32 + ui8 + bool + bool + padding), confirmed lock-free.
  • The EExecutionResult enum is changed to : ui8 underlying type to keep the struct within 8 bytes -- this is necessary and correct.
  • The std::memory_order_release / std::memory_order_acquire pairing is correct for the producer-consumer pattern.
  • The single atomic load in GetPrevNodeTracing() ensures CategoryName and ExecutionResult are always consistent with each other, which was not guaranteed with the old two-field design.

Behavioral equivalence:

  • The initial state (Defined=false) correctly maps to StartCategoryName (equivalent to old PrevCategoryName after Start()) and empty ExecutionResult string (equivalent to old default-constructed PrevExecutionResult).
  • Moving SetPrevNodeTracing from inside ReportTracing to after it in DoExecuteInplace is safe: the read (GetPrevNodeTracing) in ReportTracing uses the previous iteration's value, and the write happens after reporting, exactly matching the old semantics.
  • The removal of the update in the early-return path of ReportTracing (when no probes are enabled) is correct because SetPrevNodeTracing is now always called unconditionally after ReportTracing returns.

Callers of GetPrevCategoryName outside the execution loop:

  • OnEmptyStageData and BuildStageResult call GetPrevCategoryName() which uses RenderCategoryName. Since Program is set once in Start() and never cleared (not even in Stop()), the node lookup remains valid. When Defined=false (no execution happened), it returns StartCategoryName, which is correct.

Remaining NO_SANITIZE_THREAD:

  • DoExecuteInplace retains NO_SANITIZE_THREAD, along with several methods on IDataSource (AddExecutionDuration, AddBytesRead, GetTotalDuration, etc.). These are pre-existing suppressions unrelated to this fix.

The backport is clean; the conflict with tsan.supp (absent on stable-26-2) was correctly resolved by dropping the hunk.


This review was generated automatically. Critical issues require attention; other findings are advisory.
If this comment was useful, please give it a 👍 — it helps us improve the review bot.

@robot-vibe-db robot-vibe-db 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.

Inline review comments on specific lines.

std::atomic<TPrevNodeState> PrevNode = {};

TString RenderCategoryName(const TPrevNodeState& state) const {
if (!state.Defined) {

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.

Severity: Minor
Confidence: Medium

Stop() resets ProgramIterator and ExecutionVisitor but does not reset Program. This is intentionally needed so that RenderCategoryName can still resolve node IDs after execution stops (e.g., in OnEmptyStageData / BuildStageResult). However, this creates a lifetime asymmetry: Program outlives the rest of the execution state, and a second call to Start() would hit AFL_VERIFY(!Program).

Consider adding a brief comment in Stop() explaining why Program is deliberately kept alive, to prevent future maintainers from "cleaning it up" and breaking GetPrevCategoryName() post-Stop calls.

}
AFL_VERIFY(Program);
auto it = Program->GetNodes().find(state.NodeId);
AFL_VERIFY(it != Program->GetNodes().end())("node_id", state.NodeId);

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.

Severity: Minor
Confidence: Low

RenderCategoryName reads the non-atomic StartCategoryName field. This is safe because SetStartCategoryName is only called once during Start(), which completes before any Execute() can create a concurrent continuation. However, the safety invariant (that StartCategoryName is fully written before any concurrent read) is implicit -- it depends on the call order in DoExecuteInplace and is not enforced by the atomic protocol itself.

This is acceptable for the current code structure but worth noting as a maintenance concern.

StartCategoryName = std::move(name);
}

void SetPrevNodeTracing(const ui32 nodeId, const TConclusion<NArrow::NSSA::IResourceProcessor::EExecutionResult>& conclusion) {

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.

Severity: Nit
Confidence: High

SetStartCategoryName accepts only TString&& (rvalue reference). The call site SetStartCategoryName(step.GetPrevName()) works because GetPrevName() returns TString by value, but this rvalue-only signature is fragile -- if a future caller needs to pass an lvalue TString, they would need to add std::move() or a copy. Consider accepting const TString& with an internal copy, or provide both overloads, for a more conventional API.

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.

2 participants