@@ -3,13 +3,12 @@ use chrono::{offset::Utc, DateTime, TimeDelta};
33use diesel:: {
44 delete,
55 dsl:: { count, max, now, sql} ,
6- sql_types:: { BigInt , Nullable , Timestamptz } ,
6+ sql_types:: { Array , BigInt , Integer , Nullable , Timestamptz } ,
77 upsert:: excluded,
88 ExpressionMethods , IntoSql , OptionalExtension , QueryDsl , SelectableHelper ,
99} ;
1010use diesel_async:: { AsyncConnection , RunQueryDsl , TransactionManager } ;
1111use futures:: TryStreamExt ;
12- use std:: collections:: HashMap ;
1312use syncstorage_db_common:: {
1413 error:: DbErrorIntrospect , params, results, util:: SyncTimestamp , Db , Sorting , DEFAULT_BSO_TTL ,
1514} ;
@@ -18,7 +17,7 @@ use super::{PgDb, TOMBSTONE};
1817use crate :: {
1918 bsos_query,
2019 db:: { CollectionLock , PRETOUCH_DT } ,
21- orm_models:: BsoChangeset ,
20+ orm_models:: { sql_types :: PostBso , BsoChangeset } ,
2221 pool:: Conn ,
2322 schema:: { bsos, collections, user_collections} ,
2423 DbError , DbResult ,
@@ -73,6 +72,7 @@ impl Db for PgDb {
7372 & mut self ,
7473 params : params:: LockCollection ,
7574 ) -> DbResult < results:: LockCollection > {
75+ let user_id = params. user_id . legacy_id as i64 ;
7676 let collection_id = self
7777 . get_collection_id ( & params. collection )
7878 . await
@@ -86,7 +86,6 @@ impl Db for PgDb {
8686 }
8787 } ) ?;
8888
89- let user_id = params. user_id . legacy_id as i64 ;
9089 let key = ( params. user_id , collection_id) ;
9190 // If we already have a read or write lock then it's safe to
9291 // use it as-is.
@@ -117,8 +116,8 @@ impl Db for PgDb {
117116 }
118117
119118 async fn lock_for_write ( & mut self , params : params:: LockCollection ) -> DbResult < ( ) > {
120- let collection_id = self . get_or_create_collection_id ( & params. collection ) . await ?;
121119 let user_id = params. user_id . legacy_id as i64 ;
120+ let collection_id = self . get_or_create_collection_id ( & params. collection ) . await ?;
122121 let key = ( params. user_id , collection_id) ;
123122
124123 if let Some ( CollectionLock :: Read ) = self . session . coll_locks . get ( & key) {
@@ -353,38 +352,11 @@ impl Db for PgDb {
353352 Ok ( results:: GetBsoIds { items, offset } )
354353 }
355354
356- async fn post_bsos ( & mut self , params : params:: PostBsos ) -> DbResult < results:: PostBsos > {
357- let collection_id = self . get_or_create_collection_id ( & params. collection ) . await ?;
358- let modified = self . checked_timestamp ( ) ?;
359-
360- self . ensure_user_collection ( params. user_id . legacy_id as i64 , collection_id)
361- . await ?;
362- for pbso in params. bsos {
363- self . put_bso ( params:: PutBso {
364- user_id : params. user_id . clone ( ) ,
365- collection : params. collection . clone ( ) ,
366- id : pbso. id . clone ( ) ,
367- payload : pbso. payload ,
368- sortindex : pbso. sortindex ,
369- ttl : pbso. ttl ,
370- } )
371- . await ?;
372- }
373- self . update_collection ( params:: UpdateCollection {
374- user_id : params. user_id ,
375- collection_id,
376- collection : params. collection ,
377- } )
378- . await ?;
379-
380- Ok ( modified)
381- }
382-
383355 async fn delete_bso ( & mut self , params : params:: DeleteBso ) -> DbResult < results:: DeleteBso > {
384- let user_id = params. user_id . legacy_id ;
356+ let user_id = params. user_id . legacy_id as i64 ;
385357 let collection_id = self . get_collection_id ( & params. collection ) . await ?;
386358 let affected_rows = delete ( bsos:: table)
387- . filter ( bsos:: user_id. eq ( user_id as i64 ) )
359+ . filter ( bsos:: user_id. eq ( user_id) )
388360 . filter ( bsos:: collection_id. eq ( & collection_id) )
389361 . filter ( bsos:: bso_id. eq ( params. id ) )
390362 . filter ( bsos:: expiry. gt ( now) )
@@ -437,65 +409,31 @@ impl Db for PgDb {
437409 }
438410
439411 async fn put_bso ( & mut self , bso : params:: PutBso ) -> DbResult < results:: PutBso > {
412+ let user_id = bso. user_id . legacy_id as i64 ;
440413 let collection_id = self . get_or_create_collection_id ( & bso. collection ) . await ?;
441- let user_id: u64 = bso. user_id . legacy_id ;
442- if self . quota . enabled {
443- let usage = self
444- . get_quota_usage ( params:: GetQuotaUsage {
445- user_id : bso. user_id . clone ( ) ,
446- collection : bso. collection . clone ( ) ,
447- collection_id,
448- } )
449- . await ?;
450- if usage. total_bytes >= self . quota . size {
451- let mut tags = HashMap :: default ( ) ;
452- tags. insert ( "collection" . to_owned ( ) , bso. collection . clone ( ) ) ;
453- self . metrics . incr_with_tags ( "storage.quota.at_limit" , tags) ;
454- if self . quota . enforced {
455- return Err ( DbError :: quota ( ) ) ;
456- } else {
457- warn ! ( "Quota at limit for user's collection ({} bytes)" , usage. total_bytes; "collection" =>bso. collection. clone( ) ) ;
458- }
459- }
460- }
414+
415+ self . check_quota ( & bso. user_id , & bso. collection , collection_id)
416+ . await ?;
461417
462418 let payload = bso. payload . as_deref ( ) . unwrap_or_default ( ) ;
463419 let sortindex = bso. sortindex ;
464- let ttl = bso. ttl . map_or ( DEFAULT_BSO_TTL , |ttl| ttl ) ;
420+ let ttl = bso. ttl . unwrap_or ( DEFAULT_BSO_TTL ) ;
465421
466422 let modified = self . checked_timestamp ( ) ?. as_datetime ( ) ?;
467423 // Expiry originally required millisecond conversion
468424 let expiry = modified + TimeDelta :: seconds ( ttl as i64 ) ;
469-
470425 // The changeset utilizes Diesel's `AsChangeset` trait.
471426 // This allows selective updates of fields if and only if they are `Some(<T>)`
472427 let changeset = BsoChangeset {
473- sortindex : if bso. sortindex . is_some ( ) {
474- sortindex // sortindex is already an Option of type `Option<i32>`
475- } else {
476- None
477- } ,
478- payload : if bso. payload . is_some ( ) {
479- Some ( payload)
480- } else {
481- None
482- } ,
483- expiry : if bso. ttl . is_some ( ) {
484- Some ( expiry)
485- } else {
486- None
487- } ,
488- modified : if bso. payload . is_some ( ) || bso. sortindex . is_some ( ) {
489- Some ( modified)
490- } else {
491- None
492- } ,
428+ sortindex : bso. sortindex ,
429+ payload : bso. payload . as_deref ( ) ,
430+ modified : ( bso. payload . is_some ( ) || bso. sortindex . is_some ( ) ) . then_some ( modified) ,
431+ expiry : bso. ttl . map ( |_| expiry) ,
493432 } ;
494- self . ensure_user_collection ( user_id as i64 , collection_id)
495- . await ?;
433+ self . ensure_user_collection ( user_id, collection_id) . await ?;
496434 diesel:: insert_into ( bsos:: table)
497435 . values ( (
498- bsos:: user_id. eq ( user_id as i64 ) ,
436+ bsos:: user_id. eq ( user_id) ,
499437 bsos:: collection_id. eq ( & collection_id) ,
500438 bsos:: bso_id. eq ( & bso. id ) ,
501439 bsos:: sortindex. eq ( sortindex) ,
@@ -517,6 +455,35 @@ impl Db for PgDb {
517455 . await
518456 }
519457
458+ async fn post_bsos ( & mut self , params : params:: PostBsos ) -> DbResult < results:: PostBsos > {
459+ let user_id = params. user_id . legacy_id as i64 ;
460+ let collection_id = self . get_or_create_collection_id ( & params. collection ) . await ?;
461+ self . check_quota ( & params. user_id , & params. collection , collection_id)
462+ . await ?;
463+ self . ensure_user_collection ( user_id, collection_id) . await ?;
464+
465+ // Rendering a VALUES statement for MERGE INTO here is painful so we
466+ // pass the bsos in a single bind param as an Array of a named
467+ // composite type (post_bso[]). The composite type must be explicitly
468+ // named/declared as postgres disallows unnamed/anonymous composite
469+ // types to be used as bind parameters
470+ diesel:: sql_query ( include_str ! ( "post_bsos.sql" ) )
471+ . bind :: < BigInt , _ > ( user_id)
472+ . bind :: < Integer , _ > ( collection_id)
473+ . bind :: < Array < PostBso > , _ > ( params. bsos )
474+ . bind :: < Timestamptz , _ > ( self . checked_timestamp ( ) ?. as_datetime ( ) ?)
475+ . bind :: < BigInt , _ > ( DEFAULT_BSO_TTL as i64 )
476+ . execute ( & mut self . conn )
477+ . await ?;
478+
479+ self . update_collection ( params:: UpdateCollection {
480+ user_id : params. user_id ,
481+ collection_id,
482+ collection : params. collection ,
483+ } )
484+ . await
485+ }
486+
520487 async fn get_collection_id ( & mut self , name : & str ) -> DbResult < results:: GetCollectionId > {
521488 if let Some ( id) = self . coll_cache . get_id ( name) ? {
522489 return Ok ( id) ;
@@ -548,9 +515,9 @@ impl Db for PgDb {
548515 & mut self ,
549516 params : params:: UpdateCollection ,
550517 ) -> DbResult < SyncTimestamp > {
518+ let user_id = params. user_id . legacy_id as i64 ;
551519 let quota = if self . quota . enabled {
552- self . calc_quota_usage ( params. user_id . legacy_id as i64 , params. collection_id )
553- . await ?
520+ self . calc_quota_usage ( user_id, params. collection_id ) . await ?
554521 } else {
555522 results:: GetQuotaUsage {
556523 count : 0 ,
@@ -562,7 +529,7 @@ impl Db for PgDb {
562529
563530 diesel:: insert_into ( user_collections:: table)
564531 . values ( (
565- user_collections:: user_id. eq ( params . user_id . legacy_id as i64 ) ,
532+ user_collections:: user_id. eq ( user_id) ,
566533 user_collections:: collection_id. eq ( params. collection_id ) ,
567534 user_collections:: modified. eq ( modified. as_datetime ( ) ?) ,
568535 user_collections:: count. eq ( quota. count as i64 ) ,
0 commit comments