Skip to content

Commit 6cec109

Browse files
committed
Modify PutObject API to add delete functionality
1 parent 064f3db commit 6cec109

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

app/src/main/java/org/vss/impl/postgres/PostgresBackendImpl.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,31 @@ public PutObjectResponse put(PutObjectRequest request) {
6666

6767
String storeId = request.getStoreId();
6868

69-
List<VssDbRecord> vssRecords = new ArrayList<>(request.getTransactionItemsList().stream()
69+
List<VssDbRecord> vssPutRecords = new ArrayList<>(request.getTransactionItemsList().stream()
70+
.map(kv -> buildVssRecord(storeId, kv)).toList());
71+
72+
List<VssDbRecord> vssDeleteRecords = new ArrayList<>(request.getDeleteItemsList().stream()
7073
.map(kv -> buildVssRecord(storeId, kv)).toList());
7174

7275
if (request.hasGlobalVersion()) {
7376
VssDbRecord globalVersionRecord = buildVssRecord(storeId,
7477
KeyValue.newBuilder()
7578
.setKey(GLOBAL_VERSION_KEY)
7679
.setVersion(request.getGlobalVersion())
80+
.setValue(ByteString.EMPTY)
7781
.build());
7882

79-
vssRecords.add(globalVersionRecord);
83+
vssPutRecords.add(globalVersionRecord);
8084
}
8185

8286
context.transaction((ctx) -> {
8387
DSLContext dsl = ctx.dsl();
84-
List<Query> batchQueries = vssRecords.stream()
85-
.map(vssRecord -> buildPutObjectQuery(dsl, vssRecord)).toList();
88+
List<Query> batchQueries = new ArrayList<>();
89+
90+
batchQueries.addAll(vssPutRecords.stream()
91+
.map(vssRecord -> buildPutObjectQuery(dsl, vssRecord)).toList());
92+
batchQueries.addAll(vssDeleteRecords.stream()
93+
.map(vssRecord -> buildDeleteObjectQuery(dsl, vssRecord)).toList());
8694

8795
int[] batchResult = dsl.batch(batchQueries).execute();
8896

@@ -97,6 +105,12 @@ public PutObjectResponse put(PutObjectRequest request) {
97105
return PutObjectResponse.newBuilder().build();
98106
}
99107

108+
private Query buildDeleteObjectQuery(DSLContext dsl, VssDbRecord vssRecord) {
109+
return dsl.deleteFrom(VSS_DB).where(VSS_DB.STORE_ID.eq(vssRecord.getStoreId())
110+
.and(VSS_DB.KEY.eq(vssRecord.getKey()))
111+
.and(VSS_DB.VERSION.eq(vssRecord.getVersion())));
112+
}
113+
100114
private Query buildPutObjectQuery(DSLContext dsl, VssDbRecord vssRecord) {
101115
return vssRecord.getVersion() == 0 ? buildInsertRecordQuery(dsl, vssRecord)
102116
: buildUpdateRecordQuery(dsl, vssRecord);

app/src/test/java/org/vss/AbstractKVStoreIntegrationTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)