Skip to content

Commit 7d3c3bc

Browse files
authored
fix: when listing-kv, limit the right bound if possible (#15372)
1 parent 57b30a9 commit 7d3c3bc

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

src/meta/raft-store/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ pub mod ondisk;
2828
pub mod sm_v002;
2929
pub mod state;
3030
pub mod state_machine;
31+
pub mod utils;

src/meta/raft-store/src/sm_v002/leveled_store/level.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl MapApiRO<String> for Level {
112112

113113
if vec.len() > 1000 {
114114
warn!(
115-
"Level::<ExpireKey>::range() returns big range of len={}",
115+
"Level::<String>::range() returns big range of len={}",
116116
vec.len()
117117
);
118118
}

src/meta/raft-store/src/sm_v002/sm_v002.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ use crate::sm_v002::SnapshotViewV002;
6262
use crate::state_machine::sm::BlockingConfig;
6363
use crate::state_machine::ExpireKey;
6464
use crate::state_machine::StateMachineSubscriber;
65+
use crate::utils::prefix_right_bound;
6566

6667
/// A wrapper that implements KVApi **readonly** methods for the state machine.
6768
pub struct SMV002KVApi<'a> {
@@ -290,7 +291,11 @@ impl SMV002 {
290291
pub async fn list_kv(&self, prefix: &str) -> Result<ResultStream<(String, SeqV)>, io::Error> {
291292
let p = prefix.to_string();
292293

293-
let strm = self.levels.str_map().range(p.clone()..).await?;
294+
let strm = if let Some(right) = prefix_right_bound(&p) {
295+
self.levels.str_map().range(p.clone()..right).await?
296+
} else {
297+
self.levels.str_map().range(p.clone()..).await?
298+
};
294299

295300
let strm = strm
296301
// Return only keys with the expected prefix

src/meta/raft-store/src/utils.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2021 Datafuse Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// Return the right bound of the prefix, so that `p..right` will cover all strings with prefix `p`.
16+
pub fn prefix_right_bound(p: &str) -> Option<String> {
17+
let last = p.chars().last()?;
18+
let mut next_str = p[..p.len() - last.len_utf8()].to_owned();
19+
let next_char = char::from_u32(last as u32 + 1)?;
20+
next_str.push(next_char);
21+
Some(next_str)
22+
}
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use super::prefix_right_bound;
27+
28+
#[test]
29+
fn test_next_string() {
30+
assert_eq!(None, prefix_right_bound(""));
31+
assert_eq!(Some(s("b")), prefix_right_bound("a"));
32+
assert_eq!(Some(s("{")), prefix_right_bound("z"));
33+
assert_eq!(Some(s("foo0")), prefix_right_bound("foo/"));
34+
assert_eq!(Some(s("foo💰")), prefix_right_bound("foo💯"));
35+
}
36+
37+
fn s(s: impl ToString) -> String {
38+
s.to_string()
39+
}
40+
}

0 commit comments

Comments
 (0)