Skip to content

fix(striper): start each object on its own drive - #3

Merged
neo-th merged 1 commit into
mainfrom
fix/stripe-start-drive
Nov 4, 2026
Merged

neo-th merged 1 commit into
mainfrom
fix/stripe-start-drive

Conversation

@neo-th

@neo-th neo-th commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Chunk 0 of every object went to the first drive (block_devices[chunk_idx % num_devices]). That has two effects:

  • Objects that don't fill a whole stripe leave the last drives underused. A 14 MiB object in 4 MiB chunks puts 4 MiB on drives 1 to 3 and 2 MiB on drive 4.
  • Objects under one chunk all land on the first drive, however many drives are in the pool.

Change

The start drive now comes from a hash of the object's URI.

  • The hash is stable across restarts, so an append continues the object's pattern.
  • Each chunk records its own drive, so data that's already stored stays readable.
  • test_server_stage_write_and_commit_flow required chunk 0 on drive 0. It now checks that the object's two chunks use both drives.

Results

These are from 4 Micron 7450 Gen4 drives through LMCache's L2 benchmark, with 64 objects in flight and 32 workers. Each figure is the median of 3 runs in shuffled order, old and fixed in the same session.

Objects Load before (MB/s) Load after (MB/s) Store before (MB/s) Store after (MB/s)
2 MiB 6321 24609 4962 15390
14 MiB 20740 24884 16129 18549
16 MiB 23587 24646 18144 18759

At 14 MiB on 2 drives, load goes from 10526 to 12071 MB/s. fio measured 27.6 GB/s read on the same 4 drives at 4 MiB QD32.

Testing

All on this code or the same fix on the connector branch, on 4 Micron 7450 drives:

  • Old vs fixed: 3 runs of each setup in shuffled order, in one session.
  • Upgrade: from today's main through the Python client, objects read back byte for byte after the upgrade and after a rollback, with a clean stop and with kill -9. Existing objects keep their placement and aren't rebalanced.
  • Crash, restart, corruption and eviction: no wrong bytes in any test. The connector branch's tests of these use the same striper.
  • Soak: 30 minutes of reads at 21 GB/s while 2 GB/s of writes replaced the same objects. 323,029 loads were checked byte for byte, none wrong.
  • Release checks: Rust, Python and end-to-end stages pass.

@neo-th
neo-th force-pushed the fix/stripe-start-drive branch from 8815ea0 to 4389254 Compare November 4, 2026 07:32
Chunk 0 of every object went to the first drive. Objects that don't fill a
whole stripe therefore left the last drives underused, and objects under one
chunk all landed on the first drive, however many drives were in the pool.

The start drive now comes from a hash of the object's URI. It's stable across
restarts, so an append continues the object's pattern. Each chunk records its
own drive, so data that's already stored stays readable.

Measured through LMCache's L2 benchmark on 4 Micron 7450 Gen4 drives, 32
workers, median of 3 runs:

- 2 MiB objects: load 6321 to 24609 MB/s, store 4962 to 15390 MB/s
- 14 MiB objects: load 20740 to 24884 MB/s, store 16129 to 18549 MB/s

Tested from main's daemon through the Python client: objects stored before the
upgrade read back byte for byte after it, and objects from both versions read
back after a rollback, with a clean stop and with kill -9.
@neo-th
neo-th force-pushed the fix/stripe-start-drive branch from 4389254 to 1b145ab Compare November 4, 2026 09:24
@neo-th
neo-th changed the base branch from feat/lmcache-rust-connector to main November 4, 2026 09:24
@neo-th
neo-th marked this pull request as ready for review November 4, 2026 09:25
@neo-th
neo-th merged commit ac4da67 into main Nov 4, 2026
1 check passed
@neo-th
neo-th deleted the fix/stripe-start-drive branch November 4, 2026 19:52
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.

1 participant