@@ -131,6 +131,40 @@ void putShouldSucceedWhenNoGlobalVersionIsGiven() {
131131 assertThat (getObject (KVStore .GLOBAL_VERSION_KEY ).getVersion (), is (0L ));
132132 }
133133
134+ @ Test
135+ void putAndDeleteShouldSucceedAsAtomicTransaction () {
136+ assertDoesNotThrow (() -> putObjects (null , List .of (kv ("k1" , "k1v1" , 0 ))));
137+ // Put and Delete succeeds
138+ assertDoesNotThrow (() -> putAndDeleteObjects (null , List .of (kv ("k2" , "k2v1" , 0 )), List .of (kv ("k1" , "" , 1 ))));
139+
140+ KeyValue response = getObject ("k2" );
141+ assertThat (response .getKey (), is ("k2" ));
142+ assertThat (response .getVersion (), is (1L ));
143+ assertThat (response .getValue ().toStringUtf8 (), is ("k2v1" ));
144+
145+ assertTrue (getObject ("k1" ).getValue ().isEmpty ());
146+
147+ // Delete fails (and hence put as well) due to mismatched version for the deleted item.
148+ assertThrows (ConflictException .class , () -> putAndDeleteObjects (null , List .of (kv ("k3" , "k3v1" , 0 )), List .of (kv ("k2" , "" , 3 ))));
149+
150+ assertTrue (getObject ("k3" ).getValue ().isEmpty ());
151+ assertFalse (getObject ("k2" ).getValue ().isEmpty ());
152+
153+ // Put fails (and hence delete as well) due to mismatched version for the put item.
154+ assertThrows (ConflictException .class , () -> putAndDeleteObjects (null , List .of (kv ("k3" , "k3v1" , 1 )), List .of (kv ("k2" , "" , 1 ))));
155+
156+ assertTrue (getObject ("k3" ).getValue ().isEmpty ());
157+ assertFalse (getObject ("k2" ).getValue ().isEmpty ());
158+
159+ // Put and delete both fail due to mismatched global version.
160+ assertThrows (ConflictException .class , () -> putAndDeleteObjects (2L , List .of (kv ("k3" , "k3v1" , 0 )), List .of (kv ("k2" , "" , 1 ))));
161+
162+ assertTrue (getObject ("k3" ).getValue ().isEmpty ());
163+ assertFalse (getObject ("k2" ).getValue ().isEmpty ());
164+
165+ assertThat (getObject (KVStore .GLOBAL_VERSION_KEY ).getVersion (), is (0L ));
166+ }
167+
134168 @ Test
135169 void getShouldReturnEmptyResponseWhenKeyDoesNotExist () {
136170 KeyValue response = getObject ("non_existent_key" );
@@ -370,6 +404,19 @@ private void putObjects(@Nullable Long globalVersion, List<KeyValue> keyValues)
370404 this .kvStore .put (putObjectRequestBuilder .build ());
371405 }
372406
407+ private void putAndDeleteObjects (@ Nullable Long globalVersion , List <KeyValue > putKeyValues , List <KeyValue > deleteKeyValues ) {
408+ PutObjectRequest .Builder putObjectRequestBuilder = PutObjectRequest .newBuilder ()
409+ .setStoreId (STORE_ID )
410+ .addAllTransactionItems (putKeyValues )
411+ .addAllDeleteItems (deleteKeyValues );
412+
413+ if (Objects .nonNull (globalVersion )) {
414+ putObjectRequestBuilder .setGlobalVersion (globalVersion );
415+ }
416+
417+ this .kvStore .put (putObjectRequestBuilder .build ());
418+ }
419+
373420 private ListKeyVersionsResponse list (@ Nullable String nextPageToken , @ Nullable Integer pageSize ,
374421 @ Nullable String keyPrefix ) {
375422 ListKeyVersionsRequest .Builder listRequestBuilder = ListKeyVersionsRequest .newBuilder ()
0 commit comments