Skip to content

Commit 890a557

Browse files
committed
Add version and more details on count implementation
1 parent 52a674f commit 890a557

1 file changed

Lines changed: 22 additions & 9 deletions

File tree

open-log/rfcs/0001-storage.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ Logs are stored in SlateDB's LSM tree. Writes are appended to the WAL and memtab
3131

3232
### Key Encoding
3333

34-
SlateDB keys are a composite of the user key and a `u64` sequence number. A leading byte discriminator distinguishes entry types.
34+
SlateDB keys are a composite of the user key and a `u64` sequence number. A version prefix and record type discriminator provide forward compatibility.
3535

3636
```
3737
Log Entry:
38-
SlateDB Key: | 0x01 | key (bytes) | sequence (u64) |
38+
SlateDB Key: | version (u8) | type (u8) | key (bytes) | sequence (u64) |
3939
SlateDB Value: | record value (bytes) |
4040
```
4141

42-
The `0x01` discriminator is reserved for log entries. Additional record types (e.g., metadata, indexes) may be introduced in future RFCs using different discriminators.
42+
The initial version is `1`. The type discriminator `0x01` is reserved for log entries. Additional record types (e.g., metadata, indexes) may be introduced in future RFCs using different discriminators.
4343

4444
This encoding preserves lexicographic key ordering, enabling key-range scans. Entries for the same key are ordered by sequence number.
4545

@@ -55,10 +55,13 @@ In practice, users are likely to use fixed-length keys, which avoids this issue
5555

5656
### Sequence Numbers
5757

58-
Sequence numbers are assigned from a global counter that increments on every append. Each key's log is monotonically ordered by sequence number, but the sequence numbers are not contiguous—other keys' appends are interleaved in the global sequence.
58+
Sequence numbers are assigned from a single counter that is maintained by the SlateDB writer and is incremented after every append. Each key's log is monotonically ordered by sequence number, but the sequence numbers are not contiguous—other keys' appends are interleaved in the global sequence.
5959

6060
This approach simplifies ingestion by avoiding per-key sequence tracking. The trade-off is that sequence numbers do not reflect the count of entries within a key's log.
6161

62+
If SlateDB supports multi-writer in the future, each writer would maintain its own sequence counter. This design assumes each key would still have a single writer—interleaving appends from multiple writers to the same key would break monotonic ordering within that key's log.
63+
64+
6265
### Write API
6366

6467
The write API mirrors SlateDB's `write` API. The only supported operation is `append`.
@@ -107,16 +110,26 @@ impl OpenLog {
107110

108111
### Lag and Count (under consideration)
109112

110-
Without contiguous sequence numbers, computing lag requires additional bookkeeping. The approach under consideration augments SlateDB data structures:
113+
Lag is a critical metric for tracking progress reading from a log. Without contiguous sequence numbers, computing lag requires additional bookkeeping. The approach under consideration augments SlateDB's SST index structure.
114+
115+
Each block entry in the SST index would include a cumulative record count:
111116

112-
- Each SST tracks the number of records it contains
113-
- Each SST provides the relative index of a given entry within it
117+
```
118+
Block Entry: | block_offset | cumulative_record_count | first_key |
119+
```
114120

115-
With this metadata, walking the LSM tree at the metadata level can compute the number of records within a given key/sequence range without scanning all entries.
121+
To count records in a range, we scan the LSM at the index level rather than reading all entries. However, block boundaries may not align with the query range. Within each level of the LSM that overlaps our target range, we may need to read the first and last blocks from that range to determine the exact offset relative to block boundaries.
122+
123+
An approximate count could be offered based on the index alone without reading any blocks—useful when exact counts are not required.
116124

117125
```rust
126+
// TODO: decide which SlateDB ScanOptions parameters to pass through
127+
struct CountOptions {
128+
approximate: bool,
129+
}
130+
118131
impl OpenLog {
119-
async fn count(&self, key: Bytes, seq_range: impl RangeBounds<u64>) -> Result<u64, Error>;
132+
async fn count(&self, key: Bytes, seq_range: impl RangeBounds<u64>, options: CountOptions) -> Result<u64, Error>;
120133
}
121134
```
122135

0 commit comments

Comments
 (0)