Skip to content

Commit bdbe114

Browse files
authored
Merge pull request #52 from scalableminds/multi-get
Add new api call GetMultipleKeysByList
2 parents dfed83d + 078c946 commit bdbe114

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- New API endpoints `DeleteAllByPrefix` and `PutMultipleVersions`. [#47](https://github.com/scalableminds/fossildb/pull/47)
55
- New API endpoints `GetMultipleKeysByListWithMultipleVersions` and `PutMultipleKeysWithMultipleVersions` for reading and writing multiple keys/versions in one request. [#48](https://github.com/scalableminds/fossildb/pull/48)
66
- `ListKeys` now supports optional `prefix` field
7+
- New API endpoint `GetMultipleKeysByList`. [#52](https://github.com/scalableminds/fossildb/pull/52)
78

89
## Breaking Changes
910

src/main/protobuf/fossildbapi.proto

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ message VersionValuePairProto {
1818
required bytes value = 2;
1919
}
2020

21+
message VersionValueBoxProto {
22+
optional VersionValuePairProto versionValuePair = 1;
23+
optional string errorMessage = 2;
24+
}
25+
2126
message HealthRequest {}
2227
message HealthReply {
2328
required bool success = 1;
@@ -123,6 +128,18 @@ message GetMultipleKeysReply {
123128
repeated uint64 actualVersions = 5;
124129
}
125130

131+
message GetMultipleKeysByListRequest {
132+
required string collection = 1;
133+
repeated string keys = 2;
134+
optional uint64 version = 3; // Applied to all requested keys
135+
}
136+
137+
message GetMultipleKeysByListReply {
138+
required bool success = 1;
139+
optional string errorMessage = 2;
140+
repeated VersionValueBoxProto versionValueBoxes = 3;
141+
}
142+
126143
message GetMultipleKeysByListWithMultipleVersionsRequest {
127144
required string collection = 1;
128145
repeated string keys = 2;
@@ -215,6 +232,7 @@ service FossilDB {
215232
rpc Get (GetRequest) returns (GetReply) {}
216233
rpc GetMultipleVersions (GetMultipleVersionsRequest) returns (GetMultipleVersionsReply) {}
217234
rpc GetMultipleKeys (GetMultipleKeysRequest) returns (GetMultipleKeysReply) {}
235+
rpc GetMultipleKeysByList (GetMultipleKeysByListRequest) returns (GetMultipleKeysByListReply) {}
218236
rpc GetMultipleKeysByListWithMultipleVersions (GetMultipleKeysByListWithMultipleVersionsRequest) returns (GetMultipleKeysByListWithMultipleVersionsReply) {}
219237
rpc Put (PutRequest) returns (PutReply) {}
220238
rpc PutMultipleVersions (PutMultipleVersionsRequest) returns (PutMultipleVersionsReply) {}

src/main/scala/com/scalableminds/fossildb/FossilDBGrpcImpl.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ class FossilDBGrpcImpl(storeManager: StoreManager)
7676
GetMultipleKeysByListWithMultipleVersionsReply(success = true, None, keyVersionsValuesPairs)
7777
} { errorMsg => GetMultipleKeysByListWithMultipleVersionsReply(success = false, errorMsg) }
7878

79+
override def getMultipleKeysByList(req: GetMultipleKeysByListRequest): Future[GetMultipleKeysByListReply] = withExceptionHandler(req) {
80+
val store = storeManager.getStore(req.collection)
81+
val versionValueBoxes = req.keys.map { key =>
82+
val versionedKeyValuePairOpt = store.withRawRocksIterator { rocksIt => store.get(rocksIt, key, req.version) }
83+
versionedKeyValuePairOpt match {
84+
case Some(pair) => VersionValueBoxProto(Some(VersionValuePairProto(pair.version, ByteString.copyFrom(pair.value))), errorMessage = None)
85+
case None => VersionValueBoxProto(None, errorMessage = None)
86+
}
87+
}
88+
GetMultipleKeysByListReply(success = true, None, versionValueBoxes)
89+
} { errorMsg => GetMultipleKeysByListReply(success = false, errorMsg) }
90+
7991
override def putMultipleKeysWithMultipleVersions(req: PutMultipleKeysWithMultipleVersionsRequest): Future[PutMultipleKeysWithMultipleVersionsReply] = withExceptionHandler(req) {
8092
val store = storeManager.getStore(req.collection)
8193
require(req.versionedKeyValuePairs.forall(_.version >= 0), "Version numbers must be non-negative")

src/test/scala/com/scalableminds/fossildb/FossilDBSuite.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,41 @@ class FossilDBSuite extends AnyFlatSpec with BeforeAndAfterEach with TestHelpers
441441
assert(reply.keyVersionsValuesPairs.isEmpty)
442442
}
443443

444+
"GetMultipleKeysByList" should "return version-value tuples for existing, and empty for missing keys" in {
445+
client.put(PutRequest(collectionA, aKey, Some(0), testData1))
446+
client.put(PutRequest(collectionA, aNotherKey, Some(0), testData2))
447+
client.put(PutRequest(collectionA, aNotherKey, Some(1), testData3))
448+
val reply = client.getMultipleKeysByList(GetMultipleKeysByListRequest(collectionA, keys = Seq(aKey, aNotherKey, aThirdKey)))
449+
assert(reply.versionValueBoxes.length == 3)
450+
assert(reply.versionValueBoxes(0).versionValuePair.exists(_.value == testData1))
451+
assert(reply.versionValueBoxes(1).versionValuePair.exists(_.value == testData3))
452+
assert(reply.versionValueBoxes(1).versionValuePair.exists(_.actualVersion == 1))
453+
assert(reply.versionValueBoxes(2).versionValuePair.isEmpty)
454+
}
455+
456+
it should "not return something newer than the requested version" in {
457+
client.put(PutRequest(collectionA, aKey, Some(0), testData1))
458+
client.put(PutRequest(collectionA, aNotherKey, Some(0), testData1))
459+
client.put(PutRequest(collectionA, aNotherKey, Some(1), testData2))
460+
client.put(PutRequest(collectionA, aNotherKey, Some(2), testData3))
461+
client.put(PutRequest(collectionA, aThirdKey, Some(2), testData3))
462+
val reply = client.getMultipleKeysByList(GetMultipleKeysByListRequest(collectionA, keys = Seq(aKey, aNotherKey, aThirdKey), version = Some(1)))
463+
assert(reply.versionValueBoxes.length == 3)
464+
assert(reply.versionValueBoxes(0).versionValuePair.exists(_.value == testData1))
465+
assert(reply.versionValueBoxes(0).versionValuePair.exists(_.actualVersion == 0))
466+
assert(reply.versionValueBoxes(1).versionValuePair.exists(_.value == testData2))
467+
assert(reply.versionValueBoxes(1).versionValuePair.exists(_.actualVersion == 1))
468+
assert(reply.versionValueBoxes(2).versionValuePair.isEmpty)
469+
}
470+
471+
it should "return only empty boxes if nothing matches" in {
472+
client.put(PutRequest(collectionA, aKey, Some(2), testData1))
473+
client.put(PutRequest(collectionA, aNotherKey, Some(2), testData1))
474+
val reply = client.getMultipleKeysByList(GetMultipleKeysByListRequest(collectionA, keys = Seq(aKey, aNotherKey, aThirdKey), version = Some(1)))
475+
assert(reply.versionValueBoxes.length == 3)
476+
assert(reply.versionValueBoxes.forall(_.versionValuePair.isEmpty))
477+
}
478+
444479
"Backup" should "create non-empty backup directory" in {
445480
client.put(PutRequest(collectionA, aKey, Some(0), testData1))
446481
client.backup(BackupRequest())

0 commit comments

Comments
 (0)