pub struct Cursor(/* private fields */);Expand description
Where a scan stopped, as the client sees it.
Opaque to the client, and deliberately so, but not opaque in here: it is a
partition count, a partition, and a row index, packed the way 08 §4 names
them.
63 52 51 40 39 0
+--------+--------+--------------------------------------+
| P | part | idx + 1 |
+--------+--------+--------------------------------------+Zero is both the start and the end, which is Redis’s convention and is unambiguous here because a real cursor always names a partition count and a partition count is never zero.
An idx + 1 of zero is not a row, it means the top of that partition,
whatever its length turns out to be. A resume needs to be able to say that
without knowing how long the partition is, because Cursor::rebase moves a
cursor into a partition it has never looked at.
Implementations§
Source§impl Cursor
impl Cursor
Sourcepub const END: Cursor
pub const END: Cursor
Nothing left. The same value as Cursor::START, which is what the
protocol says and what every Redis client already loops on.
Sourcepub const fn at(parts: u32, part: u32, idx: u64) -> Cursor
pub const fn at(parts: u32, part: u32, idx: u64) -> Cursor
Resume at a row.
parts and part are clamped rather than rejected. A client can send
any number back and Redis answers all of them, so a cursor that names a
partition that does not exist has to mean something sane rather than be
an error.
Sourcepub const fn top(parts: u32, part: u32) -> Cursor
pub const fn top(parts: u32, part: u32) -> Cursor
Resume at the top of a partition, without saying how long it is.
Sourcepub const fn parts(self) -> u32
pub const fn parts(self) -> u32
How many partitions the collection had when this was issued.
One for a cursor that has not been anywhere yet, which is also the truth for every collection below the partitioned band.
Sourcepub const fn idx(self) -> Option<u64>
pub const fn idx(self) -> Option<u64>
The next row to read, or None for the top of the partition.
Sourcepub const fn rebase(self, parts_now: u32) -> Cursor
pub const fn rebase(self, parts_now: u32) -> Cursor
Move a cursor into the layout the collection has now.
Growing from P to some larger power of two splits every partition and
moves nothing between the halves, because a member’s partition is the low
bits of its hash and growing only reads more of them. So an old partition
part becomes the new partitions whose low log2(P) bits are part, and
every new partition whose low bits are above part has already been
walked in full.
Resuming at the top of the highest new partition with those low bits covers all of the work that is left, and walking down from there also passes back over some partitions that were already done. That is duplicate work and duplicates are allowed. What it never does is skip one, and it never restarts the whole scan either, which is the other easy answer and the one that turns a growth into a full second pass.
The row index is dropped rather than carried across. A split redistributes the rows, so an index into the old partition’s array names a different member in the new one, and resuming at the top of the partition it stopped in is the only thing that can be said honestly.
Shrinking is the other direction and is not something the size ladder does under a live scan, so a cursor from a larger layout is answered by starting the current one at the top. A repeat is allowed. A miss is not.