Temporary workaround: do not probe object-storage listing keys as JSON - #2393
Merged
filimonov merged 1 commit intoSep 17, 2026
Merged
Conversation
`RelativePathWithMetadata(String)` builds a `CommandInTaskResponse` for every
string it receives, which includes every key returned by `listObjects` /
`iterate` of every object storage, not only the `{"retry_after_us": N}`
answers of `StorageObjectStorageStableTaskDistributor` it exists for. Each
ordinary key was parsed with `Poco::JSON::Parser` and threw one caught
`JSONException`.
Under ASan this is a leak: the fake stack frame of
`Poco::JSON::ParserImpl::parseImpl` that exits by exception is never
collected (it is re-entered at the same stack depth and `FakeStack::GC`
frees only frames strictly below the next allocation). One leaked frame per
listed key per thread; once the size class holds 8192 frames every
`__asan_stack_malloc_1` scans all of them and every small function on that
thread runs ~100x slower. On the `amd_asan_ubsan, cas s3 storage` lane,
where CAS lists constantly, 27.7% of all CPU samples of a run sit in that
scan and `02435_rollback_cancelled_queries` sorts 1M rows in 200 s instead
of 2 s.
Only strings that start with `{` after whitespace are parsed now; object
keys never do, the distributor answer always does. The proper fix is to stop
sending the command in the path field (a separate `ObjectInfo` kind or the
versioned cluster-function protocol, the direction of
#1360), after which this probe
can be removed.
Related: #2362
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ilejn
approved these changes
Sep 16, 2026
ilejn
left a comment
Collaborator
There was a problem hiding this comment.
Don't get ASAN part of the problem.
As a workaround is fine anyway.
Member
Author
|
CI on this PR (run 35120487077, ASan CAS-S3 lane 2/2): For scale, in the same run: ASan without S3 = 66 s, release binary with CAS-S3 = 58 s, release binary without S3 = 27 s. The remaining ASan CAS-S3 / ASan ratio (2.2x) matches the S3 / no-S3 ratio of the non-sanitized builds, so what is left is S3 insert cost, not the fake-stack scan. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RelativePathWithMetadata(String)builds aCommandInTaskResponsefor every string it receives, which includes every key returned bylistObjects/iterateof every object storage, not only the{"retry_after_us": N}answers ofStorageObjectStorageStableTaskDistributorit exists for. Each ordinary key was parsed withPoco::JSON::Parserand threw one caughtJSONException.Under ASan this is a leak: the fake stack frame of
Poco::JSON::ParserImpl::parseImplthat exits by exception is never collected (it is re-entered at the same stack depth andFakeStack::GCfrees only frames strictly below the next allocation). One leaked frame per listed key per thread; once the size class holds 8192 frames every__asan_stack_malloc_1scans all of them and every small function on that thread runs ~100x slower. On theamd_asan_ubsan, cas s3 storagelane, where CAS lists constantly, 27.7% of all CPU samples of a run sit in that scan and02435_rollback_cancelled_queriessorts 1M rows in 200 s instead of 2 s. Full analysis with the CI numbers, the live-server fake-stack dump and a 40-line standalone reproduction is in the linked issue.This is a temporary workaround: only strings that start with
{after whitespace are parsed now. Object keys never do, the distributor answer always does, so the swarm protocol is unchanged. The proper fix is to stop sending the command in the path field (a separateObjectInfokind or the versioned cluster-function protocol, the direction of #1360), after which this probe can be removed.Related: #2362
Related: #1360
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Object-storage listings no longer try to parse every listed key as a JSON swarm command; this removes one thrown-and-caught exception per listed key on every object storage and the ASan fake-stack leak that made the CAS-S3 sanitizer lane up to 100x slower.
Documentation entry for user-facing changes
CI/CD Options
Exclude tests:
Regression jobs to run:
🤖 Generated with Claude Code