fix(cubestore): rebuild missing secondary index when unique index lookup returns empty - #11415
Open
Xuxiaotuan wants to merge 4 commits into
Open
fix(cubestore): rebuild missing secondary index when unique index lookup returns empty#11415Xuxiaotuan wants to merge 4 commits into
Xuxiaotuan wants to merge 4 commits into
Conversation
…kup returns empty (cube-js#11212) When a unique secondary index entry is missing but the row still exists, get_single_row_by_index fails with 'One value expected'. This fix calls rebuild_index() when a unique index lookup returns empty, mirroring the existing repair pattern in get_row_by_index_opt.
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.
Issue Reference this PR resolves
#11212
Description of Changes Made
Background
DROP TABLE <schema>.<table>via MySQL wire protocol fails with:The affected schemas' tables still appear in
information_schema.tables(which uses schema ID lookup, not the name index), so they look normal from the client's perspective. But any DROP against them hits this error at the schema name lookup stage. MySQL wire clients are stuck — they can't clean up. Cube's own driver doesn't help either because it uses a different code path (version_entries) that also doesn't see these schemas. Physical tables accumulate indefinitely.Root Cause
The error comes from
get_single_row_by_index()atrocks_table.rs:883. The secondary index entry for the schema name is missing, but the schema row itself still exists (findable by primary key ID).Normal write paths (
do_insert,delete,update) write both the row KV and secondary index entries atomically in the same RocksDB batch, so the issue is not caused by normal operations. The index entry gets lost in less common scenarios:migration_check_indexes()only checks for theSecondaryIndexInfomarker entry, not the actual index entries. If the marker exists but individual entries are missing (e.g. from an interrupted upgrade), no rebuild is triggered.Changes
File:
rust/cubestore/cubestore/src/metastore/rocks_table.rs—get_single_row_by_index()(+11 lines)Why this approach: Mirrors the existing repair pattern at
get_row_by_index_opt(line 833), which handles the reverse case — index entry exists but row is missing — by callingrebuild_index()and returning an error.Scope: Only fires for unique indexes. Non-unique indexes returning empty is a legitimate "no results" result. The affected callers are all name-based lookups on small datasets:
SchemaRocksIndex::Name,TableRocksIndex::Name,SourceRocksIndex::Name,MultiIndexRocksIndex::ByName.Caveat: Since
rebuild_index()writes directly to RocksDB butget_single_row_by_indexis typically called inside awrite_operationwith a point-in-time snapshot, the first call returns an error (stale snapshot doesn't see the new entries). The caller retries with a fresh snapshot and the operation succeeds. This is the same behavior as the existing repair path.Alternative considered: Also considered scanning all rows to find the match and writing just the missing entry — that way the first call would succeed without a retry. The code would be:
Went with the current approach because it mirrors the existing
rebuild_indexpattern inget_row_by_index_opt. The alternative's advantage of first-attempt success doesn't justify introducing a repair mechanism with no precedent in the codebase.Verification
A separate reproduction binary (not included in this PR) validated the fix:
CREATE TABLE test_schema.t1 (id INT)— first call triggersrebuild_indexbut returns error (stale snapshot)CREATE TABLE test_schema.t1 (id INT)again — succeeds (fresh snapshot sees repaired index)Check List