@@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex};
13
13
14
14
pub struct KeyValueAzureCosmos {
15
15
client : CollectionClient ,
16
+ app_id : String ,
16
17
}
17
18
18
19
/// Azure Cosmos Key / Value runtime config literal options for authentication
@@ -71,6 +72,7 @@ impl KeyValueAzureCosmos {
71
72
database : String ,
72
73
container : String ,
73
74
auth_options : KeyValueAzureCosmosAuthOptions ,
75
+ app_id : String ,
74
76
) -> Result < Self > {
75
77
let token = match auth_options {
76
78
KeyValueAzureCosmosAuthOptions :: RuntimeConfigValues ( config) => {
@@ -86,15 +88,16 @@ impl KeyValueAzureCosmos {
86
88
let database_client = cosmos_client. database_client ( database) ;
87
89
let client = database_client. collection_client ( container) ;
88
90
89
- Ok ( Self { client } )
91
+ Ok ( Self { client, app_id } )
90
92
}
91
93
}
92
94
93
95
#[ async_trait]
94
96
impl StoreManager for KeyValueAzureCosmos {
95
- async fn get ( & self , _name : & str ) -> Result < Arc < dyn Store > , Error > {
97
+ async fn get ( & self , name : & str ) -> Result < Arc < dyn Store > , Error > {
96
98
Ok ( Arc :: new ( AzureCosmosStore {
97
99
client : self . client . clone ( ) ,
100
+ partition_key : format ! ( "{}/{}" , self . app_id, name) ,
98
101
} ) )
99
102
}
100
103
@@ -114,13 +117,7 @@ impl StoreManager for KeyValueAzureCosmos {
114
117
#[ derive( Clone ) ]
115
118
struct AzureCosmosStore {
116
119
client : CollectionClient ,
117
- }
118
-
119
- struct CompareAndSwap {
120
- key : String ,
121
- client : CollectionClient ,
122
- bucket_rep : u32 ,
123
- etag : Mutex < Option < String > > ,
120
+ partition_key : String ,
124
121
}
125
122
126
123
#[ async_trait]
@@ -134,6 +131,7 @@ impl Store for AzureCosmosStore {
134
131
let pair = Pair {
135
132
id : key. to_string ( ) ,
136
133
value : value. to_vec ( ) ,
134
+ partition_key : self . partition_key . clone ( ) ,
137
135
} ;
138
136
self . client
139
137
. create_document ( pair)
@@ -145,7 +143,10 @@ impl Store for AzureCosmosStore {
145
143
146
144
async fn delete ( & self , key : & str ) -> Result < ( ) , Error > {
147
145
if self . exists ( key) . await ? {
148
- let document_client = self . client . document_client ( key, & key) . map_err ( log_error) ?;
146
+ let document_client = self
147
+ . client
148
+ . document_client ( key, & self . partition_key )
149
+ . map_err ( log_error) ?;
149
150
document_client. delete_document ( ) . await . map_err ( log_error) ?;
150
151
}
151
152
Ok ( ( ) )
@@ -165,7 +166,10 @@ impl Store for AzureCosmosStore {
165
166
. map ( |k| format ! ( "'{}'" , k) )
166
167
. collect :: < Vec < String > > ( )
167
168
. join ( ", " ) ;
168
- let stmt = Query :: new ( format ! ( "SELECT * FROM c WHERE c.id IN ({})" , in_clause) ) ;
169
+ let stmt = Query :: new ( format ! (
170
+ "SELECT * FROM c WHERE c.id IN ({}) AND partition_key='{}'" ,
171
+ in_clause, self . partition_key
172
+ ) ) ;
169
173
let query = self
170
174
. client
171
175
. query_documents ( stmt)
@@ -175,9 +179,11 @@ impl Store for AzureCosmosStore {
175
179
let mut stream = query. into_stream :: < Pair > ( ) ;
176
180
while let Some ( resp) = stream. next ( ) . await {
177
181
let resp = resp. map_err ( log_error) ?;
178
- for ( pair, _) in resp. results {
179
- res. push ( ( pair. id , Some ( pair. value ) ) ) ;
180
- }
182
+ res. extend (
183
+ resp. results
184
+ . into_iter ( )
185
+ . map ( |( pair, _) | ( pair. id , Some ( pair. value ) ) ) ,
186
+ ) ;
181
187
}
182
188
Ok ( res)
183
189
}
@@ -200,7 +206,7 @@ impl Store for AzureCosmosStore {
200
206
let operations = vec ! [ Operation :: incr( "/value" , delta) . map_err( log_error) ?] ;
201
207
let _ = self
202
208
. client
203
- . document_client ( key. clone ( ) , & key . as_str ( ) )
209
+ . document_client ( key. clone ( ) , & self . partition_key )
204
210
. map_err ( log_error) ?
205
211
. patch_document ( operations)
206
212
. await
@@ -227,10 +233,19 @@ impl Store for AzureCosmosStore {
227
233
client : self . client . clone ( ) ,
228
234
etag : Mutex :: new ( None ) ,
229
235
bucket_rep,
236
+ partition_key : self . partition_key . clone ( ) ,
230
237
} ) )
231
238
}
232
239
}
233
240
241
+ struct CompareAndSwap {
242
+ key : String ,
243
+ client : CollectionClient ,
244
+ bucket_rep : u32 ,
245
+ etag : Mutex < Option < String > > ,
246
+ partition_key : String ,
247
+ }
248
+
234
249
#[ async_trait]
235
250
impl Cas for CompareAndSwap {
236
251
/// `current` will fetch the current value for the key and store the etag for the record. The
@@ -239,8 +254,8 @@ impl Cas for CompareAndSwap {
239
254
let mut stream = self
240
255
. client
241
256
. query_documents ( Query :: new ( format ! (
242
- "SELECT * FROM c WHERE c.id='{}'" ,
243
- self . key
257
+ "SELECT * FROM c WHERE c.id='{}' and c.partition_key='{}' " ,
258
+ self . key, self . partition_key
244
259
) ) )
245
260
. query_cross_partition ( true )
246
261
. max_item_count ( 1 )
@@ -272,10 +287,11 @@ impl Cas for CompareAndSwap {
272
287
/// `swap` updates the value for the key using the etag saved in the `current` function for
273
288
/// optimistic concurrency.
274
289
async fn swap ( & self , value : Vec < u8 > ) -> Result < ( ) , SwapError > {
275
- let pk = PartitionKey :: from ( & self . key ) ;
290
+ let pk = PartitionKey :: from ( & self . partition_key ) ;
276
291
let pair = Pair {
277
292
id : self . key . clone ( ) ,
278
293
value,
294
+ partition_key : self . partition_key . clone ( ) ,
279
295
} ;
280
296
281
297
let doc_client = self
@@ -318,23 +334,23 @@ impl AzureCosmosStore {
318
334
async fn get_pair ( & self , key : & str ) -> Result < Option < Pair > , Error > {
319
335
let query = self
320
336
. client
321
- . query_documents ( Query :: new ( format ! ( "SELECT * FROM c WHERE c.id='{}'" , key) ) )
337
+ . query_documents ( Query :: new ( format ! (
338
+ "SELECT * FROM c WHERE c.id='{}' AND c.partition_key='{}'" ,
339
+ key, self . partition_key
340
+ ) ) )
322
341
. query_cross_partition ( true )
323
342
. max_item_count ( 1 ) ;
324
343
325
344
// There can be no duplicated keys, so we create the stream and only take the first result.
326
345
let mut stream = query. into_stream :: < Pair > ( ) ;
327
- let res = stream. next ( ) . await ;
328
- match res {
329
- Some ( r) => {
330
- let r = r. map_err ( log_error) ?;
331
- match r. results . first ( ) . cloned ( ) {
332
- Some ( ( p, _) ) => Ok ( Some ( p) ) ,
333
- None => Ok ( None ) ,
334
- }
335
- }
336
- None => Ok ( None ) ,
337
- }
346
+ let Some ( res) = stream. next ( ) . await else {
347
+ return Ok ( None ) ;
348
+ } ;
349
+ Ok ( res
350
+ . map_err ( log_error) ?
351
+ . results
352
+ . first ( )
353
+ . map ( |( p, _) | p. clone ( ) ) )
338
354
}
339
355
340
356
async fn get_keys ( & self ) -> Result < Vec < String > , Error > {
@@ -347,9 +363,7 @@ impl AzureCosmosStore {
347
363
let mut stream = query. into_stream :: < Pair > ( ) ;
348
364
while let Some ( resp) = stream. next ( ) . await {
349
365
let resp = resp. map_err ( log_error) ?;
350
- for ( pair, _) in resp. results {
351
- res. push ( pair. id ) ;
352
- }
366
+ res. extend ( resp. results . into_iter ( ) . map ( |( pair, _) | pair. id ) ) ;
353
367
}
354
368
355
369
Ok ( res)
@@ -358,15 +372,15 @@ impl AzureCosmosStore {
358
372
359
373
#[ derive( Serialize , Deserialize , Clone , Debug ) ]
360
374
pub struct Pair {
361
- // In Azure CosmosDB, the default partition key is "/id", and this implementation assumes that partition ID is not changed.
362
375
pub id : String ,
363
376
pub value : Vec < u8 > ,
377
+ pub partition_key : String ,
364
378
}
365
379
366
380
impl CosmosEntity for Pair {
367
381
type Entity = String ;
368
382
369
383
fn partition_key ( & self ) -> Self :: Entity {
370
- self . id . clone ( )
384
+ self . partition_key . clone ( )
371
385
}
372
386
}
0 commit comments