17
17
mod errors;
18
18
use std:: marker:: PhantomData ;
19
19
use std:: sync:: Arc ;
20
+ use std:: time:: Duration ;
20
21
21
22
use databend_common_meta_app:: schema:: CreateOption ;
22
23
use databend_common_meta_app:: tenant:: Tenant ;
@@ -36,6 +37,7 @@ use databend_common_meta_types::With;
36
37
use databend_common_proto_conv:: FromToProto ;
37
38
pub use errors:: CrudError ;
38
39
use futures:: TryStreamExt ;
40
+ use log:: info;
39
41
40
42
use crate :: kv_pb_api:: KVPbApi ;
41
43
use crate :: kv_pb_api:: UpsertPB ;
@@ -80,18 +82,42 @@ where
80
82
// As a kvapi::Key, the corresponding value contains a name.
81
83
ValueOf < R > : ValueWithName + FromToProto + Clone ,
82
84
{
85
+ /// Add a record.
86
+ ///
87
+ /// `create_option` specifies the behavior when the record already exists.
83
88
#[ async_backtrace:: framed]
84
89
#[ fastrace:: trace]
85
90
pub async fn add (
86
91
& self ,
87
92
value : ValueOf < R > ,
88
93
create_option : & CreateOption ,
94
+ ) -> Result < ( ) , CrudError < ExistError < R > > > {
95
+ self . add_with_ttl ( value, None , create_option) . await
96
+ }
97
+
98
+ /// Add a record with an optional time-to-live(TTL) argument.
99
+ ///
100
+ /// If `ttl` is `None`, the record will not expire.
101
+ /// `create_option` specifies the behavior when the record already exists.
102
+ #[ async_backtrace:: framed]
103
+ #[ fastrace:: trace]
104
+ pub async fn add_with_ttl (
105
+ & self ,
106
+ value : ValueOf < R > ,
107
+ ttl : Option < Duration > ,
108
+ create_option : & CreateOption ,
89
109
) -> Result < ( ) , CrudError < ExistError < R > > > {
90
110
let ident = self . ident ( value. name ( ) ) ;
91
111
92
112
let seq = MatchSeq :: from ( * create_option) ;
93
113
let upsert = UpsertPB :: insert ( ident, value. clone ( ) ) . with ( seq) ;
94
114
115
+ let upsert = if let Some ( ttl) = ttl {
116
+ upsert. with_ttl ( ttl)
117
+ } else {
118
+ upsert
119
+ } ;
120
+
95
121
let res = self . kv_api . upsert_pb ( & upsert) . await ?;
96
122
97
123
if let CreateOption :: Create = create_option {
@@ -109,15 +135,71 @@ where
109
135
& self ,
110
136
value : ValueOf < R > ,
111
137
match_seq : MatchSeq ,
138
+ ) -> Result < u64 , CrudError < UnknownError < R > > > {
139
+ self . update_with_ttl ( value, match_seq, None ) . await
140
+ }
141
+
142
+ /// Update a record with an optional time-to-live(TTL) argument.
143
+ ///
144
+ /// Returns the seq of the updated record.
145
+ /// If `ttl` is `None`, the record will not expire.
146
+ /// `match_seq` specifies what existing value will be overridden,
147
+ /// if the seq of existing value does not match the provided `match_seq`,
148
+ /// nothing will be done.
149
+ #[ async_backtrace:: framed]
150
+ #[ fastrace:: trace]
151
+ pub async fn update_with_ttl (
152
+ & self ,
153
+ value : ValueOf < R > ,
154
+ match_seq : MatchSeq ,
155
+ ttl : Option < Duration > ,
112
156
) -> Result < u64 , CrudError < UnknownError < R > > > {
113
157
let ident = self . ident ( value. name ( ) ) ;
114
158
let upsert = UpsertPB :: update ( ident, value. clone ( ) ) . with ( match_seq) ;
115
159
160
+ let upsert = if let Some ( ttl) = ttl {
161
+ upsert. with_ttl ( ttl)
162
+ } else {
163
+ upsert
164
+ } ;
165
+
116
166
let res = self . kv_api . upsert_pb ( & upsert) . await ?;
117
167
118
- match res. result {
119
- Some ( SeqV { seq, .. } ) => Ok ( seq) ,
120
- None => Err ( UnknownError :: new ( value. name ( ) , "NotFound when update" ) . into ( ) ) ,
168
+ if res. is_changed ( ) {
169
+ Ok ( res. result . seq ( ) )
170
+ } else {
171
+ Err ( UnknownError :: new ( value. name ( ) , match_seq, "NotFound when update" ) . into ( ) )
172
+ }
173
+ }
174
+
175
+ /// Fetch the record with the given `name`, update it with the provided function `update`,
176
+ /// then save it back if the seq number did not change.
177
+ ///
178
+ /// The `seq` is the initial seq number to fetch the record.
179
+ #[ async_backtrace:: framed]
180
+ #[ fastrace:: trace]
181
+ pub async fn cas_with (
182
+ & self ,
183
+ name : & str ,
184
+ seq : MatchSeq ,
185
+ update : impl Fn ( SeqV < ValueOf < R > > ) -> ValueOf < R > + Send ,
186
+ ) -> Result < u64 , CrudError < UnknownError < R > > > {
187
+ loop {
188
+ let seq_v = self . get ( name, seq) . await ?;
189
+
190
+ let seq = seq_v. seq ;
191
+ let new_value = update ( seq_v) ;
192
+
193
+ let ident = self . ident ( name) ;
194
+ let upsert = UpsertPB :: update ( ident, new_value) . with ( MatchSeq :: Exact ( seq) ) ;
195
+
196
+ let res = self . kv_api . upsert_pb ( & upsert) . await ?;
197
+
198
+ if res. is_changed ( ) {
199
+ return Ok ( res. result . seq ( ) ) ;
200
+ } else {
201
+ info ! ( "cas: retrying, name: {}, seq: {}" , name, seq) ;
202
+ }
121
203
}
122
204
}
123
205
@@ -136,6 +218,7 @@ where
136
218
res. removed_or_else ( |e| {
137
219
UnknownError :: new (
138
220
name,
221
+ seq,
139
222
format_args ! ( "NotFound when remove, seq of existing record: {}" , e. seq( ) ) ,
140
223
)
141
224
} ) ?;
@@ -154,11 +237,13 @@ where
154
237
155
238
let res = self . kv_api . get_pb ( & ident) . await ?;
156
239
157
- let seq_value = res. ok_or_else ( || UnknownError :: new ( name, "NotFound when get" ) ) ?;
240
+ let seq_value = res. ok_or_else ( || UnknownError :: new ( name, seq , "NotFound when get" ) ) ?;
158
241
159
242
match seq. match_seq ( & seq_value) {
160
243
Ok ( _) => Ok ( seq_value) ,
161
- Err ( e) => Err ( UnknownError :: new ( name, format_args ! ( "NotFound when get: {}" , e) ) . into ( ) ) ,
244
+ Err ( e) => {
245
+ Err ( UnknownError :: new ( name, seq, format_args ! ( "NotFound when get: {}" , e) ) . into ( ) )
246
+ }
162
247
}
163
248
}
164
249
0 commit comments