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: Resurrected the STATIC flag reason. Documented the caching strategy. #224

Merged
merged 4 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
### Caching

`flagd` has a caching strategy implementable by providers that support server-to-client streaming.

#### Cacheable flags

`flagd` sets the `reason` of a flag evaluation as `STATIC` when no targeting rules are configured for the flag. A client can safely store the result of a static evaluation in its cache indefinitely (until the configuration of the flag changes, see [busting](#busting)).
skyerus marked this conversation as resolved.
Show resolved Hide resolved

Put simply in pseudocode:

```
if reason == "STATIC" {
isFlagCacheable = true
}
```



#### Busting
skyerus marked this conversation as resolved.
Show resolved Hide resolved

`flagd` emits events to the server-to-client stream, among these is the `configuration_change` event. The structure of this event is as such:

```
{
"type": "delete", // ENUM:["delete","write","update"]
"source": "/flag-configuration.json", // the source of the flag configuration
"flagKey": "foo"
}
```

A client should bust the cache of any flag found in a `configuration_change` event to prevent stale data.
17 changes: 10 additions & 7 deletions pkg/eval/json_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,16 +202,19 @@ func (je *JSONEvaluator) evaluateVariant(
}
// strip whitespace and quotes from the variant
variant = strings.ReplaceAll(strings.TrimSpace(result.String()), "\"", "")
}

// if this is a valid variant, return it
if _, ok := je.state.Flags[flagKey].Variants[variant]; ok {
return variant, model.TargetingMatchReason, nil
// if this is a valid variant, return it
if _, ok := je.state.Flags[flagKey].Variants[variant]; ok {
return variant, model.TargetingMatchReason, nil
}

je.Logger.DebugWithID(reqID, fmt.Sprintf("returning default variant for flagKey %s, variant is not valid", flagKey))
reason = model.DefaultReason
} else {
reason = model.StaticReason
}

// if it's not a valid variant, use the default value
je.Logger.DebugWithID(reqID, fmt.Sprintf("returning default variant for flagKey %s, variant is not valid", flagKey))
return je.state.Flags[flagKey].DefaultVariant, model.DefaultReason, nil
return je.state.Flags[flagKey].DefaultVariant, reason, nil
}

// validateDefaultVariants returns an error if any of the default variants aren't valid
Expand Down
10 changes: 5 additions & 5 deletions pkg/eval/json_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ func TestResolveBooleanValue(t *testing.T) {
reason string
errorCode string
}{
{StaticBoolFlag, nil, StaticBoolValue, model.DefaultReason, ""},
{StaticBoolFlag, nil, StaticBoolValue, model.StaticReason, ""},
{DynamicBoolFlag, map[string]interface{}{ColorProp: ColorValue}, StaticBoolValue, model.TargetingMatchReason, ""},
{StaticObjectFlag, nil, StaticBoolValue, model.ErrorReason, model.TypeMismatchErrorCode},
{MissingFlag, nil, StaticBoolValue, model.ErrorReason, model.FlagNotFoundErrorCode},
Expand Down Expand Up @@ -361,7 +361,7 @@ func TestResolveStringValue(t *testing.T) {
reason string
errorCode string
}{
{StaticStringFlag, nil, StaticStringValue, model.DefaultReason, ""},
{StaticStringFlag, nil, StaticStringValue, model.StaticReason, ""},
{DynamicStringFlag, map[string]interface{}{ColorProp: ColorValue}, DynamicStringValue, model.TargetingMatchReason, ""},
{StaticObjectFlag, nil, "", model.ErrorReason, model.TypeMismatchErrorCode},
{MissingFlag, nil, "", model.ErrorReason, model.FlagNotFoundErrorCode},
Expand Down Expand Up @@ -401,7 +401,7 @@ func TestResolveFloatValue(t *testing.T) {
reason string
errorCode string
}{
{StaticFloatFlag, nil, StaticFloatValue, model.DefaultReason, ""},
{StaticFloatFlag, nil, StaticFloatValue, model.StaticReason, ""},
{DynamicFloatFlag, map[string]interface{}{ColorProp: ColorValue}, DynamicFloatValue, model.TargetingMatchReason, ""},
{StaticObjectFlag, nil, 13, model.ErrorReason, model.TypeMismatchErrorCode},
{MissingFlag, nil, 13, model.ErrorReason, model.FlagNotFoundErrorCode},
Expand Down Expand Up @@ -441,7 +441,7 @@ func TestResolveIntValue(t *testing.T) {
reason string
errorCode string
}{
{StaticIntFlag, nil, StaticIntValue, model.DefaultReason, ""},
{StaticIntFlag, nil, StaticIntValue, model.StaticReason, ""},
{DynamicIntFlag, map[string]interface{}{ColorProp: ColorValue}, DynamicIntValue, model.TargetingMatchReason, ""},
{StaticObjectFlag, nil, 13, model.ErrorReason, model.TypeMismatchErrorCode},
{MissingFlag, nil, 13, model.ErrorReason, model.FlagNotFoundErrorCode},
Expand Down Expand Up @@ -481,7 +481,7 @@ func TestResolveObjectValue(t *testing.T) {
reason string
errorCode string
}{
{StaticObjectFlag, nil, StaticObjectValue, model.DefaultReason, ""},
{StaticObjectFlag, nil, StaticObjectValue, model.StaticReason, ""},
{DynamicObjectFlag, map[string]interface{}{ColorProp: ColorValue}, DynamicObjectValue, model.TargetingMatchReason, ""},
{StaticBoolFlag, nil, "{}", model.ErrorReason, model.TypeMismatchErrorCode},
{MissingFlag, nil, "{}", model.ErrorReason, model.FlagNotFoundErrorCode},
Expand Down
1 change: 1 addition & 0 deletions pkg/model/reason.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ const (
DefaultReason = "DEFAULT"
UnknownReason = "UNKNOWN"
ErrorReason = "ERROR"
StaticReason = "STATIC"
)