Skip to content

Commit 4630e59

Browse files
committed
fix(actor-kv): fix listing keys
1 parent c971e5f commit 4630e59

File tree

6 files changed

+729
-11
lines changed

6 files changed

+729
-11
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

engine/packages/actor-kv/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@ tracing.workspace = true
2020
universaldb.workspace = true
2121

2222
pegboard.workspace = true
23+
24+
[dev-dependencies]
25+
portpicker.workspace = true
26+
rivet-config.workspace = true
27+
rivet-test-deps.workspace = true
28+
tokio.workspace = true
29+
tracing-subscriber.workspace = true
30+
url.workspace = true
31+
uuid.workspace = true

engine/packages/actor-kv/src/key.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ use universaldb::tuple::{
33
Bytes, PackResult, TupleDepth, TuplePack, TupleUnpack, VersionstampOffset,
44
};
55

6+
/// Wraps a key with a trailing NIL byte for exact key matching.
7+
///
8+
/// Encodes as: `[NESTED, ...bytes..., NIL]`
9+
///
10+
/// Use this for:
11+
/// - Storing keys
12+
/// - Getting/deleting specific keys
13+
/// - Range query end points (to create closed boundaries)
614
#[derive(Debug, Clone, PartialEq)]
715
pub struct KeyWrapper(pub rp::KvKey);
816

@@ -44,7 +52,13 @@ impl<'de> TupleUnpack<'de> for KeyWrapper {
4452
}
4553
}
4654

47-
/// Same as Key: except when packing, it leaves off the NIL byte to allow for an open range.
55+
/// Wraps a key without a trailing NIL byte for prefix/range matching.
56+
///
57+
/// Encodes as: `[NESTED, ...bytes...]` (no trailing NIL)
58+
///
59+
/// Use this for:
60+
/// - Range query start points (to create open boundaries)
61+
/// - Prefix queries (to match all keys starting with these bytes)
4862
pub struct ListKeyWrapper(pub rp::KvKey);
4963

5064
impl TuplePack for ListKeyWrapper {

engine/packages/actor-kv/src/lib.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,18 @@ pub async fn list(
172172

173173
let curr = if let Some(inner) = &mut current_entry {
174174
if inner.key != key {
175+
// Check limit before adding the key
176+
if keys.len() >= limit {
177+
current_entry = None;
178+
break;
179+
}
180+
175181
let (key, value, meta) =
176182
std::mem::replace(inner, EntryBuilder::new(key)).build()?;
177183

178184
keys.push(key);
179185
values.push(value);
180186
metadata.push(meta);
181-
182-
if keys.len() >= limit {
183-
current_entry = None;
184-
break;
185-
}
186187
}
187188

188189
inner
@@ -203,12 +204,15 @@ pub async fn list(
203204
}
204205
}
205206

207+
// Only add the current entry if we haven't hit the limit yet
206208
if let Some(inner) = current_entry {
207-
let (key, value, meta) = inner.build()?;
209+
if keys.len() < limit {
210+
let (key, value, meta) = inner.build()?;
208211

209-
keys.push(key);
210-
values.push(value);
211-
metadata.push(meta);
212+
keys.push(key);
213+
values.push(value);
214+
metadata.push(meta);
215+
}
212216
}
213217

214218
Ok((keys, values, metadata))
@@ -330,7 +334,24 @@ fn list_query_range(query: rp::KvListQuery, subspace: &Subspace) -> (Vec<u8>, Ve
330334
},
331335
),
332336
rp::KvListQuery::KvListPrefixQuery(prefix) => {
333-
subspace.subspace(&KeyWrapper(prefix.key)).range()
337+
// For prefix queries, we need to create a range that matches all keys
338+
// that start with the given prefix bytes. The tuple encoding adds a
339+
// terminating 0 byte to strings, which would make the range too narrow.
340+
//
341+
// Instead, we construct the range manually:
342+
// - Start: the prefix bytes within the subspace
343+
// - End: the prefix bytes + 0xFF (next possible byte)
344+
345+
let mut start = subspace.pack(&ListKeyWrapper(prefix.key.clone()));
346+
// Remove the trailing 0 byte that tuple encoding adds to strings
347+
if let Some(&0) = start.last() {
348+
start.pop();
349+
}
350+
351+
let mut end = start.clone();
352+
end.push(0xFF);
353+
354+
(start, end)
334355
}
335356
}
336357
}

0 commit comments

Comments
 (0)