obs(store): track MVCC write conflicts by key-prefix class#587
Conversation
Adds a protocol-independent OCC conflict signal at the store layer:
checkConflicts / checkReadConflicts in the Pebble and in-memory MVCC
stores now increment a per-(kind, key_prefix) counter immediately
before returning NewWriteConflictError. A new WriteConflictCollector
polls each group's store every 5s (matching DispatchCollector and
PebbleCollector cadence) and mirrors deltas into
elastickv_store_write_conflict_total{group, kind, key_prefix}.
Motivation: the proxy-side metric counts per client request; this
store-side metric counts per Raft-applied proposal, which is the
right signal for capacity and lock-resolver races (e.g. the txn
rollback incident pattern). The metric is visible whether the
consumer is Redis, DynamoDB, or raw KV.
The kind label splits read-write (checkReadConflicts) from
write-write (checkConflicts) conflicts since the ops implications
differ; key_prefix uses a bounded classification aligned with the
existing txn/redis/hash/set/zset/list/ddb prefixes.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 46 minutes and 47 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements a monitoring system to track Optimistic Concurrency Control (OCC) conflicts at the MVCC store layer, adding a Prometheus collector and key-prefix classification logic across the store and monitoring packages. Reviewer feedback suggests that these conflict checks are redundant at the store layer because the leader is responsible for issuing consistent timestamps, and recommends removing the logic to align with the repository's concurrency model.
| // failing ApplyMutations corresponds to one OCC conflict | ||
| // regardless of how many subsequent mutations would also | ||
| // collide, which matches the Prometheus rate semantics. | ||
| s.writeConflicts.record(WriteConflictKindWrite, classifyWriteConflictKey(mut.Key)) |
There was a problem hiding this comment.
Optimistic Concurrency Control (OCC) conflict checks are not required at the store layer because the leader is responsible for issuing consistent timestamps. Instead of refactoring this logic, it should be removed to align with the repository's concurrency model.
References
- Optimistic Concurrency Control (OCC) conflict checks are not required at the store layer because the leader is responsible for issuing consistent timestamps, which is expected to prevent write conflicts.
| func (s *mvccStore) checkConflictsLocked(mutations []*KVPairMutation, readKeys [][]byte, startTS uint64) error { | ||
| for _, mut := range mutations { | ||
| if latestVer, ok := s.latestVersionLocked(mut.Key); ok && latestVer.TS > startTS { | ||
| s.writeConflicts.record(WriteConflictKindWrite, classifyWriteConflictKey(mut.Key)) |
There was a problem hiding this comment.
Conflict checks at the store layer are unnecessary as the leader ensures consistent timestamps. Following the repository's architectural rules, this logic should be removed rather than centralized.
References
- Optimistic Concurrency Control (OCC) conflict checks are not required at the store layer because the leader is responsible for issuing consistent timestamps, which is expected to prevent write conflicts.
|
@gemini-code-assist Both medium-priority comments (store/lsm_store.go:872 and store/mvcc_store.go:502) appear to misread the diff. Clarifying with evidence so you can re-evaluate. This PR does not add
|
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
Pull request overview
This PR adds a protocol-independent MVCC store–layer metric for OCC write conflicts, bucketed by conflict kind (read/write) and a bounded key-prefix classification, and wires a new collector into node monitoring to export elastickv_store_write_conflict_total{group,kind,key_prefix}.
Changes:
- Add per-store write-conflict counters and key-prefix classification, incremented on WW and RW conflict paths immediately before returning
NewWriteConflictError. - Introduce
monitoring.WriteConflictCollectorand registry plumbing to poll stores every 5s and export conflict deltas to Prometheus. - Extend
store.MVCCStore(and wrappers) withWriteConflictCountsByPrefix()plus comprehensive unit/integration tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| store/write_conflict_counter.go | New bounded key-prefix classifier + mutex-protected per-bucket counters and label encode/decode helpers. |
| store/store.go | Extends MVCCStore interface with WriteConflictCountsByPrefix() snapshot API. |
| store/mvcc_store.go | Adds in-memory store conflict counter recording + snapshot accessors. |
| store/lsm_store.go | Adds Pebble store conflict counter recording in WW/RW conflict checks + snapshot accessors. |
| kv/shard_store.go | Aggregates shard-group conflict snapshots into a node-wide snapshot for ShardStore. |
| kv/leader_routed_store.go | Delegates conflict snapshot access to the local store with nil-safety. |
| monitoring/write_conflict.go | New Prometheus metric + polling collector that emits positive deltas with reset rebasing. |
| monitoring/registry.go | Registers write-conflict metrics and exposes WriteConflictCollector(). |
| main.go | Factors monitoring startup into helper and wires in the new write-conflict collector sources. |
| store/write_conflict_counter_test.go | Unit tests for classification, counter record/snapshot behavior, and label decoding. |
| store/write_conflict_store_test.go | Integration tests asserting WW/RW conflict paths increment the expected buckets (Pebble + in-memory). |
| monitoring/write_conflict_test.go | Collector tests for deltas, reset rebasing, malformed label skipping, and nil-safety. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
checkConflicts(write-write) andcheckReadConflicts(read-write) instore/lsm_store.go/store/mvcc_store.gonow increment a bounded per-(kind, key_prefix) counter immediately before returningNewWriteConflictError; a newmonitoring.WriteConflictCollectorpolls the stores every 5 s (matching the existingDispatchCollector/PebbleCollectorcadence) and mirrors deltas intoelastickv_store_write_conflict_total{group, kind, key_prefix}.kindsplitsread(RW conflict via read set) fromwrite(WW conflict via mutation set) since ops implications differ.key_prefixuses a bounded classification aligned with existing prefix constants (!txn|lock|,!txn|rb|,!redis|str|,!hs|,!zs|,!lst|,!ddb|, ...) with anotherfallback so user-supplied keys cannot grow cardinality.Motivation
#585adds the proxy-side view (per client request). This PR adds the underlying store-side view (per Raft-applied proposal), so the signal is visible regardless of protocol adapter (Redis, DynamoDB, raw KV). In the!txn|rb|production incident the proxy counter spiked per user request but a single request fanned out to many Raft proposals; the store-side metric would have surfaced the real pressure (txn_rollback bucket) directly.Sample Grafana query
Split by conflict kind:
Test plan
go test -race -count=1 ./store/... ./monitoring/... ./adapter/... ./kv/...make lintstore/write_conflict_counter_test.go: table test forclassifyWriteConflictKeycovering every bucket; counter record / snapshot / nil-safety.store/write_conflict_store_test.go: integration tests for both pebble-backed and in-memory MVCC stores exercising the WW and RW conflict paths and asserting the right bucket increments.monitoring/write_conflict_test.go: collector delta behaviour, source-reset (counter-down) rebasing, malformed-label skip, nil-safety.