Skip to content

Commit 0720c2b

Browse files
committed
feat: add wrap_around() for indefinite iteration on sequential and write iterators
- Add wrap_around() method to SequentialIter for cycling through IDs - Add wrap_around() method to WriteIter for claiming IDs indefinitely - WriteIter wrap_around clears bitmap each cycle to re-claim IDs - SequentialIter wrap_around restarts from id_min when reaching end - Add multi-threaded tests for both wrap_around modes - Update README.md with wrap_around examples and comparison table - Update INTEGRATION_GUIDE.md with wrap_around documentation and API reference Iterator behavior summary: - sequential().wrap_around(): cycles through matching IDs indefinitely - write().wrap_around(): clears bitmap and restarts when exhausted - random(): already cycles via sampling (no wrap needed)
1 parent 0008bc9 commit 0720c2b

3 files changed

Lines changed: 338 additions & 14 deletions

File tree

INTEGRATION_GUIDE.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,38 @@ Statistical distributions for realistic workload simulation:
226226
| **Sequential** | `.sequential()` | Yes (partitioned) | Cache warming, full scan |
227227
| **Random** | `.random()` | Yes (partitioned) | Random access patterns |
228228
| **Overlapping** | `.overlapping().random()` | Yes | Contention testing |
229-
| **Claim** | `.claim()` | Yes (atomic) | Exactly-once processing |
229+
| **Claim** | `.write()` / `.continue_write()` | Yes (atomic) | Exactly-once processing |
230+
231+
### Wrap-Around for Indefinite Iteration
232+
233+
By default, iterators stop when the keyspace is exhausted. For benchmarks requiring
234+
indefinite iteration, use `.wrap_around()`:
235+
236+
| Iterator | Default | With `.wrap_around()` |
237+
|----------|---------|----------------------|
238+
| `sequential()` | Stops at end | Cycles back to start |
239+
| `write()` | Stops when full | Clears bitmap, restarts |
240+
| `random()` | Samples with replacement | Already cycles (no wrap needed) |
241+
242+
```rust
243+
// Sequential read - cycles through existing keys forever
244+
let mut iter = tracker.iter().set_only().sequential().wrap_around();
245+
246+
// Write with wrap - clears and restarts when full
247+
let mut iter = tracker.iter().write().wrap_around();
248+
249+
// Multi-threaded write with wrap-around
250+
tracker.reset_write_cursor();
251+
let handles: Vec<_> = (0..num_workers).map(|_| {
252+
let t = tracker.clone();
253+
thread::spawn(move || {
254+
let mut iter = t.iter().continue_write().wrap_around();
255+
for _ in 0..requests_per_worker {
256+
iter.next().unwrap(); // Never returns None
257+
}
258+
})
259+
}).collect();
260+
```
230261

231262
### Partitioning Strategies
232263

@@ -786,6 +817,20 @@ impl<'a> TrackerIterBuilder<'a> {
786817
/// Use when multiple workers share cursor state.
787818
pub fn continue_write(self) -> WriteIter<'a>;
788819
}
820+
821+
/// SequentialIter methods
822+
impl SequentialIter<'a> {
823+
/// Enable wrap-around: when iteration reaches end, restart from beginning.
824+
/// Useful for benchmarks that read existing keys indefinitely.
825+
pub fn wrap_around(self) -> Self;
826+
}
827+
828+
/// WriteIter methods
829+
impl WriteIter<'a> {
830+
/// Enable wrap-around: when keyspace exhausted, reset cursor and clear
831+
/// bitmap to allow re-claiming IDs indefinitely.
832+
pub fn wrap_around(self) -> Self;
833+
}
789834
```
790835

791836
### BitmapSnapshot

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ for (id, _) in tracker.iter()
189189
{
190190
// ...
191191
}
192+
193+
// Wrap-around for indefinite iteration
194+
let mut iter = tracker.iter().set_only().sequential().wrap_around();
195+
for _ in 0..1_000_000 {
196+
let (id, _) = iter.next().unwrap(); // Cycles through set IDs forever
197+
}
192198
```
193199

194200
### Random Iterator
@@ -333,6 +339,43 @@ let all_claimed: Vec<u64> = handles.into_iter()
333339
| `write()` | Resets cursor to 0 | Single iterator, fresh start |
334340
| `continue_write()` | Keeps current position | Multiple workers sharing cursor |
335341

342+
#### Wrap-Around Mode
343+
344+
By default, iterators return `None` when the keyspace is exhausted. For benchmarks
345+
that need indefinite iteration, enable wrap-around on any iterator type:
346+
347+
```rust
348+
// Sequential wrap-around (read existing keys indefinitely)
349+
let mut iter = tracker.iter().set_only().sequential().wrap_around();
350+
for _ in 0..1_000_000 {
351+
let (id, _) = iter.next().unwrap(); // Cycles through set IDs
352+
}
353+
354+
// Write wrap-around (claim IDs indefinitely, clears bitmap each cycle)
355+
let mut iter = tracker.iter().write().wrap_around();
356+
for _ in 0..1_000_000 {
357+
let (id, _) = iter.next().unwrap(); // Cycles through 0..max_id
358+
}
359+
360+
// Multi-threaded wrap-around
361+
tracker.reset_write_cursor();
362+
let handles: Vec<_> = (0..8).map(|_| {
363+
let t = tracker.clone();
364+
thread::spawn(move || {
365+
let mut iter = t.iter().continue_write().wrap_around();
366+
for _ in 0..10_000 {
367+
iter.next().unwrap();
368+
}
369+
})
370+
}).collect();
371+
```
372+
373+
| Iterator | `wrap_around()` Behavior |
374+
|----------|-------------------------|
375+
| `sequential()` | Restarts from `id_min` when reaching end |
376+
| `write()` | Resets cursor, clears bitmap, restarts claiming |
377+
| `random()` | Already cycles via sampling (no wrap needed) |
378+
336379
### Delete Iterator
337380

338381
Exclusive iterator that clears IDs:

0 commit comments

Comments
 (0)