1
1
use anyhow:: Result ;
2
2
use azure_data_cosmos:: prelude:: Operation ;
3
- use azure_data_cosmos:: resources:: collection:: PartitionKey ;
4
3
use azure_data_cosmos:: {
5
4
prelude:: { AuthorizationToken , CollectionClient , CosmosClient , Query } ,
6
5
CosmosEntity ,
@@ -13,6 +12,11 @@ use std::sync::{Arc, Mutex};
13
12
14
13
pub struct KeyValueAzureCosmos {
15
14
client : CollectionClient ,
15
+ /// An optional app id
16
+ ///
17
+ /// If provided, the store will handle multiple stores per container using a
18
+ /// partition key of `/$app_id/$store_name`, otherwise there will be one container
19
+ /// per store, and the partition key will be `/id`.
16
20
app_id : Option < String > ,
17
21
}
18
22
@@ -97,7 +101,7 @@ impl StoreManager for KeyValueAzureCosmos {
97
101
async fn get ( & self , name : & str ) -> Result < Arc < dyn Store > , Error > {
98
102
Ok ( Arc :: new ( AzureCosmosStore {
99
103
client : self . client . clone ( ) ,
100
- partition_key : self . app_id . as_ref ( ) . map ( |i| format ! ( "{i}/{name}" ) ) ,
104
+ store_id : self . app_id . as_ref ( ) . map ( |i| format ! ( "{i}/{name}" ) ) ,
101
105
} ) )
102
106
}
103
107
@@ -117,10 +121,10 @@ impl StoreManager for KeyValueAzureCosmos {
117
121
#[ derive( Clone ) ]
118
122
struct AzureCosmosStore {
119
123
client : CollectionClient ,
120
- /// An optional partition key to use for all operations.
124
+ /// An optional store id to use as a partition key for all operations.
121
125
///
122
- /// If the partition key is not set, the store will use `/id` as the partition key.
123
- partition_key : Option < String > ,
126
+ /// If the store id not set, the store will use `/id` as the partition key.
127
+ store_id : Option < String > ,
124
128
}
125
129
126
130
#[ async_trait]
@@ -134,7 +138,7 @@ impl Store for AzureCosmosStore {
134
138
let pair = Pair {
135
139
id : key. to_string ( ) ,
136
140
value : value. to_vec ( ) ,
137
- partition_key : self . partition_key . clone ( ) ,
141
+ store_id : self . store_id . clone ( ) ,
138
142
} ;
139
143
self . client
140
144
. create_document ( pair)
@@ -148,7 +152,7 @@ impl Store for AzureCosmosStore {
148
152
if self . exists ( key) . await ? {
149
153
let document_client = self
150
154
. client
151
- . document_client ( key, & self . partition_key )
155
+ . document_client ( key, & self . store_id )
152
156
. map_err ( log_error) ?;
153
157
document_client. delete_document ( ) . await . map_err ( log_error) ?;
154
158
}
@@ -201,7 +205,7 @@ impl Store for AzureCosmosStore {
201
205
let operations = vec ! [ Operation :: incr( "/value" , delta) . map_err( log_error) ?] ;
202
206
let _ = self
203
207
. client
204
- . document_client ( key. clone ( ) , & self . partition_key )
208
+ . document_client ( key. clone ( ) , & self . store_id )
205
209
. map_err ( log_error) ?
206
210
. patch_document ( operations)
207
211
. await
@@ -228,7 +232,7 @@ impl Store for AzureCosmosStore {
228
232
client : self . client . clone ( ) ,
229
233
etag : Mutex :: new ( None ) ,
230
234
bucket_rep,
231
- partition_key : self . partition_key . clone ( ) ,
235
+ store_id : self . store_id . clone ( ) ,
232
236
} ) )
233
237
}
234
238
}
@@ -238,18 +242,18 @@ struct CompareAndSwap {
238
242
client : CollectionClient ,
239
243
bucket_rep : u32 ,
240
244
etag : Mutex < Option < String > > ,
241
- partition_key : Option < String > ,
245
+ store_id : Option < String > ,
242
246
}
243
247
244
248
impl CompareAndSwap {
245
249
fn get_query ( & self ) -> String {
246
250
let mut query = format ! ( "SELECT * FROM c WHERE c.id='{}'" , self . key) ;
247
- self . append_partition_key ( & mut query) ;
251
+ self . append_store_id ( & mut query, true ) ;
248
252
query
249
253
}
250
254
251
- fn append_partition_key ( & self , query : & mut String ) {
252
- append_partition_key_condition ( query, self . partition_key . as_deref ( ) ) ;
255
+ fn append_store_id ( & self , query : & mut String , condition_already_exists : bool ) {
256
+ append_store_id_condition ( query, self . store_id . as_deref ( ) , condition_already_exists ) ;
253
257
}
254
258
}
255
259
@@ -291,20 +295,15 @@ impl Cas for CompareAndSwap {
291
295
/// `swap` updates the value for the key using the etag saved in the `current` function for
292
296
/// optimistic concurrency.
293
297
async fn swap ( & self , value : Vec < u8 > ) -> Result < ( ) , SwapError > {
294
- let pk = PartitionKey :: from (
295
- self . partition_key
296
- . as_deref ( )
297
- . unwrap_or_else ( || self . key . as_str ( ) ) ,
298
- ) ;
299
298
let pair = Pair {
300
299
id : self . key . clone ( ) ,
301
300
value,
302
- partition_key : self . partition_key . clone ( ) ,
301
+ store_id : self . store_id . clone ( ) ,
303
302
} ;
304
303
305
304
let doc_client = self
306
305
. client
307
- . document_client ( & self . key , & pk )
306
+ . document_client ( & self . key , & pair . partition_key ( ) )
308
307
. map_err ( log_cas_error) ?;
309
308
310
309
let etag_value = self . etag . lock ( ) . unwrap ( ) . clone ( ) ;
@@ -376,38 +375,47 @@ impl AzureCosmosStore {
376
375
377
376
fn get_query ( & self , key : & str ) -> String {
378
377
let mut query = format ! ( "SELECT * FROM c WHERE c.id='{}'" , key) ;
379
- self . append_partition_key ( & mut query) ;
378
+ self . append_store_id ( & mut query, true ) ;
380
379
query
381
380
}
382
381
383
382
fn get_keys_query ( & self ) -> String {
384
383
let mut query = "SELECT * FROM c" . to_owned ( ) ;
385
- self . append_partition_key ( & mut query) ;
384
+ self . append_store_id ( & mut query, false ) ;
386
385
query
387
386
}
388
387
389
388
fn get_in_query ( & self , keys : Vec < String > ) -> String {
390
389
let in_clause: String = keys
391
390
. into_iter ( )
392
- . map ( |k| format ! ( "'{}'" , k ) )
391
+ . map ( |k| format ! ( "'{k }'" ) )
393
392
. collect :: < Vec < String > > ( )
394
393
. join ( ", " ) ;
395
394
396
395
let mut query = format ! ( "SELECT * FROM c WHERE c.id IN ({})" , in_clause) ;
397
- self . append_partition_key ( & mut query) ;
396
+ self . append_store_id ( & mut query, true ) ;
398
397
query
399
398
}
400
399
401
- fn append_partition_key ( & self , query : & mut String ) {
402
- append_partition_key_condition ( query, self . partition_key . as_deref ( ) ) ;
400
+ fn append_store_id ( & self , query : & mut String , condition_already_exists : bool ) {
401
+ append_store_id_condition ( query, self . store_id . as_deref ( ) , condition_already_exists ) ;
403
402
}
404
403
}
405
404
406
- /// Appends an option partition key condition to the query.
407
- fn append_partition_key_condition ( query : & mut String , partition_key : Option < & str > ) {
408
- if let Some ( pk) = partition_key {
409
- query. push_str ( " AND c.partition_key='" ) ;
410
- query. push_str ( pk) ;
405
+ /// Appends an option store id condition to the query.
406
+ fn append_store_id_condition (
407
+ query : & mut String ,
408
+ store_id : Option < & str > ,
409
+ condition_already_exists : bool ,
410
+ ) {
411
+ if let Some ( s) = store_id {
412
+ if condition_already_exists {
413
+ query. push_str ( " AND" ) ;
414
+ } else {
415
+ query. push_str ( " WHERE" ) ;
416
+ }
417
+ query. push_str ( " c.store_id='" ) ;
418
+ query. push_str ( s) ;
411
419
query. push ( '\'' )
412
420
}
413
421
}
@@ -417,15 +425,13 @@ pub struct Pair {
417
425
pub id : String ,
418
426
pub value : Vec < u8 > ,
419
427
#[ serde( skip_serializing_if = "Option::is_none" ) ]
420
- pub partition_key : Option < String > ,
428
+ pub store_id : Option < String > ,
421
429
}
422
430
423
431
impl CosmosEntity for Pair {
424
432
type Entity = String ;
425
433
426
434
fn partition_key ( & self ) -> Self :: Entity {
427
- self . partition_key
428
- . clone ( )
429
- . unwrap_or_else ( || self . id . clone ( ) )
435
+ self . store_id . clone ( ) . unwrap_or_else ( || self . id . clone ( ) )
430
436
}
431
437
}
0 commit comments