Skip to content

Commit

Permalink
fix panic concurrent map writes when constructing metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
nabiel-flik committed Mar 20, 2024
1 parent 71343ed commit 80d7234
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 12 deletions.
3 changes: 2 additions & 1 deletion variation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ffclient

import (
"fmt"
"maps"
"time"

"github.com/thomaspoignant/go-feature-flag/exporter"
Expand Down Expand Up @@ -468,7 +469,7 @@ func getVariation[T model.JSONType](
// the targeting.rule's name (from configuration) to the Metadata.
// That way, it is possible to see when a targeting rule is match during the evaluation process.
func constructMetadata(f flag.Flag, resolutionDetails flag.ResolutionDetails) map[string]interface{} {
metadata := f.GetMetadata()
metadata := maps.Clone(f.GetMetadata())
if resolutionDetails.RuleName == nil || *resolutionDetails.RuleName == "" {
return metadata
}
Expand Down
67 changes: 56 additions & 11 deletions variation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,25 @@ import (
"context"
"encoding/json"
"errors"
"github.com/thomaspoignant/go-feature-flag/exporter"
"github.com/thomaspoignant/go-feature-flag/ffcontext"
"github.com/thomaspoignant/go-feature-flag/internal/dto"
"fmt"
"log"
"os"
"runtime"
"testing"
"time"

"github.com/thomaspoignant/go-feature-flag/testutils/flagv1"

"github.com/thomaspoignant/go-feature-flag/model"

"github.com/stretchr/testify/assert"
"github.com/thomaspoignant/go-feature-flag/exporter"
"github.com/thomaspoignant/go-feature-flag/exporter/fileexporter"

"github.com/thomaspoignant/go-feature-flag/exporter/logsexporter"
"github.com/thomaspoignant/go-feature-flag/retriever/fileretriever"

"github.com/stretchr/testify/assert"
"github.com/thomaspoignant/go-feature-flag/ffcontext"
"github.com/thomaspoignant/go-feature-flag/internal/cache"
"github.com/thomaspoignant/go-feature-flag/internal/dto"
"github.com/thomaspoignant/go-feature-flag/internal/flag"
"github.com/thomaspoignant/go-feature-flag/model"
"github.com/thomaspoignant/go-feature-flag/retriever/fileretriever"
"github.com/thomaspoignant/go-feature-flag/testutils"
"github.com/thomaspoignant/go-feature-flag/testutils/flagv1"
"github.com/thomaspoignant/go-feature-flag/testutils/testconvert"
)

Expand Down Expand Up @@ -3945,3 +3943,50 @@ func TestRawVariation(t *testing.T) {
})
}
}

func Test_constructMetadataParallel(t *testing.T) {
sharedFlag := flag.InternalFlag{
Metadata: &map[string]interface{}{
"key1": "value1",
},
}

type args struct {
resolutionDetails flag.ResolutionDetails
}
tests := []struct {
name string
args args
wantEvaluatedRuleName string
}{}

runtime.GOMAXPROCS(runtime.NumCPU())

// generate test cases
for i := 0; i < 10_000; i++ {
ruleName := fmt.Sprintf("rule-%d", i)
tests = append(tests, struct {
name string
args args
wantEvaluatedRuleName string
}{
name: fmt.Sprintf("Rule %d", i),
args: args{
resolutionDetails: flag.ResolutionDetails{
RuleName: &ruleName,
},
},
wantEvaluatedRuleName: ruleName,
})
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

got := constructMetadata(&sharedFlag, tt.args.resolutionDetails)
assert.Equal(t, tt.wantEvaluatedRuleName, got["evaluatedRuleName"])
})
}
}

0 comments on commit 80d7234

Please sign in to comment.