refactor(flagd): migrate JSONLogic evaluator to datalogic-rs v5 via C FFI#105
refactor(flagd): migrate JSONLogic evaluator to datalogic-rs v5 via C FFI#105shankar-gpio wants to merge 1 commit into
Conversation
f02a997 to
5169226
Compare
There was a problem hiding this comment.
Code Review
This pull request replaces the custom C++ JsonLogic implementation with the datalogic-rs engine via a C FFI. The changes include removing the previous flagd_ops and json_logic source files, updating the Bazel build configuration to fetch platform-specific datalogic-rs binaries, and introducing the DatalogicEngine RAII wrapper. Feedback highlights a critical need to check for null pointers on the engine handle to prevent segmentation faults and suggests documenting the performance implications of serializing JSON to strings on every evaluation.
… FFI
Replaces the hand-written JSONLogic engine and four flagd-specific
operators (~1700 LOC across providers/flagd/src/evaluator/json_logic/,
flagd_ops.{cpp,h}, and murmur_hash/) with FFI calls into the
datalogic-rs v5 C ABI.
The flagd-specific operators (starts_with, ends_with, sem_ver,
fractional) are now native in datalogic-rs's `flagd` Cargo feature,
using the same MurmurHash3-x86-32 bucketing as the flagd reference
implementation. JSONLogic spec conformance is now maintained upstream
across all language bindings (.NET, JVM, Python, Node, PHP, Go, WASM,
C++).
Per-platform staticlibs are pulled in via http_archive against the
SHA-pinned v5.0.0 release tarballs (linux/darwin x amd64/arm64). The
thin DatalogicEngine C++ wrapper (~50 LOC) provides RAII over the C
engine handle and a single Apply(rule, data) entry point that
serializes via nlohmann::json and surfaces errors through datalogic's
thread-local last-error state.
The .bazelrc workaround for abseil discarded-section issues
(-Wl,--gc-sections) is gated behind build:linux since macOS ld64
rejects the flag.
How to test:
bazelisk test //providers/flagd/tests/...
All 7 flagd tests pass, including the unmodified 627-line
evaluator_test which is the end-to-end regression contract covering
targeting rules, fractional bucketing, sem_ver gates, variant
selection, and metadata enrichment.
Signed-off-by: shankar-gpio <shankar@goplasmatic.io>
5169226 to
ddaf818
Compare
|
Thanks for the review! Addressed both points in the amended commit (
All 7 flagd tests still pass locally. |
|
Thank you for this very detailed and well-architected PR! The effort put into establishing the Bazel platform selects, pinning the dependencies, and setting up the C ABI wrapper is impressive. However, my personal opinion is to hold off on migrating the evaluation engine to the 1. Premature Ecosystem UnificationWhile unification around a single engine is an active and valuable discussion, no decisions have been finalized, and no other non-Rust SDK bindings (Go, Java, Python, Node, etc.) have adopted 2. Portability and Platform SupportC++ is often utilized in specialized, resource-constrained, or diverse environments where portability is key. By relying on prebuilt static libraries ( .a blobs) fetched via 3. Performance and FFI OverheadThe proposed wrapper requires serializing/deserializing JSON strings across the C++/Rust FFI boundary ( As the C++ is often used in critical and performance sensitive environments, I'm against merging this change in current form, but I think that the goal of evaluation engine unification is justified and I will be happy to revisit this migration once an official organization-wide consensus is reached and other language bindings start actively adopting it! |
|
On top of what @m-olko said above I also would like to add that there are organizations that have strict policies against using Rust FFI and similar technologies. Any sort of decision about introduction of unified implementations across flagd providers needs careful deliberation with the broader community. |
This PR
Replaces the hand-written JSONLogic engine and four flagd-specific operators (~1700 LOC across
providers/flagd/src/evaluator/json_logic/,flagd_ops.{cpp,h}, andmurmur_hash/) with FFI calls into the datalogic-rs v5 C ABI.The flagd-specific operators (
starts_with,ends_with,sem_ver,fractional) are now native in datalogic-rs'sflagdCargo feature, using the same MurmurHash3-x86-32 bucketing as the flagd reference implementation. JSONLogic spec conformance is now maintained upstream and shared with every other language binding (.NET, JVM, Python, Node, PHP, Go, WASM) so behaviour stays aligned across the OpenFeature ecosystem.What changed
Added
providers/flagd/src/evaluator/datalogic_engine.{h,cpp}— thin RAII wrapper (~50 LOC) over the datalogic-rs C engine handle; exposes oneApply(rule, data) → absl::StatusOr<nlohmann::json>entry point that serializes vianlohmann::json, callsdatalogic_engine_apply, and surfaces errors through datalogic's thread-local last-error state.providers/flagd/third_party/datalogic/BUILD.bazel— wraps the per-platform staticlibs as a singlecc_library :datalogic_cwithselect()over@platformsconstraints.providers/flagd/tests/evaluator/datalogic_engine_test.cpp— focused unit tests for the wrapper (var, starts_with, sem_ver, fractional stability, parse-error surfacing).Modified
MODULE.bazel— addsbazel_dep platforms, declareshttp_archivefor the fourgo-staticlib-<os>-<arch>.tar.gzartifacts from the v5.0.0 GitHub release (linux/darwin × amd64/arm64), SHA-pinned..bazelrc— gates the abseil-Wl,--gc-sectionsworkaround (and-fno-asynchronous-unwind-tables) behindbuild:linuxvia--enable_platform_specific_config. macOSld64rejects--gc-sections.providers/flagd/src/evaluator/evaluator.{h,cpp}— swaps thejson_logic::JsonLogicmember forDatalogicEngine; drops the fourRegisterOperationcalls (now native upstream).providers/flagd/src/evaluator/BUILD,providers/flagd/tests/evaluator/BUILD— refresh deps.Deleted (~1700 LOC)
providers/flagd/src/evaluator/json_logic/(whole directory, 7 source files + BUILD)providers/flagd/src/evaluator/flagd_ops.{cpp,h}providers/flagd/src/evaluator/murmur_hash/(whole directory, including the vendored MurmurHash3 from aappleby/smhasher)providers/flagd/tests/evaluator/json_logic/— JSONLogic spec tests, now maintained upstream incrates/datalogic-rs/tests/suites/providers/flagd/tests/evaluator/flagd_ops_test.cpp,flagd_fractional_op_test.cpp— covered upstream incrates/datalogic-rs/tests/suites/flagd/Design choices
datalogic_engine_applyper call) over compile-cache + session. Simpler code; the C ABI explicitly exposes the string-in/string-out form because Rust-native types can't cross FFI. Rule-compile cache and arena-reusingSessioncan be added behind the sameDatalogicEngineabstraction later if profiling motivates it.libdatalogic_c.aper platform. Self-contained, no runtimeLD_LIBRARY_PATH/rpath setup for downstream consumers.const char*) in and out.nlohmann::json::dump()/parse()round-trip is unavoidable across the boundary and matches the contract exactly.Related Issues
Supersedes the in-house implementation from #65 and #92. Closes the maintenance burden of:
evaluator.cpp:222)How to test
All 7 flagd tests pass — including the unmodified 627-line
evaluator_test, which is the end-to-end regression contract covering targeting rules, fractional bucketing, sem_ver gates, variant selection, and metadata enrichment:Verified on
darwin-arm64; CI will cover the other supported platforms.Platform coverage
Currently pulling staticlibs for: linux-amd64, linux-arm64, darwin-amd64, darwin-arm64. Windows is published upstream (the v5.0.0 release ships
go-staticlib-windows-{amd64,arm64}.tar.gz) and can be added to the Bazelselect()if/when the SDK targets Windows.