getKey in FoundationDB returns unexpected result

371 Views Asked by At

I trying to find a key in some Subspace at FoundationDB with getKey and KeySelector. In case if result exists in Subspace it works pretty well.

val key      = new Tuple().add(3)
val subspace = new Subspace(new Tuple().add("test-subspace"))

tr.set(key.pack(), new Tuple().pack())
tr.set(subspace.pack(key), new Tuple().pack())

tr.getKey(KeySelector.firstGreaterOrEqual(subspace.pack(key)))
              .thenApply[Tuple] { result =>
                println(Tuple.fromBytes(result)) // ("test-subspace", 3)
                subspace.unpack(result) // (3)
              }

In the same time, if key does not exist in target subspace, it returns me key that was found in default subspace. Which is not what I expected...

val key      = new Tuple().add(3)
val subspace = new Subspace(new Tuple().add("test-subspace"))

tr.set(key.pack(), new Tuple().pack())

tr.getKey(KeySelector.firstGreaterOrEqual(subspace.pack(key)))
              .thenApply[Tuple] { result =>
                println(Tuple.fromBytes(result)) // (3)
                subspace.unpack(result) // Cannot unpack key that is not contained in subspace.
              }

Also, if db empty, getKey instead of returning null, returns some weird byte array which cannot be parsed by Tuple.fromBytes.

val key = new Tuple().add("my-key")    

tr.getKey(KeySelector.firstGreaterOrEqual(key.pack()))
              .thenApply[Tuple] { result =>
                println(result == null) // false
                Tuple.fromBytes(result) // throws java.lang.IllegalArgumentException: Unknown tuple data type -1 at index 0
              }

How should I handle situations when target subspaces do not contain the search result?

2

There are 2 best solutions below

2
On BEST ANSWER

This is expected behavior. Keyselector returns you the key that matches the condition - in this case first key that is greater or equal to the passed byte[]. You would need to check if returned key is valid as per your subspace requirement - by using subspace.contains() or any other validation on the returned key.

Same explanation for the second question- the returned key could be some special pre-existing row in the db, that is not created using tuple layer. Hence it cannot be parsed using tuple layer. You need to check for key validity by using subspace.contains or some similar check.

0
On

To add on to what Guarav said, when a key selector resolves to a key before the beginning of the database, it returns the empty key (''). If the key resolves past the end of the database, you'll get '\xff' in a normal transaction or '\xff\xff' if your transaction is allowed to read system keys. This is mentioned briefly at the end of the key selector documentation here.

As for not returning a result outside of your subspace, to do so would probably require getKey accepting a bound key parameter that restricts searches beyond that key. It doesn't currently have that parameter, but getRange does and can be used to perform the same query if you use a limit of 1. For example, you could do:

tr.getRange(KeySelector.firstGreaterOrEqual(subspace.pack(key)), subspace.range().end, 1)

In this case, the result will either have a key if one could be found in the subspace matching your key selector or will be empty if one could not. Of course, you'll also get back the value in this query.