@@ -24,6 +24,8 @@ const MAX_RAW_KV_SCAN_LIMIT: u32 = 10240;
2424pub struct Client {
2525 rpc : Arc < PdRpcClient > ,
2626 cf : Option < ColumnFamily > ,
27+ /// Whether to use the [`atomic mode`](Client::with_atomic_for_cas).
28+ atomic : bool ,
2729}
2830
2931impl Client {
@@ -63,7 +65,11 @@ impl Client {
6365 ) -> Result < Client > {
6466 let pd_endpoints: Vec < String > = pd_endpoints. into_iter ( ) . map ( Into :: into) . collect ( ) ;
6567 let rpc = Arc :: new ( PdRpcClient :: connect ( & pd_endpoints, & config, false ) . await ?) ;
66- Ok ( Client { rpc, cf : None } )
68+ Ok ( Client {
69+ rpc,
70+ cf : None ,
71+ atomic : false ,
72+ } )
6773 }
6874
6975 /// Set the column family of requests.
@@ -89,6 +95,22 @@ impl Client {
8995 Client {
9096 rpc : self . rpc . clone ( ) ,
9197 cf : Some ( cf) ,
98+ atomic : self . atomic ,
99+ }
100+ }
101+
102+ /// Set to use the atomic mode.
103+ ///
104+ /// The only reason of using atomic mode is the
105+ /// [`compare_and_swap`](Client::compare_and_swap) operation. To guarantee
106+ /// the atomicity of CAS, write operations like [`put`](Client::put) or
107+ /// [`delete`](Client::delete) in atomic mode are more expensive. Some
108+ /// operations are not supported in the mode.
109+ pub fn with_atomic_for_cas ( & self ) -> Client {
110+ Client {
111+ rpc : self . rpc . clone ( ) ,
112+ cf : self . cf . clone ( ) ,
113+ atomic : true ,
92114 }
93115 }
94116
@@ -171,7 +193,7 @@ impl Client {
171193 /// # });
172194 /// ```
173195 pub async fn put ( & self , key : impl Into < Key > , value : impl Into < Value > ) -> Result < ( ) > {
174- let request = new_raw_put_request ( key. into ( ) , value. into ( ) , self . cf . clone ( ) ) ;
196+ let request = new_raw_put_request ( key. into ( ) , value. into ( ) , self . cf . clone ( ) , self . atomic ) ;
175197 let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , request)
176198 . single_region ( )
177199 . await ?
@@ -203,7 +225,11 @@ impl Client {
203225 & self ,
204226 pairs : impl IntoIterator < Item = impl Into < KvPair > > ,
205227 ) -> Result < ( ) > {
206- let request = new_raw_batch_put_request ( pairs. into_iter ( ) . map ( Into :: into) , self . cf . clone ( ) ) ;
228+ let request = new_raw_batch_put_request (
229+ pairs. into_iter ( ) . map ( Into :: into) ,
230+ self . cf . clone ( ) ,
231+ self . atomic ,
232+ ) ;
207233 let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , request)
208234 . multi_region ( )
209235 . retry_region ( DEFAULT_REGION_BACKOFF )
@@ -231,7 +257,7 @@ impl Client {
231257 /// # });
232258 /// ```
233259 pub async fn delete ( & self , key : impl Into < Key > ) -> Result < ( ) > {
234- let request = new_raw_delete_request ( key. into ( ) , self . cf . clone ( ) ) ;
260+ let request = new_raw_delete_request ( key. into ( ) , self . cf . clone ( ) , self . atomic ) ;
235261 let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , request)
236262 . single_region ( )
237263 . await ?
@@ -260,6 +286,7 @@ impl Client {
260286 /// # });
261287 /// ```
262288 pub async fn batch_delete ( & self , keys : impl IntoIterator < Item = impl Into < Key > > ) -> Result < ( ) > {
289+ self . assert_non_atomic ( ) ?;
263290 let request =
264291 new_raw_batch_delete_request ( keys. into_iter ( ) . map ( Into :: into) , self . cf . clone ( ) ) ;
265292 let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , request)
@@ -287,6 +314,7 @@ impl Client {
287314 /// # });
288315 /// ```
289316 pub async fn delete_range ( & self , range : impl Into < BoundRange > ) -> Result < ( ) > {
317+ self . assert_non_atomic ( ) ?;
290318 let request = new_raw_delete_range_request ( range. into ( ) , self . cf . clone ( ) ) ;
291319 let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , request)
292320 . multi_region ( )
@@ -415,6 +443,40 @@ impl Client {
415443 . collect ( ) )
416444 }
417445
446+ /// Create a new *atomic* 'compare and set' request.
447+ ///
448+ /// Once resolved this request will result in an atomic `compare and set'
449+ /// operation for the given key.
450+ ///
451+ /// If the value retrived is equal to `current_value`, `new_value` is
452+ /// written.
453+ ///
454+ /// # Return Value
455+ ///
456+ /// A tuple is returned if successful: the previous value and whether the
457+ /// value is swapped
458+ pub async fn compare_and_swap (
459+ & self ,
460+ key : impl Into < Key > ,
461+ previous_value : impl Into < Option < Value > > ,
462+ new_value : impl Into < Value > ,
463+ ) -> Result < ( Option < Value > , bool ) > {
464+ self . assert_atomic ( ) ?;
465+ let req = new_cas_request (
466+ key. into ( ) ,
467+ new_value. into ( ) ,
468+ previous_value. into ( ) ,
469+ self . cf . clone ( ) ,
470+ ) ;
471+ let plan = crate :: request:: PlanBuilder :: new ( self . rpc . clone ( ) , req)
472+ . single_region ( )
473+ . await ?
474+ . retry_region ( DEFAULT_REGION_BACKOFF )
475+ . post_process_default ( )
476+ . plan ( ) ;
477+ plan. execute ( ) . await
478+ }
479+
418480 async fn scan_inner (
419481 & self ,
420482 range : impl Into < BoundRange > ,
@@ -467,4 +529,12 @@ impl Client {
467529 . plan ( ) ;
468530 plan. execute ( ) . await
469531 }
532+
533+ fn assert_non_atomic ( & self ) -> Result < ( ) > {
534+ ( !self . atomic ) . then ( || ( ) ) . ok_or ( Error :: UnsupportedMode )
535+ }
536+
537+ fn assert_atomic ( & self ) -> Result < ( ) > {
538+ self . atomic . then ( || ( ) ) . ok_or ( Error :: UnsupportedMode )
539+ }
470540}
0 commit comments