Skip to content

fix: keep the classification when a vortex cell load fails - #610

Open
xiaofan-luan wants to merge 1 commit into
milvus-io:mainfrom
xiaofan-luan:fix/no-stringified-status-throw
Open

fix: keep the classification when a vortex cell load fails#610
xiaofan-luan wants to merge 1 commit into
milvus-io:mainfrom
xiaofan-luan:fix/no-stringified-status-throw

Conversation

@xiaofan-luan

Copy link
Copy Markdown
Contributor

Problem

VortexTranslater::get_cells threw the status as a string:

throw std::runtime_error(status.ToString());

The status arriving there is fully classified on the object-store path — S3FileSystem's ErrorToStatus attaches an ExtendStatusDetail, and both hops up (FillVortexRangeFile, the loader lambda) propagate it untouched via ARROW_*_RAISE. Stringifying it destroyed the one thing the caller needed.

Consequence: a throttled object store during a vortex cache miss surfaced as an untyped exception, hit the "unclassified means permanent" fallback, and was never retried.

Fix

throw ToSegcoreError(status);

milvus::SegcoreError is already an exception type carrying a milvus::ErrorCode, so the classification survives the throw.

The exception itself stays. Translator::get_cells returns a vector, so milvus-common's caching layer reports load failures through exceptions — that is the interface's contract and not this PR's to change. What we throw is ours.

Verification

Regression test drives a real translator over a filesystem whose reads fail with a classified throttle, and asserts the caught exception carries StorageTransientError.

Red/green verified rather than assumed:

Result
With the fix *Vortex* 80 passed / 0 failed / 15 skipped (pre-existing cloud gates)
Fix reverted exactly 1 failure, the new test, in the catch (const std::exception&) branch: load failure lost its type, got: IOError: SlowDown...

Blast radius with the fix reverted is exactly the one test written to catch it — nothing else was depending on the stringified-throw behaviour.

One detail from the red run that is worth stating, because it is the shape of this whole bug class: the reverted message still contains Detail: StorageTransientThrottling: SlowDown. The classification was never missing from the text. It was just unbranchable.

Test harness note

The fake source file implements arrow::io::RandomAccessFile directly rather than deriving from arrow::io::BufferReader. BufferReader inherits RandomAccessFileConcurrencyWrapper<BufferReader>, which declares both ReadAt overloads final and dispatches through CRTP to a non-virtual DoReadAt — a subclass cannot intercept a read there at all, and a DoReadAt override would silently never be called. Similarly ThrottledFileSystem wraps the fixture's rooted filesystem instead of subclassing LocalFileSystem, which would resolve the relative path against the process CWD.

Scope

This is one of three sites in the same class, tracked in #609. The other two are left for separate PRs because they need a decision rather than a mechanical change:

  • cpp/src/packed/reader.cpp:88 is the deprecated throwing PackedRecordBatchReader constructor. If it is going away, deleting it beats reclassifying it.
  • cpp/src/packed/column_group.cpp:64 uses result.status().message(), which drops even the arrow StatusCode. The condition is arrow::Table::FromRecordBatches failing on a schema mismatch across batches — our own invariant, so PackedUnexpected rather than a passthrough.

Related: #603 defines the taxonomy this fix preserves.

🤖 Generated with Claude Code

https://claude.ai/code/session_011HDgMok3nR8ZNugKWWQQK2

`VortexTranslater::get_cells` threw `std::runtime_error(status.ToString())`.
The status arriving there is fully classified on the object-store path --
S3FileSystem's ErrorToStatus attaches an ExtendStatusDetail, and both hops up
(FillVortexRangeFile, the loader lambda) propagate it untouched via
ARROW_*_RAISE -- so stringifying it destroyed the one thing the caller needed.

A throttled object store during a vortex cache miss therefore surfaced as an
untyped exception, landed on the "unclassified means permanent" fallback, and
was never retried.

Throw `ToSegcoreError(status)` instead. `milvus::SegcoreError` is already an
exception type carrying a `milvus::ErrorCode`, so the classification survives
the throw. The exception itself stays: `Translator::get_cells` returns a vector
and milvus-common's caching layer reports load failures through exceptions --
that is the interface's contract and not ours to change here. What we throw is
ours.

Regression test drives a real translator over a filesystem whose reads fail
with a classified throttle and asserts the caught exception carries
StorageTransientError. Red/green verified: with the fix reverted the test fails
in the `catch (const std::exception&)` branch with "load failure lost its
type", and no other test changes verdict -- blast radius is exactly the one
test written to catch it.

Worth noting what the red run prints: the reverted message still *contains*
"Detail: StorageTransientThrottling: SlowDown". The classification was never
missing from the text, it was just unbranchable. That is the whole bug.

Part of milvus-io#609.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HDgMok3nR8ZNugKWWQQK2
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sre-ci-robot

Copy link
Copy Markdown
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: xiaofan-luan
To complete the pull request process, please assign tedxu after the PR has been reviewed.
You can assign the PR to them by writing /assign @tedxu in a comment when ready.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.99%. Comparing base (c6b7c10) to head (f965bc2).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #610      +/-   ##
==========================================
- Coverage   76.00%   75.99%   -0.02%     
==========================================
  Files         168      169       +1     
  Lines       16688    16937     +249     
  Branches     2511     2535      +24     
==========================================
+ Hits        12684    12871     +187     
- Misses       4004     4066      +62     
Flag Coverage Δ
cpp 78.65% <100.00%> (-0.06%) ⬇️
python 44.45% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

const auto& cell_metas = *cell_metas_;
for (auto cid : cids) {
if (cid < 0 || static_cast<size_t>(cid) >= cell_metas.size()) {
throw std::out_of_range(fmt::format("Vortex cell id {} is out of range, num_cells={}", cid, cell_metas.size()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this throw also be handled?

VortexTranslater::estimated_byte_size_of_cell(milvus::cachinglayer::cid_t cid) const {
const auto& cell_metas = *cell_metas_;
if (cid < 0 || static_cast<size_t>(cid) >= cell_metas.size()) {
throw std::out_of_range(fmt::format("Vortex cell id {} is out of range, num_cells={}", cid, cell_metas.size()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ditto, and check others

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants