1- use std:: collections:: BTreeMap ;
1+ use std:: collections:: { BTreeMap , BTreeSet } ;
22
33use rocket:: async_trait;
44use tokio:: sync:: RwLock ;
@@ -24,6 +24,18 @@ pub trait MetadataService: Send + Sync {
2424 /// refresh if the metadata has changed.
2525 async fn set ( & self , metadata : BTreeMap < String , String > ) -> Result < ( ) , Error > ;
2626
27+ /// Insert or update the given metadata key-value pairs.
28+ ///
29+ /// Existing keys not present in `metadata` are left untouched. Will trigger a
30+ /// registration refresh if the metadata has changed.
31+ async fn upsert ( & self , metadata : BTreeMap < String , String > ) -> Result < ( ) , Error > ;
32+
33+ /// Delete the given metadata keys.
34+ ///
35+ /// Keys that do not exist are ignored. Will trigger a registration refresh if the
36+ /// metadata has changed.
37+ async fn delete ( & self , keys : BTreeSet < String > ) -> Result < ( ) , Error > ;
38+
2739 /// Get all user-set metadata key-value pairs.
2840 async fn get ( & self ) -> Result < BTreeMap < String , String > , Error > ;
2941}
@@ -67,19 +79,16 @@ impl<A: App> OasisMetadataService<A> {
6779 limits,
6880 } )
6981 }
70- }
7182
72- #[ async_trait]
73- impl < A : App > MetadataService for OasisMetadataService < A > {
74- async fn set ( & self , metadata : BTreeMap < String , String > ) -> Result < ( ) , Error > {
75- // Validate metadata against runtime limits.
83+ /// Validate the given metadata map against the configured runtime limits.
84+ fn validate ( & self , metadata : & BTreeMap < String , String > ) -> Result < ( ) , Error > {
7685 let max_user_pairs =
7786 ( self . limits . max_pairs as usize ) . saturating_sub ( RESERVED_METADATA_SLOTS ) ;
7887 if metadata. len ( ) > max_user_pairs {
7988 return Err ( Error :: InvalidArgument ) ;
8089 }
8190
82- for ( key, value) in & metadata {
91+ for ( key, value) in metadata {
8392 // Account for namespace prefix when checking key size.
8493 let full_key_size = METADATA_NAMESPACE . len ( ) + 1 + key. len ( ) ;
8594 if full_key_size > self . limits . max_key_size as usize {
@@ -90,8 +99,70 @@ impl<A: App> MetadataService for OasisMetadataService<A> {
9099 }
91100 }
92101
102+ Ok ( ( ) )
103+ }
104+ }
105+
106+ #[ async_trait]
107+ impl < A : App > MetadataService for OasisMetadataService < A > {
108+ async fn set ( & self , metadata : BTreeMap < String , String > ) -> Result < ( ) , Error > {
109+ // Validate metadata against runtime limits.
110+ self . validate ( & metadata) ?;
111+
93112 let mut map = self . metadata . write ( ) . await ;
113+ if * map == metadata {
114+ return Ok ( ( ) ) ;
115+ }
94116 * map = metadata;
117+ drop ( map) ;
118+
119+ // Refresh registration.
120+ self . env . refresh_registration ( ) . await ?;
121+
122+ Ok ( ( ) )
123+ }
124+
125+ async fn upsert ( & self , metadata : BTreeMap < String , String > ) -> Result < ( ) , Error > {
126+ let mut map = self . metadata . write ( ) . await ;
127+
128+ // Apply the upsert onto a copy and validate the resulting map against runtime
129+ // limits before committing.
130+ let mut updated = map. clone ( ) ;
131+ let mut changed = false ;
132+ for ( key, value) in metadata {
133+ if updated. get ( & key) != Some ( & value) {
134+ changed = true ;
135+ }
136+ updated. insert ( key, value) ;
137+ }
138+ self . validate ( & updated) ?;
139+
140+ if !changed {
141+ return Ok ( ( ) ) ;
142+ }
143+ * map = updated;
144+ drop ( map) ;
145+
146+ // Refresh registration.
147+ self . env . refresh_registration ( ) . await ?;
148+
149+ Ok ( ( ) )
150+ }
151+
152+ async fn delete ( & self , keys : BTreeSet < String > ) -> Result < ( ) , Error > {
153+ let mut map = self . metadata . write ( ) . await ;
154+
155+ let mut changed = false ;
156+ for key in & keys {
157+ if map. remove ( key) . is_some ( ) {
158+ changed = true ;
159+ }
160+ }
161+
162+ if !changed {
163+ return Ok ( ( ) ) ;
164+ }
165+ drop ( map) ;
95166
96167 // Refresh registration.
97168 self . env . refresh_registration ( ) . await ?;
0 commit comments