Visitar URL original
Keep `blob_storage_log` connection info per fiber by kolsha · Pull Request #131273 · ClickHouse/ClickHouse · GitHub
Skip to content

Keep blob_storage_log connection info per fiber - #131273

Draft
kolsha wants to merge 1 commit into
masterfrom
silk-http-connection-info-per-fiber
Draft

kolsha wants to merge 1 commit into
masterfrom
silk-http-connection-info-per-fiber

Conversation

@kolsha

@kolsha kolsha commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

The connection info that pairs a system.blob_storage_log row with its HTTP connection is now kept per fiber instead of per thread, so requests made in Silk fibers log their own connection.

HTTPConnectionInfoScope hands the connection of a blob storage request to the code that logs it through thread_local state. A request made in a Silk fiber can resume on another thread in the middle of it, and the fibers that run on one scheduler thread interleave their scopes, so a row could get another request's connection or none.

  • The state is one FiberLocal object, like TracingContextOnThread (src/Common/OpenTelemetryTraceContext.cpp). Its slot is shared with the stackful coroutines of a thread, so threads and coroutines keep the previous behavior.
  • The header comment that justified the thread_local handoff now describes the fiber-local one.

No caller in this repository makes blob storage requests from fibers yet. The new gtest runs more fibers than scheduler threads; each publishes a connection inside its own scope and takes it back after all of them have published, which fails with per-thread state.

Changelog category (leave one):

  • Not for changelog (changelog entry is not required)

Workflow [PR]
Sync PR [sync-upstream/pr/131273]

`HTTPConnectionInfoScope` pairs a `system.blob_storage_log` row with the connection that carried its request through `thread_local` state. A request made in a Silk fiber can resume on another thread in the middle of it, and the fibers that run on one scheduler thread interleave their scopes, so a row could get another request's connection or none.

The state is now one `FiberLocal` object, like `TracingContextOnThread` (`src/Common/OpenTelemetryTraceContext.cpp`). Its slot is shared with the stackful coroutines of a thread, so threads and coroutines keep the previous behavior.

Test: more fibers than scheduler threads each publish a connection inside their own scope and take it back after all of them have published.
@clickhouse-gh

clickhouse-gh Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [c711e8c]


AI Review

Summary

Moves the HTTP connection attribution state used by system.blob_storage_log into one FiberLocal slot and adds a concurrent-fiber regression test. The test needs exception-safe cleanup: a scheduling exception can leave running fibers referencing destroyed locals, and a failed fiber can strand its peers at a barrier.

Missing context / blind spots
  • ⚠️ Reviewed PR head c711e8c6ce0fea8e7a294ab679644d2f15ebbece; all changed files match the checkout. Unit-test jobs are pending, and no configured local build or populated Silk submodule is available, so runtime behavior was assessed from the current source and pinned Silk implementation. Completed unit-test results would close this validation gap.
Findings

❌ Blockers

  • src/Common/tests/gtest_http_connection_info_in_fibers.cpp:63: Spawned fibers must finish before their referenced state is destroyed. Silk::spawn calls the throwing FiberLocalStorage::create. If a later spawn throws, both participant-release calls and the wait loop are skipped. silk::FiberFuture destruction does not join, so running fibers retain references to destroyed futures, results, and barriers. Separately, a first-access allocation failure in HTTPConnectionInfoScope is converted to EIO by FiberContext::main without dropping its barrier participation, so its peers wait indefinitely. Add an RAII release-and-join guard for scheduling exceptions and release peers when a spawned fiber fails before reaching a barrier.
Tests
  • ⚠️ Add a focused failure-injection check for a throwing spawn after at least one successful spawn and for a fiber failing before the publication barrier. Both paths must release peers and join started work before destroying shared state. No local tests were run.
Final Verdict

❌ Block — make the regression test's barrier and fiber cleanup safe on exceptional exits, then verify the failure paths and normal concurrent-fiber case.

@clickhouse-gh clickhouse-gh Bot added the pr-not-for-changelog This PR should not be mentioned in the changelog label Nov 13, 2026
@kolsha
kolsha requested a review from mstetsyuk November 13, 2026 11:45
@kolsha
kolsha marked this pull request as ready for review November 13, 2026 11:45
@kolsha
kolsha marked this pull request as draft November 13, 2026 11:53
std::vector<silk::FiberFuture> futures(fiber_count);

size_t spawned = 0;
while (spawned < fiber_count && Silk::spawn([&, i = spawned]

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.

[Blocker] Keep the shared test state alive when scheduling throws

Silk::spawn can throw: it calls FiberLocalStorage::create, which uses throwing new. If one spawn succeeds and a later one throws, execution skips both dropParticipants calls and the wait loop. silk::FiberFuture has no joining destructor, so futures, results, and both barriers are destroyed while successfully started fibers still reference them. This permits use-after-free or strands fibers at a barrier.

Please install an RAII cleanup guard after the shared locals that releases waiting participants and joins every successfully spawned fiber on exceptional exits too. Also account for a fiber failing before it reaches published: the new HTTPConnectionInfoScope can allocate and throw, and FiberContext::main returns EIO without dropping that participant, leaving the remaining fibers waiting indefinitely. A focused allocation-failure check after the first successful spawn should verify that these paths terminate safely.

@mstetsyuk mstetsyuk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, but each FiberLocal<...>::operator-> call is 4 extra instructions on ARM: get the underlying thread local storage base pointer, add the FiberLocalStorageThreadStorage + slot offset to that base, read the object pointer from the resulting pointer, check if it's null, dereference it. We can use FiberLocal<...>::get() once instead. Not sure if this place is hot enough, but IMO still worth doing as a simple optimisation, no complexity.

@clickhouse-gh clickhouse-gh Bot added the comp-object-storage Object storage connectivity (S3/GCS/Azure) including credentials, retries, multipart, etc. label Nov 13, 2026
@clickhouse-gh

clickhouse-gh Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Build profile diff (arm_release)

Comparing c711e8c6c with master c65bdd819 (stripped binary size, per-symbol sizes and ThinLTO time; compile times per translation unit against the most recent warmup build that recompiled it).

✅ No significant changes.

Binary sizes

programs/clickhouse-stripped: smaller than the master baseline by the known offset between the two builds, so the difference is not shown. A delta that differs from the offset by more than 50% of it is shown, in either direction.

The official master build is compiled with -g and a pull request build is not, and XRay counts debug instructions towards its instrumentation threshold, so master instruments thousands of functions more and its binary is ~0.4% larger no matter what the pull request does.

Slowest function optimization changes (ThinLTO)

Median per-function time ratio to the master baseline: programs/clickhouse ×0.59, programs/clickhouse-keeper ×0.64 (each binary is linked separately, on a different machine and with different build flags); deltas below are relative to it.

Binary Function Master PR Δ vs median
programs/clickhouse DB::registerFunctionConversion(DB::FunctionFactory&) 5.7 s 10.7 s +7.3 s (+214%)
Compile time of recompiled translation units

1636 translation units recompiled, 10430 s compile time in total, 1636 of them have a recent master baseline.

Job report

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp-object-storage Object storage connectivity (S3/GCS/Azure) including credentials, retries, multipart, etc. pr-not-for-changelog This PR should not be mentioned in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants