FoundationDB fdb_future_block_until_ready return before data is ready

I’m currently working locally with foundationDB on a linux machine in order to get to know it.
When calling fdb_future_get_value() after fdb_future_block_until_ready() I don’t always get the requested value.

For example, for the code below output is sometimes:

val3_len: 8
val3:

and sometimes

val3_len: 8
val3: Example

#include <stdexcept>
#include <string>
#include <iostream>
#include <thread>
#include <chrono>

#define FDB_API_VERSION 620
#include <foundationdb/fdb_c.h>

#define FDB_EXPECT(x) FDB_EXPECT_((x), __LINE__)

inline void FDB_EXPECT_(fdb_error_t x, size_t line)
{
    if(0 != x)
    {
        throw std::runtime_error(std::string("failed at line ").append(std::to_string(line).append(" with error: ").append(fdb_get_error(x))));
    }
}

int main()
{
    FDB_EXPECT(fdb_select_api_version(FDB_API_VERSION));
    FDB_EXPECT(fdb_setup_network());

    FDBDatabase *db = NULL;
    FDB_EXPECT(fdb_create_database(NULL, &db));

    try
    {
        FDBTransaction *transaction = NULL;
        FDB_EXPECT(fdb_database_create_transaction(db, &transaction));

        try
        {
            std::thread run_thread(fdb_run_network);

            const uint8_t key1[1] = {0};
            const char *str_val = "Example";
            const uint8_t *val1 = reinterpret_cast<const uint8_t *>(str_val);

            fdb_transaction_set(transaction, key1, 1, val1, 8);

            fdb_bool_t present;
            const uint8_t *val_out;
            int out_len;

            FDBFuture *future = fdb_transaction_get(transaction, key1, 1, 0);

            FDB_EXPECT(fdb_future_block_until_ready(future));

            FDB_EXPECT(fdb_future_get_value(future, &present, &val_out, &out_len));
            fdb_future_destroy(future);

            if (present)
            {
                std::cout << "val3_len: " << out_len                                 << std::endl;
                std::cout << "val3:     " << reinterpret_cast<const char *>(val_out) << std::endl;
            }

            FDB_EXPECT(fdb_stop_network());
            run_thread.join();
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';

            FDB_EXPECT(fdb_stop_network());
        }
        
        fdb_transaction_destroy(transaction);
        transaction = NULL;
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }

    fdb_database_destroy(db);
    db = NULL;
}

Note that if I add a command to sleep for two ms after fdb_future_block_until_ready() returns, the data is valid and full (I ran the code multiple times to make sure it’s not a coincidence).

Is it a known issue? Is there a way to wait until the data in FDBFuture is actually ready?

It would definitely be surprising if fdb_future_block_until_ready returned successfully and the future was not ready. I haven’t been able to repro with your example so far. One thing I’d like to point out is that val_out is not necessarily null terminated. Can you fix your “print to stdout” code to account for that and see if you can still repro? (I don’t know why that would cause Example not to show up but I’d like to at least rule it out)

I see now that you’re inserting a null-terminated string as the value. Nevermind

fdb_error_t fdb_future_get_value (FDBFuture* future , fdb_bool_t* out_present , uint8_t const** out_value , int* out_value_length )

The memory referenced by the result is owned by the FDBFuture object and will be valid until either fdb_future_destroy(future) or fdb_future_release_memory(future) is called.

So this needs to be

            FDB_EXPECT(fdb_future_get_value(future, &present, &val_out, &out_len));

            if (present)
            {
                std::cout << "val3_len: " << out_len                                 << std::endl;
                std::cout << "val3:     " << reinterpret_cast<const char *>(val_out) << std::endl;
            }

            fdb_future_destroy(future);
1 Like

Thanks, it seems to solve the problem :slight_smile:
Still not sure why waiting after blocking solve it too, but I guess it’s the kind of undefined behavior that I can’t really count on.