Skip to content

fix: implement FileAuditKV.MultiSave instead of returning "not implemented" - #521

Open
lokeshramchand-ctrl wants to merge 2 commits into
milvus-io:mainfrom
lokeshramchand-ctrl:fix/file-audit-kv-multisave-not-implemented
Open

fix: implement FileAuditKV.MultiSave instead of returning "not implemented"#521
lokeshramchand-ctrl wants to merge 2 commits into
milvus-io:mainfrom
lokeshramchand-ctrl:fix/file-audit-kv-multisave-not-implemented

Conversation

@lokeshramchand-ctrl

Copy link
Copy Markdown

Summary

  • FileAuditKV.MultiSave was a stub that always returned errors.New("not implemented") instead of delegating to the wrapped MetaKV client, unlike every other method on FileAuditKV (Save, Remove, etc.), which write an audit record and then delegate.
  • FileAuditKV is the default write wrapper whenever an audit log file is open (the normal case), so this broke two live call sites:
    • repair segment-storage-layout fails 100% of the time at the metadata-backup step, before any repair logic runs.
    • restore silently no-ops: its MultiSave error is only printed inside a goroutine and never propagated, so a restore can report success while writing zero keys to etcd.
  • Mirrors the existing Save() pattern: write audit records for the batch, delegate to c.cli.MultiSave, and return its result. Also guards against mismatched keys/values slice lengths.

Fixes #520

Test plan

  • go build ./states/kv/...
  • go vet ./states/kv/...
  • New states/kv/kv_audit_test.go covering: delegation + persistence, error propagation from the wrapped client, and mismatched key/value length rejection — all pass

…ented"

FileAuditKV.MultiSave always returned errors.New("not implemented")
instead of delegating to the wrapped MetaKV client, unlike every other
method on FileAuditKV (Save, Remove, etc.), which log/record the audit
entry and then delegate.

FileAuditKV is the default write wrapper whenever an audit log file is
open (the normal case), so this broke two call sites:
  - `repair segment-storage-layout` fails at the metadata-backup step
    before any repair logic runs.
  - `restore` silently no-ops: its MultiSave error is only printed
    inside a goroutine and never propagated, so a restore can report
    success while writing zero keys to etcd.

Mirror the Save() pattern: write audit records around the batch, then
delegate to the wrapped client's MultiSave and return its result.

Fixes milvus-io#520

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: lokeshramchand-ctrl <lokeshramchand@gmail.com>
@sre-ci-robot
sre-ci-robot requested a review from congqixia August 23, 2026 18:08
@sre-ci-robot

Copy link
Copy Markdown
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: lokeshramchand-ctrl
To complete the pull request process, please assign congqixia after the PR has been reviewed.
You can assign the PR to them by writing /assign @congqixia 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

@mergify

mergify Bot commented Aug 23, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@lokeshramchand-ctrl

lokeshramchand-ctrl commented Aug 24, 2026

Copy link
Copy Markdown
Author

/assign @congqixia - Please look into this and let me know if any edits required

Comment thread states/kv/kv_audit.go
return errors.Newf("keys and values length mismatch, keys: %d, values: %d", len(keys), len(values))
}
c.writeHeader(models.AuditOpType_OpPut, int32(len(keys)))
err := c.cli.MultiSave(ctx, keys, values)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

states/kv/kv_audit.go line:58
Medium ---- This delegation is correct and faithfully mirrors the Save() pattern, but the stated motivation (repair/restore broken by this stub) does not match the client wiring at head. repair segment-storage-layout runs on ComponentRepair, which is constructed with the unwrapped client (states/instance.go:182 passes cli, and states/backup_mock_connect.go:126 builds the client with plain kv.NewEtcdKV), so its MultiSave calls never reach FileAuditKV. On the TiKV connection the "not implemented" error actually comes from txnTiKV.MultiSave (states/kv/kv.go:476), which this PR does not touch, so that repair path stays broken; restore/load-backup likewise writes through the raw etcd client. Could you confirm which path you reproduced the failure on, and whether fixing txnTiKV.MultiSave (or wiring the components through the audit wrapper) belongs in this PR?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch, and you're right on both counts.

I reproduced this on the restore/load-backup path (restoreEtcdFromBackV2 in states/etcd_restore.go:118, which writes via state.client — the audit-wrapped kv), not on repair. On repair specifically, the actual bug was one step earlier: GetInstanceState in states/instance.go constructed ComponentRepair/ComponentRemove/ComponentShow/ComponentSet with the raw cli instead of the audit-wrapped kv, so those components' writes never reached FileAuditKV at all — the MultiSave fix alone wouldn't have helped them.

I've pushed a follow-up commit (731e2c8) that wires all four components through the audit-wrapped kv, so repair segment-storage-layout now goes through FileAuditKV.MultiSave as intended. txnTiKV.MultiSave (states/kv/kv.go:476) is a separate, still-unimplemented method on a different backend — happy to file that as its own follow-up issue rather than scope-creep this PR, unless you'd prefer it bundled here too.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The issue is confirmed fixed.

require.Len(t, fake.multiSaveCalls, 1)
assert.Equal(t, keys, fake.multiSaveCalls[0][0])
assert.Equal(t, values, fake.multiSaveCalls[0][1])
assert.Equal(t, "v1", fake.data["k1"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

states/kv/kv_audit_test.go line:108
Low ---- The three subtests verify delegation, error propagation, and length rejection, but none ever reads the audit file back, so the actual purpose of FileAuditKV - writing the OpPut/OpPutBefore/OpPutAfter headers plus the key/value records - is untested. A regression in the file-writing path (wrong headers, wrong entry counts, dropped records) would pass all three tests. Consider a case that replays the temp file by parsing the length-prefixed AuditHeader records and asserts header counts and key/value contents, so the on-disk format is locked in.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed, added in 731e2c8. Two new subtests in states/kv/kv_audit_test.go replay the raw file (length-prefixed records, decoded via proto.Unmarshal into AuditHeader / mvccpb.KeyValue) and assert on it directly:

  • writes OpPut/OpPutBefore/OpPutAfter headers and key/value records: on success, asserts the exact 5-record sequence (OpPut header with EntriesNum=2, OpPutBefore header, the two key/value records with correct key/value bytes, OpPutAfter header).
  • writes only OpPutAfter header when underlying save fails: on failure, asserts only the OpPut + OpPutAfter headers are written and no key/value records leak through.

That locks in the on-disk format so a regression in header ordering/counts or dropped records would now fail the test.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The issue is confirmed fixed.

@mergify

mergify Bot commented Aug 27, 2026

Copy link
Copy Markdown

@lokeshramchand-ctrl Thanks for your contribution. Please submit with DCO, see the contributing guide https://github.com/milvus-io/milvus/blob/master/CONTRIBUTING.md#developer-certificate-of-origin-dco.

@mergify mergify Bot added needs-dco and removed dco-passed labels Aug 27, 2026
ComponentRepair, ComponentRemove, ComponentShow, and ComponentSet were
constructed with the raw etcd client instead of the FileAuditKV wrapper,
so their writes (e.g. repair segment-storage-layout) bypassed audit
logging entirely and never reached the previously-fixed MultiSave path.

Also adds a regression test that replays the on-disk audit log format
(length-prefixed AuditHeader + key/value records) to lock in the
OpPut/OpPutBefore/OpPutAfter sequence, per review feedback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: lokeshramchand-ctrl <lokeshramchand@gmail.com>
@lokeshramchand-ctrl
lokeshramchand-ctrl force-pushed the fix/file-audit-kv-multisave-not-implemented branch from 731e2c8 to 907cbfa Compare August 27, 2026 18:11
@mergify mergify Bot added dco-passed and removed needs-dco labels Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: FileAuditKV.MultiSave is unimplemented, breaking repair segment-storage-layout and silently no-opping restore

3 participants