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

feat: make provider interface "stateless" #299

Merged
merged 9 commits into from
Nov 20, 2024

Conversation

warber
Copy link
Contributor

@warber warber commented Oct 31, 2024

This PR

State management for providers is now handled internally, eliminating the need for providers to implement or maintain their own state. The associated method has been removed from the interface, simplifying provider implementations.
State updates occur both during provider initialization and dynamically at runtime through various events emitted by the provider implementation.

Related Issues

fixes #258

Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
@@ -975,7 +988,7 @@ func TestEventHandler_HandlersRunImmediately(t *testing.T) {
rsp <- e
}

AddHandler(ProviderReady, &callback)
//AddHandler(ProviderReady, &callback) // TODO: hard to test, because callback is executed on handler registration
Copy link
Member

Choose a reason for hiding this comment

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

Is this TODO still valid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unfortunately, yes. The RDY callback comes from emitOnRegistration. Now that i cannot mock the state via the provider implementation the testing this becomes a bit harder.

@@ -41,6 +41,7 @@ type Client struct {
metadata ClientMetadata
hooks []Hook
evaluationContext EvaluationContext
name string
Copy link
Member

@toddbaert toddbaert Nov 5, 2024

Choose a reason for hiding this comment

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

Suggested change
name string
domain string

name has been renamed to domain in the spec (see change for details). I think there's a few places we need to update param names, etc, but if we're adding new fields, prefer domain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, thx i will rename that one. For the other ocurrences. I just saw that the old/wrong terminology is used in quite a lot of places. Wdyt about if I just open another PR that contains all the renaming?

Copy link
Member

Choose a reason for hiding this comment

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

You can open a PR or even just an issue. 🙏

if err := SetProviderAndWait(provider); err != nil {
t.Fatalf("failed to set up provider: %v", err)
}
eventually(t, func() bool {
Copy link
Member

@toddbaert toddbaert Nov 5, 2024

Choose a reason for hiding this comment

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

I think we can get rid of all the eventually's here, and in similar tests, since you are using SetProviderAndWait, which blocks. In fact, I think that will increase the quality of these tests.

I also think it might reduce test interference and increase certainly if you pass t.Name() into all GetClient calls as the domain: GetApiInstance().GetClient(t.Name()), so that we can be even more confident there's no test interference through the singleton.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's a good idea :) will do. And you are absolutely right, no need for eventually in a lot of places 👌

// is in NOT_READY.
func TestRequirement_1_7_6(t *testing.T) {
defer t.Cleanup(initSingleton)
t.Skip("Test not yet implemented")
Copy link
Member

Choose a reason for hiding this comment

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

Did you mean to implement these? (1.7.6 - 1.7.8)?

It seems to me that the short-circuiting logic is missing for NOT_READY and FATAL, at least. I think you'd need to implement that in client.evaluate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, let's do that in a another PR

Copy link
Member

Choose a reason for hiding this comment

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

I think you can complete these now, yes? Or would you rather leave them to another PR?

@toddbaert toddbaert self-requested a review November 5, 2024 15:35
Copy link
Member

@toddbaert toddbaert left a comment

Choose a reason for hiding this comment

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

Great progress! I have 2 concerns: this and this.

Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
@warber warber force-pushed the provider-no-state branch from 9782969 to b482e5e Compare November 6, 2024 17:29
When triggering an evaluation while the provider is in "not ready" or "fatal state" the sdk shall
return an appropriate error.

Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
@warber warber force-pushed the provider-no-state branch from b482e5e to 9695ff6 Compare November 6, 2024 18:22
Comment on lines 1 to 23
package openfeature

import (
"errors"
"fmt"
)

// ProviderInitError represents an error that occurs during provider initialization.
type ProviderInitError struct {
ErrorCode ErrorCode // Field to store the specific error code
Message string // Custom error message
}

// Error implements the error interface for ProviderInitError.
func (e *ProviderInitError) Error() string {
return fmt.Sprintf("ProviderInitError: %s (code: %s)", e.Message, e.ErrorCode)
}

// ProviderNotReadyError signifies that an operation failed because the provider is in a NOT_READY state.
var ProviderNotReadyError = errors.New("provider not yet initialized")

// ProviderFatalError signifies that an operation failed because the provider is in a FATAL state.
var ProviderFatalError = errors.New("provider is in an irrecoverable error state")
Copy link
Member

Choose a reason for hiding this comment

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

I think these contents should be moved to resolution_error.go where we already have some error consts and types.

@toddbaert toddbaert self-requested a review November 6, 2024 18:42
Copy link
Member

@toddbaert toddbaert left a comment

Choose a reason for hiding this comment

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

Almost there, I think, please check this and this.

Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
Signed-off-by: Bernd Warmuth <bernd.warmuth@dynatrace.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Copy link
Member

@toddbaert toddbaert left a comment

Choose a reason for hiding this comment

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

Hey @warber ! (sorry about the wait, been quite busy at KubeCon). This looks great to me now. I will merge this by the middle of next week unless I hear other objections from the TC or maintainers.

cc @kinyoklion @lukas-reining @thomaspoignant @justinabrahms

@toddbaert toddbaert merged commit 510b2a6 into open-feature:main Nov 20, 2024
6 checks passed
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.

[FEATURE] Make provider interface "stateless", SDK maintains provider state
3 participants