Skip to content

Commit 1d72786

Browse files
authored
FFM-12699 - Update refresher to only pull segments that have changed (#421)
* FFM-12699 - Update refresher to only pull segments that have changed
1 parent 9b657ac commit 1d72786

File tree

2 files changed

+93
-15
lines changed

2 files changed

+93
-15
lines changed

cache/refresher.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -448,17 +448,37 @@ func (s Refresher) updateFeatureConfigsEntry(ctx context.Context, env string, id
448448
})
449449
}
450450

451-
func (s Refresher) handleFetchSegmentEvent(ctx context.Context, env, id string) error {
452-
s.log.Debug("updating featureConfig entry", "environment", env, "identifier", id)
451+
// handleFetchSegmentEvent handles any PATCH/CREATE Segment events that we receive from Harness Saas.
452+
//
453+
// When we get a PATCH/CREATE segment event there are three keys in the cache that we need to update.
454+
// 1. env-<id>-segment-<identifier> entry which is the individual segment record for that environment
455+
// 2. env-<id>-segments entry which is the collection of segments for that environment. Since a change has been made to a flag in this environment, we need to update that specific segment in this collection.
456+
// 3. inventory entry for the segments. This Inventory entry tracks all the resources in the cache associated with the Proxy key. It's used for cache cleanup and diffing assets during stream disconnects.
457+
//
458+
// In order to ensure we update these records correctly, when we get a PATCH/CREATE feature event we need to do the following:
459+
// 1. Fetch the segment from HarnessSaas.
460+
// 2. Update the individual segment record in the cache.
461+
// 3. Update the segments record in the cache.
462+
// 4. Update the inventory record for the segment.
463+
func (s Refresher) handleFetchSegmentEvent(ctx context.Context, env, identifier string) error {
464+
s.log.Debug("updating featureConfig entry", "environment", env, "identifier", identifier)
453465

454-
segmentConfig, err := s.clientService.FetchSegmentConfigForEnvironment(ctx, s.config.Token(), s.config.ClusterIdentifier(), env)
466+
sc, err := s.clientService.GetSegmentByIdentifier(ctx, domain.GetSegmentByIdentifierInput{
467+
AuthToken: s.config.Token(),
468+
Cluster: s.config.ClusterIdentifier(),
469+
EnvID: env,
470+
Identifier: identifier,
471+
})
455472
if err != nil {
456-
return err
473+
return fmt.Errorf("failed to get segment by identifier: %w", err)
457474
}
458-
segments := make([]domain.Segment, 0, len(segmentConfig))
459-
for _, v := range segmentConfig {
460-
segments = append(segments, domain.Segment(v))
475+
updatedSegment := domain.Segment(sc)
476+
477+
segments, ok := s.segmentRepo.GetSegmentsForEnvironment(ctx, env)
478+
if !ok {
479+
return fmt.Errorf("failed to get segment for environment %s", env)
461480
}
481+
replaceSegmentConfig(updatedSegment, &segments)
462482

463483
if err := s.segmentRepo.Add(ctx, domain.SegmentConfig{
464484
EnvironmentID: env,
@@ -600,3 +620,16 @@ func (s Refresher) handleDeleteProxyKeyEvent(ctx context.Context) error {
600620
return s.inventory.Remove(ctx, keyInventoryEntry)
601621

602622
}
623+
624+
func replaceSegmentConfig(newConfig domain.Segment, segmentConfigs *[]domain.Segment) {
625+
if segmentConfigs == nil {
626+
return
627+
}
628+
629+
for i := range *segmentConfigs {
630+
if (*segmentConfigs)[i].Identifier == newConfig.Identifier {
631+
(*segmentConfigs)[i] = newConfig
632+
break
633+
}
634+
}
635+
}

cache/refresher_test.go

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,9 @@ func TestRefresher_HandleMessage(t *testing.T) {
116116
Event: domain.EventPatch,
117117
},
118118
},
119-
mocks: mocks{clientService: mockClientService{FetchSegmentConfigForEnvironmentFn: func(ctx context.Context, authToken, envId string) ([]clientgen.Segment, error) {
120-
return []clientgen.Segment{
121-
{Identifier: "foo"},
122-
{Identifier: "bar"},
119+
mocks: mocks{clientService: mockClientService{getSegmentByIdentifier: func(ctx context.Context, input domain.GetSegmentByIdentifierInput) (clientgen.Segment, error) {
120+
return clientgen.Segment{
121+
Identifier: "bar",
123122
}, nil
124123
}}},
125124
expected: expected{err: nil},
@@ -132,10 +131,9 @@ func TestRefresher_HandleMessage(t *testing.T) {
132131
Event: domain.EventCreate,
133132
},
134133
},
135-
mocks: mocks{clientService: mockClientService{FetchSegmentConfigForEnvironmentFn: func(ctx context.Context, authToken, envId string) ([]clientgen.Segment, error) {
136-
return []clientgen.Segment{
137-
{Identifier: "foo"},
138-
{Identifier: "bar"},
134+
mocks: mocks{clientService: mockClientService{getSegmentByIdentifier: func(ctx context.Context, input domain.GetSegmentByIdentifierInput) (clientgen.Segment, error) {
135+
return clientgen.Segment{
136+
Identifier: "foo",
139137
}, nil
140138
}}},
141139
expected: expected{err: nil},
@@ -987,3 +985,50 @@ func TestReplaceFeatureConfig(t *testing.T) {
987985
})
988986
}
989987
}
988+
989+
func TestReplaceSegmentConfig(t *testing.T) {
990+
var version1 int64 = 1
991+
var version2 int64 = 2
992+
993+
tests := []struct {
994+
name string
995+
initialConfigs []domain.Segment
996+
newConfig domain.Segment
997+
expected []domain.Segment
998+
}{
999+
{
1000+
name: "replaces matching segment",
1001+
initialConfigs: []domain.Segment{
1002+
{Identifier: "featA", Version: &version1},
1003+
{Identifier: "featB", Version: &version1},
1004+
},
1005+
newConfig: domain.Segment{Identifier: "featA", Version: &version2},
1006+
expected: []domain.Segment{
1007+
{Identifier: "featA", Version: &version2},
1008+
{Identifier: "featB", Version: &version1},
1009+
},
1010+
},
1011+
{
1012+
name: "no matching segment - no change",
1013+
initialConfigs: []domain.Segment{
1014+
{Identifier: "featA", Version: &version1},
1015+
},
1016+
newConfig: domain.Segment{Identifier: "featB", Version: &version2},
1017+
expected: []domain.Segment{
1018+
{Identifier: "featA", Version: &version1},
1019+
},
1020+
},
1021+
}
1022+
1023+
for _, tt := range tests {
1024+
tt := tt
1025+
1026+
t.Run(tt.name, func(t *testing.T) {
1027+
configs := append([]domain.Segment{}, tt.initialConfigs...) // copy to avoid mutation
1028+
replaceSegmentConfig(tt.newConfig, &configs)
1029+
if !reflect.DeepEqual(configs, tt.expected) {
1030+
t.Errorf("expected %+v, got %+v", tt.expected, configs)
1031+
}
1032+
})
1033+
}
1034+
}

0 commit comments

Comments
 (0)