Skip to content

Commit

Permalink
feat: Resurrected the STATIC flag reason. Documented the caching stra…
Browse files Browse the repository at this point in the history
…tegy. (#224)

Resurrected the STATIC flag reason. Documented the caching strategy.

Signed-off-by: Skye Gill <gill.skye95@gmail.com>
Co-authored-by: Todd Baert <toddbaert@gmail.com>
  • Loading branch information
skyerus and toddbaert authored Nov 30, 2022
1 parent c619844 commit 5830592
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 13 deletions.
29 changes: 29 additions & 0 deletions docs/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
### 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 [cache invalidation](#cache-invalidation)).

Put simply in pseudocode:

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

#### Cache invalidation

`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.
19 changes: 11 additions & 8 deletions pkg/eval/json_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (je *JSONEvaluator) evaluateVariant(
// get the targeting logic, if any
targeting := flag.Targeting

if targeting != nil {
if targeting != nil && string(targeting) != "{}" {
targetingBytes, err := targeting.MarshalJSON()
if err != nil {
je.Logger.ErrorWithID(reqID, fmt.Sprintf("Error parsing rules for flag %s, %s", flagKey, err))
Expand All @@ -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"
)

0 comments on commit 5830592

Please sign in to comment.