Skip to content

Conversation

@aepfli
Copy link
Member

@aepfli aepfli commented Dec 23, 2025

This PR

  • adds this new feature

Related Issues

Fixes #1234523

Notes

Follow-up Tasks

How to test

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @aepfli, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant architectural change by integrating a WebAssembly (WASM) based in-process resolver for flagd. This new resolver utilizes the flagd-evaluator WASM module to perform local flag evaluations, moving away from a purely Java-native approach. The changes encompass adding WASM runtime dependencies, implementing a dedicated WASM runtime manager with all required host functions, and a new resolver class to orchestrate the WASM module's lifecycle and interactions. This is presented as a Proof of Concept (PoC) to explore the benefits of WASM for flag evaluation.

Highlights

  • WASM Integration: Introduced a WebAssembly (WASM) based in-process resolver for flagd, leveraging the flagd-evaluator WASM module for local flag evaluations.
  • WASM Runtime Management: Added FlagdWasmRuntime.java to handle loading, compiling, and providing the necessary host functions (9 in total, including critical ones for timestamp and random values, legacy functions, error handling, and wasm-bindgen no-ops) for the WASM module.
  • Dependency Update: Included com.dylibso.chicory WASM runtime, compiler, and parser dependencies in pom.xml to enable WASM execution.
  • New Resolver Implementation: Implemented InProcessWasmResolver.java to replace the previous InProcessResolver, managing WASM module interaction, context serialization, and flag update processing.
  • Host Function Documentation: A new markdown file WASM_HOST_IMPORTS.md was added, detailing the purpose and implementation status of each required host function for the WASM module.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new WASM-based in-process flag evaluation mechanism, replacing the existing InProcessResolver with InProcessWasmResolver. It adds the Chicory WASM runtime dependencies and a new FlagdWasmRuntime class responsible for loading the flagd-evaluator.wasm module and providing 9 essential host functions, including critical ones for time and random value generation, legacy timestamp functions, error handling, and wasm-bindgen no-ops. A new FlagsChangedResponse class is added for handling flag update payloads from the WASM module. The InProcessWasmResolver manages WASM memory, allocates space for flag keys and evaluation contexts, calls WASM export functions for evaluation and state updates, and processes flag changes in a dedicated stateWatcher thread. Review comments highlight several issues: the WASM module loading is brittle and should use classpath resources; the resolve method in InProcessWasmResolver is not thread-safe and needs synchronization; error handling in the stateWatcher thread is too aggressive, leading to termination; the ImmutableMetadataDeserializer incorrectly converts all metadata values to strings, losing type information; the init() method of InProcessWasmResolver is not called in tests, preventing flag loading; the SecureRandom instance in FlagdWasmRuntime should be static for efficiency; the shutdown() method in InProcessWasmResolver is unimplemented; and there is a significant amount of unused host function implementations and metadata helper methods in InProcessWasmResolver that should be removed.

I am having trouble creating individual review comments. Click here to see my feedback.

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/FlagdWasmRuntime.java (35-39)

critical

Loading the WASM module using Path.of("flagd_evaluator.wasm") is brittle as it relies on the file being present in the current working directory. This will likely fail in many deployment scenarios. The WASM file should be bundled as a resource within the JAR and loaded from the classpath.

        try (java.io.InputStream wasmStream = FlagdWasmRuntime.class.getClassLoader().getResourceAsStream("flagd_evaluator.wasm")) {
            if (wasmStream == null) {
                throw new java.io.IOException("flagd_evaluator.wasm not found in classpath");
            }
            wasmBytes = wasmStream.readAllBytes();
        } catch (IOException e) {
            throw new RuntimeException("Failed to load flagd_evaluator.wasm", e);
        }

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessWasmResolver.java (226)

critical

The resolve method is not thread-safe. A single WASM Instance is shared across all evaluations. If multiple threads call evaluation methods concurrently, they will execute resolve and perform memory operations (alloc, write, evaluate, dealloc) on the same WASM memory, leading to race conditions and incorrect results. The entire resolve method should be synchronized to ensure that evaluations are executed serially.

    private synchronized <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationContext ctx) {

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessWasmResolver.java (153-157)

high

Catching JsonMappingException and JsonProcessingException and re-throwing them as RuntimeException will cause the stateWatcher thread to terminate. This means that no further flag configuration updates will be processed. These exceptions should be logged as errors, and the loop should continue to ensure the resolver can recover from malformed data and process subsequent valid updates.

            } catch (JsonMappingException | JsonProcessingException e) {
                log.error("Failed to process flag update payload", e);
            }

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessWasmResolver.java (543-545)

high

The custom ImmutableMetadataDeserializer incorrectly converts all metadata values to strings using entry.getValue().toString(). This causes a loss of type information (e.g., numbers and booleans become strings), which can lead to unexpected behavior in hooks or client applications that inspect the flag metadata. The deserializer should preserve the original types of the values.

            for (Map.Entry<String, Object> entry : map.entrySet()) {
                final String key = entry.getKey();
                final Object value = entry.getValue();

                if (value instanceof Boolean) {
                    builder.addBoolean(key, (Boolean) value);
                } else if (value instanceof String) {
                    builder.addString(key, (String) value);
                } else if (value instanceof Integer) {
                    builder.addInteger(key, (Integer) value);
                } else if (value instanceof Double) {
                    builder.addDouble(key, (Double) value);
                } else if (value != null) {
                    builder.addString(key, value.toString());
                }
            }

providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/WasmResolverTest.java (16-22)

high

The InProcessWasmResolver is instantiated in a static block, but its init() method is never called. The init() method is responsible for starting the FileQueueSource poller, which loads the flag configurations from test.json. Without calling init(), the resolver will not have any flags loaded, and the evaluations in testWasmResolverInit will likely fail with FLAG_NOT_FOUND errors. The init() method should be called after the resolver is created, and the operation should be wrapped in a try-catch block as init() can throw a checked exception.

    static {
        try {
            System.out.println("Creating InProcessWasmResolver...");
            FlagdOptions options =
                    FlagdOptions.builder().offlineFlagSourcePath("test.json").build();
            RESOLVER = new InProcessWasmResolver(options, event -> {});
            RESOLVER.init();
            System.out.println("InProcessWasmResolver created successfully!");
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize WasmResolverTest", e);
        }
    }

providers/flagd/WASM_HOST_IMPORTS.md (96)

medium

This documentation shows creating a new SecureRandom instance on each call, which is inefficient. I've left a comment on FlagdWasmRuntime.java to suggest using a static SecureRandom instance. If you apply that change, please update this documentation to reflect the improved implementation (e.g., by adding a comment about the static SECURE_RANDOM field and using it here).

                SECURE_RANDOM.nextBytes(randomBytes);

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessWasmResolver.java (168)

medium

The shutdown() method is currently empty. For a clean shutdown, it should stop the stateWatcher thread and shut down the connector.

To do this, you'll need to:

  1. Promote the stateWatcher local variable in init() to a class field: private Thread stateWatcher;
  2. In init(), assign to this field: this.stateWatcher = new Thread(...).
  3. Implement the shutdown() method as suggested to interrupt the thread and close the connector.
    public void shutdown() throws InterruptedException {
        if (stateWatcher != null) {
            stateWatcher.interrupt();
        }
        connector.shutdown();
    }

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessWasmResolver.java (264-532)

medium

This file contains a significant amount of unused code. The methods getFlagMetadata, addEntryToMetadataBuilder, and all the create... methods for host functions (from createObjectDropRef to createWbindgenError) appear to be dead code, likely leftovers from a previous implementation or experimentation. The required host functions are already correctly defined and provided by FlagdWasmRuntime. Removing this unused code will improve maintainability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants