Can't get last pair in FDBKeyValue array

I’ve been using range reads in the FoundationDB C API with the following code:

FDBFuture *future = fdb_transaction_get_range(tr, beginKey, beginKeySize, 0, 0,
endKey, endKeySize, 0, 0, limit, 0, FDB_STREAMING_MODE_WANT_ALL, 0, 0, 0);
//Wait for future and check errors...
int outMore, outCount;
FDBKeyValue *keyValue = fdb_future_get_keyvalue_array(future, &keyValue, &outCount, &outMore);
printf("%d\n", outCount);
printf("%.*s\n", keyValue[0].key_length, keyValue[0].key);
printf("%.*s\n", keyValue[1].key_length, keyValue[1].key);
printf("%.*s\n", keyValue[2].key_length, keyValue[2].key);
printf("%.*s\n", keyValue[3].key_length, keyValue[3].key);

When I run getrange '' \xff, I get:

`hello' is `world'
`hi' is `everyone'
`mykey` is `myvalue'

However, the output of my C program is:

3

hello
hi

The first pair (keyValue[0]) is empty. The next pairs are present, however I would expect hello to be at position 0, and hi to be at position 1. When reading keyValue[3], nothing is present, as expected.

I have a few questions:

  1. How can I get all 3 pairs?
  2. Why is keyValue[0] empty?

I would appreciate any help. Thanks!

I think you’re running into two separate issues here. First, fdbcli uses double quotes to quote strings rather than single quotes. Your current fdbcli command will not return any key that comes lexicographically before the string "''", and my guess is that one such key exists. Try running getrange "" \xff instead to see what’s there.

The second thing is that the end key selector in a get range call is exclusive. The call will resolve the key selector first and then exclude whatever key is resolved. Assuming your end key is "\xff", an offset of 0 and an or_equal of 0 will resolve to the last key in the database before your end key, and that key will be excluded from your result (i.e. this is equivalent to LAST_LESS_THAN). Instead, you should use FIRST_GREATER_OR_EQUAL to resolve to the first key outside of your desired range (or without the macro, this is offset=1, or_equal=0).

@ajbeamon Thanks, that worked! However, I seem to randomly get error code 2012: Range limits not valid. What could be causing this?

This isn’t an error I’ve really seen much of, but based on my interpretation of the code, it looks like it could happen if you pass in a negative row or byte limit. Is that happening in your case?

If not, what are your arguments to the fdb_transaction_get_range call?

EDIT: Problem solved! It was my fault. I forgot to dereference a pointer that I was passing to fdb_transaction_get_range().

I’m not using a byte limit, and limit is always set to 5. It seems that I get the error on fdb_future_get_error(), and it will occur seemingly randomly even when the contents of the database has not changed.

FDBFuture *future = fdb_transaction_get_range(tr, FDB_KEYSEL_FIRST_GREATER_THAN(
"r\x00", 2), FDB_KEYSEL_FIRST_GREATER_OR_EQUAL(string, size), 5, 0,
FDB_STREAMING_MODE_WANT_ALL, 0, 0, 1);