I’m not sure I exactly understand the problem you are running into, but let me give you some details about how range reading works and maybe that will help to clear things up.
To answer your primary question, you can use fdb_transaction_get_range
to read an arbitrary range with some caveats. The first is that a transaction’s lifetime is limited to 5 seconds, so if your range is too large to be read in that amount of time, you won’t be able to read it all at once. Second, the C API is lower level than what we provide in other bindings, and fdb_transaction_get_range
may return early depending on your streaming mode. The expectation is that you will call it again with updated parameters if you need more of the range.
To issue a range read for the entire database, you would read from the empty string ""
to \xff
like so:
int rowLimit = 1000; // 0 for unlimited
int byteLimit = 100000; // 0 for unlimited; the byte limit may be reduced by the streaming mode
int iteration = 1; // used by FDB_STREAMING_MODE_ITERATOR, see discussion below
fdb_bool_t snapshot = 0;
fdb_bool_t reverse = 0;
FDBFuture* f = fdb_transaction_get_range(tr, FDB_KEYSEL_FIRST_GREATER_OR_EQUAL("", 0),
FDB_KEYSEL_FIRST_GREATER_OR_EQUAL("\xff", 1),
rowLimit, byteLimit, FDB_STREAMING_MODE_ITERATOR,
iteration, snapshot, reverse);
When you get the result from the future f
using fdb_future_get_keyvalue_array
, there is an output parameter named out_more
that will be set to true if the query failed to exhaust the range. This could happen if you set a row or byte limit that gets reached, if the streaming mode you use sets a byte limit that gets reached, or if the range you are reading spans multiple shards and would require you to make a request to more storage servers to get the rest of the data. When this happens, if you want to continue reading the range you will need to reissue your query with a new begin key (or end key, if in reverse) as well as update your explicit limits if you had any. You should also increment the iteration
variable if you are using FDB_STREAMING_MODE_ITERATOR
because this streaming mode increases the byte limit each time you call it, up to a maximum.
For example, if your first attempt to read the range above returns 100 keys totaling 10000 bytes with the last result having the key foo
, you could reissue the request as follows:
int rowLimit = 900; // Subtract the 100 results we got in the first query
int byteLimit = 90000; // Subtract the 10000 bytes we got in the first query
int iteration = 2; // increment the iteration
fdb_bool_t snapshot = 0;
fdb_bool_t reverse = 0;
// Notice that the begin key is now the first key after "foo"
FDBFuture* f = fdb_transaction_get_range(tr, FDB_KEYSEL_FIRST_GREATER_THAN("foo", 3),
FDB_KEYSEL_FIRST_GREATER_OR_EQUAL("\xff", 1),
rowLimit, byteLimit, FDB_STREAMING_MODE_ITERATOR,
iteration, snapshot, reverse);
For the most part, the various streaming modes adjust the byte limit of your requests as documented here. There is a slightly different streaming mode named FDB_STREAMING_MODE_EXACT
which will always return all of your request in a single batch (i.e. out_more
will be false). To use it, you are required to specify a nonzero row or byte limit. Also, if you use it on a range that’s too large to read in a 5 second window, you will fail with an error without getting any results.
If you need to read a range that’s too large to read in 5 seconds, then the best you can do right now is to split the read up over multiple transactions. In this case, the range you read may not be consistent, but depending on your use case that may be ok. To do so, you would read your range in batches (as above), periodically switching to a new transaction. You could do this in response to an error or proactively after your transaction has been open for a sufficiently long amount of time (e.g. 3 seconds). You can see an example of this approach in our higher level bindings’ locality APIs (such as in Python).