Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProviderEvaluation is hard to serialize / deserialize #483

Closed
pulse00 opened this issue Jun 20, 2023 · 15 comments
Closed

ProviderEvaluation is hard to serialize / deserialize #483

pulse00 opened this issue Jun 20, 2023 · 15 comments

Comments

@pulse00
Copy link

pulse00 commented Jun 20, 2023

We're trying to build a custom REST backend using spring that returns ProviderEvaluation objects serialized to json.

The current ProviderEvaluation class makes it very hard to deserialize it using jackson, because it uses lombok Builder and does not expose an empty default constructor.

Would it be possible to add the Jacksonized lombok annotation, so deserialization of these instances is possible?

@Kavindu-Dodan
Copy link
Contributor

Kavindu-Dodan commented Jun 21, 2023

@pulse00 thank you for bringing this up

I checked the implementation and we already have @Data annotation which helps jackson with simple fields. However, this is not true for ImmutableMetadata field [1] which fails for serialization. I will check what we can do from SDK point of view. Especially on the fact that we have an agreement to support serialization/deserialization of the mentioned class.

A good alternative here would be to utilize a custom transporter type for your API, which you can wrap the ProviderEvaluation into with se/deserialize support

[1] - https://github.com/open-feature/java-sdk/blob/main/src/main/java/dev/openfeature/sdk/ProviderEvaluation.java#L17

@toddbaert
Copy link
Member

toddbaert commented Jun 22, 2023

I don't see why we shouldn't attempt to support this. It certainly makes sense and it seems like it won't really add much of a maintenance burden or any additional dependencies.

@Kavindu-Dodan
Copy link
Contributor

@toddbaert yes, agree with this.

I opened the PR #487 with a solution and also added unite tests to validate the support. Feel free to review and provide feedback.

@toddbaert
Copy link
Member

toddbaert commented Jun 23, 2023

It seems like this would require us to add Jackson dependencies after all. See thread here.

One of the core goals of the OpenFeature SDKs is to keep them low/no dependency. If there's a way to help @pulse00 out without adding one, I think that's the way to go.

We could add an empty constructor (with lombok's @NoArgsConstructor) at least, if that would help (we'd just also have to add @AllArgsConstructor to satisfy lombok's @Builder).

@toddbaert
Copy link
Member

toddbaert commented Jun 28, 2023

@pulse00 does #491 help your situation at all?

@pulse00
Copy link
Author

pulse00 commented Jun 28, 2023

@pulse00 does #491 help your situation at all?

no, but having an empty constructor should help #491

i will keep you updated if this works now with jackson.

On a more generic side: Are there any recommendations on how to implement a REST API that maps to the FeatureProvider interface? That's basically the reason i brought this up, because if you implement such an API with e.g. spring, you will need to serialize/deserialize ProviderEvaluation instances at some point?

Or am i missing something?

@pulse00
Copy link
Author

pulse00 commented Jul 11, 2023

I'm still unsure how one would implement a REST API in java that can be consumed for example by a javascript feature provider.

Right now i'm trying to return ProviderEvaluation<Value> from my spring controller, but the Value class is not really serialserializable at the moment as well, because the innerObject field is private.

Are there any recommendations on how to implement a REST api using the java SDK?

@Kavindu-Dodan
Copy link
Contributor

Kavindu-Dodan commented Jul 11, 2023

@pulse00 seems what you want is a way to transmit ProviderEvaluation as it is from your Java backend to the front end written in Javascript. Personally, I recommend decoupling Rest API DTO from your internals. In this case, ProviderEvaluation is an internal data structure that is not designed to be used by remote evaluations. The reason for this is that our current SDKs focus on in-process evaluations. Also, as this closed PR #487 suggests, it's not possible to support all de/serialization frameworks Java supports.

My recommendation would be to use a custom DTO with conversion logic for your use case. This will help your rest API to be more stable, decoupled from the internals of your Java backend and further add your own custom data where needed.

@pulse00
Copy link
Author

pulse00 commented Jul 12, 2023

@Kavindu-Dodan makes sense. I somehow assumed because it's an implementation of a spec, that the types should also be transported over the wire. We'll go with converters then. thanks for the input!

Some input from me as a library user: The documentation was leading me a bit in the wrong direction i think. Because we can't use any of the existing providers, we started implementing our own using java.

Maybe it would be helpful to add a recommendation to the providers section, on how to implement a custom evaluation backend using an existing provider from an existing SDK?

@Kavindu-Dodan
Copy link
Contributor

Kavindu-Dodan commented Jul 12, 2023

@pulse00 I think you brought a unique use case to the discussion. Right now our SDKs are meant to be used by a single application. We can call this in-process evaluation where SDK connects to a OpenFeature provider where the provider obtains feature flags from an external system (see the diagram of the documentation [1]). Hence, the entities we define in SDK are not meant to be de/serialized.

Maybe it would be helpful to add a recommendation to the providers section, on how to implement a custom evaluation backend using an existing provider from an existing SDK?

I think this is connected to the ongoing enhancement proposal we are discussing here [2]. With the current SDK model, this is not supported. But as we discussed, you can rely on custom DTOs to transmit evaluation data from your backend to a client (front end).

[1] - https://openfeature.dev/docs/reference/concepts/provider
[2] - open-feature/ofep#67

@toddbaert
Copy link
Member

toddbaert commented Jul 12, 2023

Hey @pulse00 thanks for your comments here. I think I'm starting to understand your use-case a bit more now, and it's different than what I thought initially. I believe you're trying to do a few things the project isn't yet designed for (though interestingly, we have been working on a way to solve exactly your problem; more on that later).

I'd like to clarify a few things:

  • The SDK is not designed to help create a feature-flag backend. It's been designed to be a "bridge" between a vendor SDK (or a custom backend) and application code, running in the same process. In other words, the SDK is build to sit between your application code, and {insert-vendor-sdk} or some custom REST interfaces for a feature-flag API you'd build and run in-house. This is why we don't want to include serialization dependencies like Jackson into the SDK. They would not be used in most use-cases.
  • Consistent with the above, SDK objects (like the ProviderEvaluation) aren't meant to be DTOs. We expect the objects returned from some vendor SDK or HTTP client to be converted to these.

However, we are very interested in solving the sort of problem you're working on (I'm assuming based on the conversations above, you're basically trying to build a "proxy" for a feature-flag vendor or system, to expose it to web clients or otherwise). We've been working on something to help with this problem here. To sum it up, we're working on specifying a wire-protocol (hopefully both gRPC and HTTP) that could be easily implemented in a server to expose a standard feature-flag evaluation API. We would then generate compatible providers that would be compliant with this protocol, which you could plug-in to the OpenFeature SDK. You could look at this as a "kit" to easily build a proxy like the one I believe you want.

I would encourage you to comment here if my assumptions above are correct.

@pulse00
Copy link
Author

pulse00 commented Jul 12, 2023

you're basically trying to build a "proxy" for a feature-flag vendor or system, to expose it to web clients or otherwise

Exactly that. We are working on a 10+ years old platform which has evolved over time, and each of the 20+ apps on the platform has their own way of dealing with feature flags. We thought we could give a more generic approach a try (using open-feature) to make it easier to query for feature flags between apps and/or clients.

I do understand that if you start from scratch with a specific feature-flag backend, you wouldn't need to write a custom provider. But in our case (and i assume a lot of projects that evolved over a couple of years), the way we would like to introduce this is by first starting with existing backend logic that is exposed via the provider API.

The Flag Evaluation Wire Protocol you linked above seems like an interesting idea to solve this topic for users that want to adopt open-feature without a dedicated vendor backend.

However, i'm wondering what your recommended approach is for projects like i described above? Because as a developer, i'm not sure if the benefit of having this SDK in our application code outweights the drawback of having to implement the serialization/deserialization of the protocol over the wire manually. If i do so, there's not much benefit over just having a custom REST api that exposes a similar API, as the java-sdk doesn't really do much other than implementing the spec.

I would encourage you to comment open-feature/ofep#67 if my assumptions above are correct.

Sure, will do!

@Kavindu-Dodan
Copy link
Contributor

What you have mentioned here is an interesting usecase

We thought we could give a more generic approach a try (using open-feature) to make it easier to query for feature flags between apps and/or clients.

This is still possible and maybe we could help you here.

..the way we would like to introduce this is by first starting with existing backend logic that is exposed via the provider API.

I think this is what we need to clear up.

There should be a backend that acts as the flag management system (see diagram [1]). From OpenFeature point of view, the provider interacts with this flag management system. For example, if you already have a database with feature flags, the flag management system implementation could simply expose the flags from that database. And this can be built from any preferred language.

Each OpenFeature SDK exposes a set of contracts. For example, check Java FeatureProvider [2]. And readily available OpenFeature compatible vendors expose their providers by implementing this interface (see flagd implementation [3]).
This provider implementation is a convertor/adopter from the flag management system to OpenFeature. In other words, OpenFeature provider implementation hides the REST API communication, DTO conversions and exposes feature flags in OpenFeature format. This is how we achieve vendor-agnostic feature flags.

Now if we map this to one of your JS Apps, this is how it will look like,

flowchart LR  
    subgraph JS APP 1
    OpenFeature[JS SDK] ---> ProviderX[ProviderX JS]
    end
    ProviderX[ProviderX JS] --> |REST API| FlagSystem[Flag Management System] 
Loading
  • Flag Management System - This will be a service that your write to expose your feature flags

  • REST API - This is a simple rest API that you define and control. Can be simple as accepting feature flag key and returning its value from the database. Do not bind our SDK entities here as this is not the intention of those entities.

  • ProviderX JS - This is a custom adopter that you write adhering to OpenFeature SDK contract.

You can have all the above sources internally but still use OpenFeature SDK. And eventually, if your org would think of migrating to a commercial flag vendor, you can simply exchange ProviderX with provider Y.

[1] - https://github.com/open-feature/spec/blob/main/specification/sections/02-providers.md#2-provider
[2] - https://github.com/open-feature/java-sdk/blob/main/src/main/java/dev/openfeature/sdk/FeatureProvider.java
[3] - https://github.com/open-feature/java-sdk-contrib/blob/main/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java#L60

@pulse00
Copy link
Author

pulse00 commented Jul 17, 2023

@Kavindu-Dodan thanks for the detailed response. We'll go with an internal backend then and a custom provider that handles the serialization/deserialization then.

@pulse00 pulse00 closed this as completed Jul 17, 2023
@Kavindu-Dodan
Copy link
Contributor

@Kavindu-Dodan thanks for the detailed response. We'll go with an internal backend then and a custom provider that handles the serialization/deserialization then.

Happy to help you. Feel free to reach us if you have further doubts.

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 a pull request may close this issue.

3 participants