Skip to content

fix: reject ambiguous secondary index names - #1266

Merged
mattisonchao merged 8 commits into
mainfrom
fix/reject-secondary-index-slash
Aug 31, 2026
Merged

fix: reject ambiguous secondary index names#1266
mattisonchao merged 8 commits into
mainfrom
fix/reject-secondary-index-slash

Conversation

@mattisonchao

@mattisonchao mattisonchao commented Aug 2, 2026

Copy link
Copy Markdown
Member

Motivation

Secondary index names are embedded unescaped in persisted index keys. A name containing / is indistinguishable from part of the secondary key, allowing two logical indexes to produce the same stored key.

Go client validation catches supported client calls, but older clients and direct protocol callers can still append ambiguous entries. Server-side rejection must be deterministic during rolling upgrades and must not stop WAL replay or commit-offset advancement.

Tracks #1265.

Modifications

  • validate collected Put options in newPutOptions without changing the PutOption.applyPut contract
  • reject / in secondary index names before a Go client Put enters batching
  • add the per-Put Status_INVALID_ARGUMENT result and map it to ErrInvalidOptions
  • add and advertise FEATURE_SECONDARY_INDEX_NAME_VALIDATION
  • activate server validation only after every ensemble member advertises support and the replicated feature-enable entry applies
  • explicitly activate secondary-index name validation in standalone mode without enabling cluster-specific checksum behavior
  • add a pure ValidatePut preflight hook to the update callback contract
  • pass a read-only feature checker into prevalidation so the secondary-index callback owns both its rollout gate and validation rule
  • invoke callback validation at the start of database.applyPut, before sequence, version, session, index, or record mutations
  • keep invalid operations as deterministic WAL no-ops while processing later batch operations and advancing the commit offset
  • preserve legacy behavior for entries applied before feature activation
  • document the index-name restriction in SecondaryIndex and UseIndex
  • add client, database, callback, feature-persistence, rolling-upgrade, and raw-protocol standalone tests

Testing

  • go test -race ./oxia ./oxiad/common/feature ./oxiad/coordinator/runtime/controller/shard ./oxiad/dataserver/controller/lead ./oxiad/dataserver/database ./oxiad/dataserver ./tests/client
  • merged the PR head with current main and reran the standalone regression test

Copilot AI previously approved these changes Aug 2, 2026

Copilot AI 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.

🟢 Ready to approve

The changes are narrowly scoped validation hardening with solid test coverage, and the only noted issue is a small doc comment accuracy fix.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR hardens secondary index handling by rejecting secondary index names containing / at public RPC boundaries and in the Go client Put option parsing, preventing ambiguous persisted index key prefixes and cross-index collisions in the data server.

Changes:

  • Added server-side validation rejecting / in secondary index names for unary Write, WriteStream, Read (Get), List, and RangeScan requests.
  • Added Go client-side validation for Put secondary index options, returning ErrInvalidOptions early.
  • Updated API documentation/comments and added focused unit tests covering the new validation behavior.
File summaries
File Description
oxiad/dataserver/public_rpc_server.go Adds RPC-layer validation for secondary index names to prevent ambiguous /-containing index names from entering the system via public APIs.
oxiad/dataserver/public_rpc_server_test.go Adds tests ensuring unary and streaming RPC paths reject /-containing secondary index names with InvalidArgument.
oxia/options_put.go Adds local Put option validation rejecting / in secondary index names and updates SecondaryIndex doc comment.
oxia/options_put_test.go Adds unit test verifying Put option validation fails locally with ErrInvalidOptions for invalid index names.
oxia/options_list.go Updates UseIndex doc comment to document the / restriction (but see review comment about accuracy across Get/RangeScan).
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread oxia/options_list.go Outdated
Copilot AI review requested due to automatic review settings August 2, 2026 04:40
@mattisonchao
mattisonchao force-pushed the fix/reject-secondary-index-slash branch from 89dbd64 to 93ba458 Compare August 2, 2026 04:40
@mattisonchao mattisonchao changed the title fix: reject ambiguous secondary index names fix: reject ambiguous secondary index names in Go client Aug 2, 2026
@mattisonchao
mattisonchao force-pushed the fix/reject-secondary-index-slash branch from 93ba458 to 4cea25c Compare August 2, 2026 04:43

Copilot AI 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.

🟡 Not ready to approve

The updated UseIndex doc comment is misleading because it describes only List semantics even though UseIndex also applies to Get and RangeScan.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (1)

oxia/options_list.go:56

  • The UseIndex doc comment describes only List semantics ("returned list") even though UseIndex also applies to Get and RangeScan. This can confuse callers about whether returned keys are primary or secondary keys for non-List operations.
// UseIndex lets users specify a secondary index to follow. Index names must not
// contain '/'. The returned list contains the primary keys of the records.
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 2, 2026 04:43
Copilot AI dismissed their stale review, a newer Copilot review was requested August 2, 2026 04:46
Copilot AI previously approved these changes Aug 2, 2026

Copilot AI 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.

🟢 Ready to approve

The behavioral change is localized and covered by a targeted test, with only a minor documentation wording tweak suggested.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (1)

oxia/options_list.go:56

  • UseIndex is applied not only to List but also to RangeScan and Get (see applyRangeScan/applyGet). The updated comment still suggests it only affects "the returned list", which can mislead users about how keys/records are returned when scanning or getting via a secondary index.
// UseIndex lets users specify a secondary index to follow. Index names must not
// contain '/'. The returned list contains the primary keys of the records.
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 2, 2026 04:48
@mattisonchao
mattisonchao force-pushed the fix/reject-secondary-index-slash branch from 4cea25c to aa3ae52 Compare August 2, 2026 04:48
@mattisonchao mattisonchao self-assigned this Aug 2, 2026
@mattisonchao mattisonchao added this to the 0.17.2 milestone Aug 2, 2026
Copilot AI dismissed their stale review, a newer Copilot review was requested August 2, 2026 04:50
Copilot AI previously approved these changes Aug 2, 2026

Copilot AI 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.

🟢 Ready to approve

The change is small and targeted, enforces the intended validation at the correct point (pre-batching), updates docs, and adds a clear unit test for the new behavior.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 2, 2026 05:00
@mattisonchao mattisonchao changed the title fix: reject ambiguous secondary index names in Go client fix: reject ambiguous secondary index names Aug 2, 2026
Copilot AI dismissed their stale review, a newer Copilot review was requested August 2, 2026 05:04

Copilot AI 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.

🟢 Ready to approve

The change is narrowly scoped, feature-gated for safe rolling upgrades, and has targeted tests covering client validation, server no-op semantics, and feature negotiation/persistence.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file
  • Files reviewed: 13/15 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@mattisonchao mattisonchao linked an issue Aug 2, 2026 that may be closed by this pull request
5 tasks
@mattisonchao

Copy link
Copy Markdown
Member Author

Hi @merlimat

could you help review it?

Copilot AI review requested due to automatic review settings August 2, 2026 05:29

Copilot AI 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.

🟡 Human review recommended

It changes core write-path behavior and rolling-upgrade feature negotiation semantics across client/server/proto layers, so a final human review is recommended despite strong test coverage.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file
  • Files reviewed: 16/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 2, 2026 05:48

Copilot AI 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.

🟡 Human review recommended

It changes replicated/protocol-level behavior and feature-gated server write semantics (with rolling-upgrade implications) that warrant final human verification beyond automated review.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file
  • Files reviewed: 16/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 2, 2026 05:51

Copilot AI 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.

🟡 Not ready to approve

The PutOption interface signature change is a breaking public API change and should be reconsidered or replaced with a backward-compatible validation approach.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file

Suppressed comments (2)

oxia/options_put.go:34

  • Changing PutOption.applyPut to return an error is a breaking public API change for any downstream code that implements PutOption (even though this is a "fix" PR). If you want to keep backward compatibility, consider keeping applyPut void and performing secondary-index name validation inside newPutOptions (after applying options) instead.
// PutOption represents an option for the [SyncClient.Put] operation.
type PutOption interface {
	applyPut(opts *putOptions) error
}

oxiad/dataserver/controller/lead/secondary_indexes.go:39

  • wrapperUpdateCallback.ValidatePut only delegates to secondaryIndexesUpdateCallback and skips sessionManagerUpdateOperationCallback.ValidatePut. Even though the current session ValidatePut always returns OK, this creates an easy-to-miss divergence from the OnPut composition order and would silently bypass any future session-level prevalidation.
func (wrapperUpdateCallback) ValidatePut(req *proto.PutRequest, features database.FeatureChecker) proto.Status {
	return secondaryIndexesUpdateCallback.ValidatePut(req, features)
}
  • Files reviewed: 16/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Validate each secondary index name while applying Put options, before the operation enters the client batch manager.

Refs #1265

Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Copilot AI review requested due to automatic review settings August 2, 2026 06:02
@mattisonchao
mattisonchao force-pushed the fix/reject-secondary-index-slash branch from 0ad6640 to c0dd124 Compare August 2, 2026 06:02

Copilot AI 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.

🟡 Not ready to approve

The wrapper callback’s new ValidatePut implementation should chain validation consistently (including the session manager validate hook) to avoid future divergence and missed validations.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file

Suppressed comments (1)

oxiad/dataserver/controller/lead/secondary_indexes.go:40

  • wrapperUpdateCallback.ValidatePut only delegates to secondaryIndexesUpdateCallback and skips sessionManagerUpdateOperationCallback.ValidatePut. Even though the current session manager implementation always returns OK, this creates an inconsistent callback chain compared to OnPut/OnDelete and risks future bugs if session validation is added later.
func (wrapperUpdateCallback) ValidatePut(req *proto.PutRequest, features feature.Checker) proto.Status {
	return secondaryIndexesUpdateCallback.ValidatePut(req, features)
}
  • Files reviewed: 17/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Signed-off-by: mattisonchao <mattisonchao@gmail.com>
Copilot AI review requested due to automatic review settings August 2, 2026 06:06
@mattisonchao

Copy link
Copy Markdown
Member Author

Addressed the latest Copilot review in f64f3d13:

  • restored the existing PutOption.applyPut(*putOptions) contract and moved secondary-index validation into newPutOptions after options are collected
  • made wrapperUpdateCallback.ValidatePut compose session prevalidation before secondary-index prevalidation, matching OnPut

The branch is also rebased onto current main, and the feature capability now lives as feature.Checker in oxiad/common/feature.

Copilot AI 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.

🟡 Human review recommended

It changes core write-apply semantics and protocol enums/feature negotiation, so a final human review is warranted despite the added test coverage.

Review details

Files not reviewed (2)

  • common/proto/client.pb.go: Generated file
  • common/proto/replication.pb.go: Generated file
  • Files reviewed: 15/17 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Signed-off-by: mattisonchao <mattisonchao@gmail.com>
@mattisonchao
mattisonchao merged commit 29f6b73 into main Aug 31, 2026
9 checks passed
@mattisonchao
mattisonchao deleted the fix/reject-secondary-index-slash branch August 31, 2026 16:13
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.

fix: harden secondary index key format handling

2 participants