17
17
use std:: collections:: BTreeMap ;
18
18
use std:: fmt:: Debug ;
19
19
use std:: fmt:: Display ;
20
+ use std:: sync:: atomic:: AtomicU64 ;
21
+ use std:: sync:: atomic:: Ordering ;
20
22
use std:: sync:: Arc ;
21
23
use std:: time:: Duration ;
22
24
use std:: time:: Instant ;
@@ -55,6 +57,7 @@ use databend_common_tracing::LogFormat;
55
57
use databend_common_tracing:: StderrConfig ;
56
58
use databend_common_version:: BUILD_INFO ;
57
59
use databend_common_version:: METASRV_COMMIT_VERSION ;
60
+ use futures:: TryStreamExt ;
58
61
use serde:: Deserialize ;
59
62
use serde:: Serialize ;
60
63
use tokio:: time:: sleep;
@@ -84,6 +87,12 @@ struct Config {
84
87
/// "get_table": single get_table() rpc;
85
88
/// "table_copy_file": upsert table with copy file.
86
89
/// "table_copy_file:{"file_cnt":100}": upsert table with 100 copy files. After ":" is a json config string
90
+ /// "list": list all keys with default prefix pattern;
91
+ /// "list:{"limit":50}": list up to 50 keys with default prefix pattern;
92
+ /// "list:{"prefix":"custom_prefix"}": list all keys with custom prefix;
93
+ /// "list:{"prefix":"custom_prefix","limit":50}": list up to 50 keys with custom prefix;
94
+ /// "list:{"interval_ms":100}": add 100ms delay between reading each item (slow client simulation);
95
+ /// "list:{"prefix":"custom_prefix","limit":50,"interval_ms":100}": combine all options;
87
96
#[ clap( long, default_value = "upsert_kv" ) ]
88
97
pub rpc : String ,
89
98
}
@@ -117,39 +126,33 @@ async fn main() {
117
126
return ;
118
127
}
119
128
129
+ let client = MetaGrpcClient :: try_create_with_features (
130
+ vec ! [ config. grpc_api_address. clone( ) ] ,
131
+ & BUILD_INFO ,
132
+ "root" ,
133
+ "xxx" ,
134
+ None ,
135
+ None ,
136
+ None ,
137
+ required:: read_write ( ) ,
138
+ )
139
+ . unwrap ( ) ;
140
+
120
141
let start = Instant :: now ( ) ;
121
142
let mut client_num = 0 ;
122
143
let mut handles = Vec :: new ( ) ;
123
144
while client_num < config. client {
124
145
client_num += 1 ;
125
- let addr = config. grpc_api_address . clone ( ) ;
126
146
let rpc = config. rpc . clone ( ) ;
127
147
let prefix = config. prefix ;
128
148
129
149
let cmd_and_param = rpc. splitn ( 2 , ':' ) . collect :: < Vec < _ > > ( ) ;
130
150
let cmd = cmd_and_param[ 0 ] . to_string ( ) ;
131
151
let param = cmd_and_param. get ( 1 ) . unwrap_or ( & "" ) . to_string ( ) ;
132
152
133
- let handle = runtime:: spawn ( async move {
134
- let client = MetaGrpcClient :: try_create_with_features (
135
- vec ! [ addr. to_string( ) ] ,
136
- & BUILD_INFO ,
137
- "root" ,
138
- "xxx" ,
139
- None ,
140
- None ,
141
- None ,
142
- required:: read_write ( ) ,
143
- ) ;
144
-
145
- let client = match client {
146
- Ok ( client) => client,
147
- Err ( e) => {
148
- eprintln ! ( "Failed to create client: {}" , e) ;
149
- return ;
150
- }
151
- } ;
153
+ let client = client. clone ( ) ;
152
154
155
+ let handle = runtime:: spawn ( async move {
153
156
for i in 0 ..config. number {
154
157
if cmd == "upsert_kv" {
155
158
benchmark_upsert ( & client, prefix, client_num, i) . await ;
@@ -161,6 +164,8 @@ async fn main() {
161
164
benchmark_table_copy_file ( & client, prefix, client_num, i, & param) . await ;
162
165
} else if cmd == "semaphore" {
163
166
benchmark_semaphore ( & client, prefix, client_num, i, & param) . await ;
167
+ } else if cmd == "list" {
168
+ benchmark_list ( & client, prefix, client_num, i, & param) . await ;
164
169
} else {
165
170
unreachable ! ( "Invalid config.rpc: {}" , rpc) ;
166
171
}
@@ -447,6 +452,93 @@ async fn benchmark_semaphore(
447
452
}
448
453
}
449
454
455
+ #[ derive( Clone , Debug , Serialize , Deserialize , PartialEq , Default ) ]
456
+ struct ListConfig {
457
+ /// Maximum number of keys to return in the list operation.
458
+ /// If None, all matching keys are returned.
459
+ limit : Option < usize > ,
460
+ /// The prefix to search for. If None, uses the default pattern "{prefix}-{client_num}".
461
+ prefix : Option < String > ,
462
+ /// Interval in milliseconds to wait between reading each item from the stream.
463
+ /// This simulates a slow client. If None or 0, no delay is added.
464
+ interval_ms : Option < u64 > ,
465
+ }
466
+
467
+ /// Benchmark listing keys with a prefix.
468
+ async fn benchmark_list (
469
+ client : & Arc < ClientHandle > ,
470
+ prefix : u64 ,
471
+ client_num : u64 ,
472
+ i : u64 ,
473
+ param : & str ,
474
+ ) {
475
+ let name = format ! ( "client[{:>05}]-{}th" , client_num, i) ;
476
+
477
+ let config = if param. is_empty ( ) {
478
+ ListConfig :: default ( )
479
+ } else {
480
+ serde_json:: from_str ( param) . unwrap ( )
481
+ } ;
482
+
483
+ let key_prefix = config
484
+ . prefix
485
+ . clone ( )
486
+ . unwrap_or_else ( || format ! ( "{}-{}" , prefix, client_num) ) ;
487
+
488
+ if i % 100 == 0 {
489
+ println ! ( "{:>10}-th list using prefix: '{}'" , name, key_prefix) ;
490
+ }
491
+
492
+ let start_time = Instant :: now ( ) ;
493
+ let stream_res = client. list ( & key_prefix) . await ;
494
+ let stream_returned_time = Instant :: now ( ) ;
495
+
496
+ static TOTAL : AtomicU64 = AtomicU64 :: new ( 0 ) ;
497
+ TOTAL . fetch_add ( 1 , Ordering :: Relaxed ) ;
498
+
499
+ println ! (
500
+ "{:>10}-th list stream returned in {:?}, err: {:?}, total streams: {}" ,
501
+ name,
502
+ stream_returned_time. duration_since( start_time) ,
503
+ stream_res. as_ref( ) . err( ) ,
504
+ TOTAL . load( Ordering :: Relaxed )
505
+ ) ;
506
+
507
+ let mut strm = match stream_res {
508
+ Ok ( stream) => stream,
509
+ Err ( e) => {
510
+ println ! ( "{:>10}-th list error: {:?}" , name, e) ;
511
+ return ;
512
+ }
513
+ } ;
514
+
515
+ let mut count = 0 ;
516
+
517
+ while let Ok ( Some ( _item) ) = strm. try_next ( ) . await {
518
+ count += 1 ;
519
+
520
+ // Apply interval delay if specified (simulate slow client)
521
+ if let Some ( interval_ms) = config. interval_ms {
522
+ if interval_ms > 0 {
523
+ sleep ( Duration :: from_millis ( interval_ms) ) . await ;
524
+ }
525
+ }
526
+
527
+ // Apply limit if specified
528
+ if let Some ( limit) = config. limit {
529
+ if count >= limit {
530
+ break ;
531
+ }
532
+ }
533
+
534
+ if count % 10 == 0 {
535
+ println ! ( "{:>10}-th list found {} keys" , name, count) ;
536
+ }
537
+ }
538
+
539
+ println ! ( "{:>10}-th list found {} keys" , name, count) ;
540
+ }
541
+
450
542
fn print_res < D : Debug > ( i : u64 , typ : impl Display , res : & D ) {
451
543
if i % 100 == 0 {
452
544
println ! ( "{:>10}-th {} result: {:?}" , i, typ, res) ;
0 commit comments