apple / foundationdb

FoundationDB - the open source, distributed, transactional key-value store
https://apple.github.io/foundationdb/
Apache License 2.0
14.42k stars 1.31k forks source link

C workload bindings #11288

Open Wonshtrum opened 5 months ago

Wonshtrum commented 5 months ago

I'm utilizing the ExternalWorkload to test custom layers within the FoundationDB simulation. The layers under test are authored in Rust and are based on the foundationdb-rs project. The workload API is defined by the C++ header file ClientWorkload.h. Interoperability between Rust and C++ presents challenges, although creating a C++ to C shim was relatively straightforward. My main concern is the unstable ABI of the C++ API, as it relies on the compiler, linker, and system libraries. This proved to be an issue when transitioning from version 7.1 to 7.3, where the workload I was using suddenly broke due to a change in the representation of the std::string class as the fdbserver switched from gcc to clang, as detailed in this forum thread and issue #11298.

To address this, I propose that alongside the existing C++ API (utilized by JavaWorkload), pure C bindings should be made available to ease integration with other languages and mitigate the impact of changes in compilation environments. This aligns well with the location of ClientWorkload.h in the bindings/c folder.

Given my limited familiarity with the FoundationDB codebase, I resorted to various wrappers to minimize code changes. However, a more efficient approach might involve rewriting the ExternalWorkload or creating a dedicated C version.

In this PR, I've divided ClientWorkload.h into CWorkload.h and CppWorkload.h. Importing CppWorkload.h should mirror the behavior of the original ClientWorkload.h, ensuring compatibility with existing workloads utilizing this API. The C bindings introduce BridgeToClient and BridgeToServer structs that encapsulate collections of function pointers. If the "useCAPI" option is enabled in the ExternalWorkload configuration file, it will load the workloadInstantiate symbol (instead of workloadFactory) and invoke it with the server-bridge (allowing the C workload to interact with the C++ FDBWorkloadContext and GenericPromise<bool> classes). The returned client-bridge is then encapsulated within a C++ "translator" workload and assigned as the workloadImpl of the ExternalWorkload.