Skip to content

Fix DSS drain gates - #521

Merged
thweetkomputer merged 1 commit into
mainfrom
fix-dss-read-write-drain-gate
Jul 13, 2026
Merged

Fix DSS drain gates#521
thweetkomputer merged 1 commit into
mainfrom
fix-dss-read-write-drain-gate

Conversation

@thweetkomputer

@thweetkomputer thweetkomputer commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR closes two DataStoreService shutdown/drain races:

  • Prevents a write request from being accepted concurrently with ReadWrite -> ReadOnly while the drainer observes zero in-flight writes.
  • Prevents new read/scan submissions from entering the backend after ReadOnly -> Closed begins draining, which is required for EloqStore because Stop() must not race with a later ExecAsyn() submission.

It also advances the nested eloqstore submodule to the latest main commit:

Details

Write drain gate

The write path already increments ongoing_write_requests_ before checking shard status. However, the previous ordering used release/acquire operations, which does not impose one global order across the writer's count increment/status load and the drainer's status CAS/count load.

This PR changes the write gate operations to seq_cst:

  • write request count increment/decrement
  • post-increment shard status loads on write paths
  • ReadWrite -> ReadOnly CAS
  • write-drain count load

With one sequentially consistent order, the unsafe outcome where a writer still observes ReadWrite while the drainer already observes count 0 is prevented.

Read/scan submit drain gate

Read and scan paths now enter a per-thread submit window before loading shard status. ReadOnly -> Closed first switches the shard status to Starting, then waits for all submit windows to drain before calling backend Shutdown().

This blocks the problematic interleaving:

  1. a read/scan request is in DataStoreService and about to submit to the backend;
  2. close switches the shard to closing and drains;
  3. backend shutdown/stop runs;
  4. the read/scan request submits after shutdown has begun.

The submit window is intentionally scoped only around the status check and backend submission. It is not a full async request lifetime counter.

That is deliberate for EloqStore: the bug is submission-after-stop, not waiting for every accepted read callback before close. Extending the counter to SetFinish() would add a cross-thread atomic decrement to the hot read path.

For RocksDB/RocksDBCloud, accepted reads/scans are already safe because their async behavior is implemented by DataStoreService's query_worker_pool_. Shutdown() joins the worker pool, and workers drain queued work before exiting, so the DB is closed/deleted only after accepted worker tasks complete.

Validation

  • cmake --build bld-eloqstore-local --target eloqkv -j2
  • git diff --check -- store_handler/eloq_data_store_service/data_store_service.cpp store_handler/eloq_data_store_service/data_store_service.h

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of read and scan operations during datastore shutdown by tracking and draining in-flight read activity before shutdown completes.
    • Prevented shutdown from interrupting in-progress read requests, scans, and scan-close flows.
    • Strengthened synchronization around shard status handling to reduce race conditions under concurrent load.
  • Chores
    • Updated the checked-in datastore service submodule reference.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a68d438-d12b-4db7-816e-f2d1ad1589b0

📥 Commits

Reviewing files that changed from the base of the PR and between f6082b3 and 631a8b2.

📒 Files selected for processing (3)
  • store_handler/eloq_data_store_service/data_store_service.cpp
  • store_handler/eloq_data_store_service/data_store_service.h
  • store_handler/eloq_data_store_service/eloqstore
🚧 Files skipped from review as they are similar to previous changes (3)
  • store_handler/eloq_data_store_service/eloqstore
  • store_handler/eloq_data_store_service/data_store_service.h
  • store_handler/eloq_data_store_service/data_store_service.cpp

Walkthrough

DataStoreService tracks read and scan submission windows per thread and shard, uses RAII guards across read paths, and waits for those windows to drain before datastore shutdown. Related atomic operations now use sequential consistency, and the eloqstore submodule pointer changes.

Changes

Read submit window quiescing

Layer / File(s) Summary
Window primitives and shard state
store_handler/eloq_data_store_service/data_store_service.h, store_handler/eloq_data_store_service/data_store_service.cpp
Adds aligned per-shard slots, thread-local tracking, move-only RAII guards, and helpers for entering, leaving, and draining read-submit windows.
Read and scan integration
store_handler/eloq_data_store_service/data_store_service.cpp
RPC and local Read, ScanNext, and ScanClose handlers enter a read-submit window before checking shard status and submitting datastore requests.
Shutdown draining and atomic ordering
store_handler/eloq_data_store_service/data_store_service.cpp, store_handler/eloq_data_store_service/data_store_service.h
Updates relevant atomic operations to sequential consistency and drains read-submit windows before closing the datastore.

Eloqstore revision

Layer / File(s) Summary
Submodule revision
store_handler/eloq_data_store_service/eloqstore
Updates the checked-in eloqstore submodule commit.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • eloqdata/tx_service#519 — The PR implements read/scan submission-window tracking and drains those windows before datastore shutdown.

Suggested reviewers: liangjchen, liunyl

Sequence Diagram(s)

sequenceDiagram
  participant ReadScanHandlers
  participant DataStoreService
  participant Datastore
  ReadScanHandlers->>DataStoreService: EnterReadSubmitWindow(shard_id)
  DataStoreService->>Datastore: Submit read or scan request
  DataStoreService-->>ReadScanHandlers: ReadSubmitGuard
  ReadScanHandlers->>DataStoreService: Guard destruction
Loading

Poem

A rabbit guards each read-submit gate,
With tiny counters ticking straight.
When shutdown calls, the windows clear,
Then datastore sleeps without fear.
Hop, hop—safe quiescence is here!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning It covers summary, implementation, and validation, but omits most required template sections like risk, rollback, reviewer guide, and follow-up. Add the missing template sections with concrete context, behavior before/after, design tradeoffs, risk assessment, rollback plan, reviewer guide, and follow-up work.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: fixing DSS drain gates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-dss-read-write-drain-gate

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thweetkomputer
thweetkomputer force-pushed the fix-dss-read-write-drain-gate branch from 27cc31e to f6082b3 Compare July 11, 2026 06:27
@thweetkomputer
thweetkomputer marked this pull request as ready for review July 13, 2026 04:54
@thweetkomputer
thweetkomputer force-pushed the fix-dss-read-write-drain-gate branch from f6082b3 to 9738f70 Compare July 13, 2026 05:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
store_handler/eloq_data_store_service/data_store_service.h (2)

716-741: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ReadSubmitSlot::depth needs a trailing underscore.

Per the Google C++ style guidelines used in this repo, member variables must use snake_case with a trailing underscore (e.g. my_name_). depth should be depth_.

🔧 Proposed fix
     struct alignas(64) ReadSubmitSlot
     {
-        std::atomic<uint32_t> depth{0};
+        std::atomic<uint32_t> depth_{0};
     };

(update the three call sites in data_store_service.cpp accordingly: slot.depthslot.depth_)

As per coding guidelines, "Member variables should use snake_case with trailing underscore (e.g., `my_name_`)".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@store_handler/eloq_data_store_service/data_store_service.h` around lines 716
- 741, Rename the ReadSubmitSlot member depth to depth_ in data_store_service.h,
then update all three corresponding slot.depth references in
data_store_service.cpp to slot.depth_.

Source: Coding guidelines


198-200: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Static sizing multiplies to a large fixed memory footprint.

data_shards_ is now std::array<DataShard, kMaxShardCount> (1000), and each DataShard embeds std::array<ReadSubmitSlot, kReadSubmitSlotCount> (1000) where ReadSubmitSlot is alignas(64) (i.e. 64 bytes per slot). That alone is 1000 × 1000 × 64 B ≈ 62 MiB of always-resident memory per DataStoreService instance, independent of how many shards/threads are actually in use on a given node. If a node only owns a handful of shards, nearly all of that memory is wasted.

Consider sizing read_submit_slots_/kReadSubmitSlotCount to the actual expected worker/thread concurrency (e.g. bthread worker pool size) rather than a flat 1000, or lazily allocate the slots array only for shards this node actually owns.

Also applies to: 822-830

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@store_handler/eloq_data_store_service/data_store_service.h` around lines 198
- 200, Reduce the fixed memory footprint in DataStoreService by removing the
flat 1000-slot sizing for each DataShard’s read_submit_slots_ and sizing it to
actual worker/thread concurrency, or allocating it only for owned shards. Update
kReadSubmitSlotCount and the read_submit_slots_ storage/initialization together,
while preserving support for the configured concurrency and existing read
submission behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@store_handler/eloq_data_store_service/data_store_service.h`:
- Around line 716-741: Rename the ReadSubmitSlot member depth to depth_ in
data_store_service.h, then update all three corresponding slot.depth references
in data_store_service.cpp to slot.depth_.
- Around line 198-200: Reduce the fixed memory footprint in DataStoreService by
removing the flat 1000-slot sizing for each DataShard’s read_submit_slots_ and
sizing it to actual worker/thread concurrency, or allocating it only for owned
shards. Update kReadSubmitSlotCount and the read_submit_slots_
storage/initialization together, while preserving support for the configured
concurrency and existing read submission behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7d0a021-c6d2-4856-bbf7-5d4990f3f2fc

📥 Commits

Reviewing files that changed from the base of the PR and between ca74cbb and f6082b3.

📒 Files selected for processing (3)
  • store_handler/eloq_data_store_service/data_store_service.cpp
  • store_handler/eloq_data_store_service/data_store_service.h
  • store_handler/eloq_data_store_service/eloqstore

@thweetkomputer

Copy link
Copy Markdown
Collaborator Author

I have performed a benchmark on an 8-core ARM VM. No degradation is found.

@thweetkomputer
thweetkomputer force-pushed the fix-dss-read-write-drain-gate branch from 9738f70 to 631a8b2 Compare July 13, 2026 10:04
@thweetkomputer
thweetkomputer merged commit a1162d3 into main Jul 13, 2026
10 checks passed
@thweetkomputer
thweetkomputer deleted the fix-dss-read-write-drain-gate branch July 13, 2026 14:09
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.

2 participants