Skip to content

Commit

Permalink
Alternative target app filtering for GCIR (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
kenny-statsig authored Sep 24, 2024
1 parent 4e57bd9 commit a5d67e5
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 36 deletions.
24 changes: 24 additions & 0 deletions client_initialize_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,32 @@ func getClientInitializeResponse(
} else {
appId, _ = e.store.getAppIDForSDKKey(options.ClientKey)
}

filterByEntities := false
gatesLookup := make(map[string]bool)
configsLookup := make(map[string]bool)
if entities, ok := e.store.getEntitiesForSDKKey(options.ClientKey); ok {
filterByEntities = true
for _, gate := range entities.Gates {
gatesLookup[gate] = true
}
for _, config := range entities.Configs {
configsLookup[config] = true
}
}

featureGates := make(map[string]GateInitializeResponse)
dynamicConfigs := make(map[string]ConfigInitializeResponse)
layerConfigs := make(map[string]LayerInitializeResponse)
for name, spec := range e.store.featureGates {
if !spec.hasTargetAppID(appId) {
continue
}
if filterByEntities {
if _, ok := gatesLookup[name]; !ok {
continue
}
}
if !strings.EqualFold(spec.Entity, "segment") && !strings.EqualFold(spec.Entity, "holdout") {
hashedName, res := gateToResponse(name, spec)
featureGates[hashedName] = res
Expand All @@ -207,6 +226,11 @@ func getClientInitializeResponse(
if !spec.hasTargetAppID(appId) {
continue
}
if filterByEntities {
if _, ok := configsLookup[name]; !ok {
continue
}
}
hashedName, res := configToResponse(name, spec)
dynamicConfigs[hashedName] = res
}
Expand Down
87 changes: 51 additions & 36 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,24 @@ type configCondition struct {
}

type downloadConfigSpecResponse struct {
HasUpdates bool `json:"has_updates"`
Time int64 `json:"time"`
FeatureGates []configSpec `json:"feature_gates"`
DynamicConfigs []configSpec `json:"dynamic_configs"`
LayerConfigs []configSpec `json:"layer_configs"`
Layers map[string][]string `json:"layers"`
IDLists map[string]bool `json:"id_lists"`
DiagnosticsSampleRates map[string]int `json:"diagnostics"`
SDKKeysToAppID map[string]string `json:"sdk_keys_to_app_ids,omitempty"`
HashedSDKKeysToAppID map[string]string `json:"hashed_sdk_keys_to_app_ids,omitempty"`
HashedSDKKeyUsed string `json:"hashed_sdk_key_used,omitempty"`
SDKFlags map[string]bool `json:"sdk_flags,omitempty"`
HasUpdates bool `json:"has_updates"`
Time int64 `json:"time"`
FeatureGates []configSpec `json:"feature_gates"`
DynamicConfigs []configSpec `json:"dynamic_configs"`
LayerConfigs []configSpec `json:"layer_configs"`
Layers map[string][]string `json:"layers"`
IDLists map[string]bool `json:"id_lists"`
DiagnosticsSampleRates map[string]int `json:"diagnostics"`
SDKKeysToAppID map[string]string `json:"sdk_keys_to_app_ids,omitempty"`
HashedSDKKeysToAppID map[string]string `json:"hashed_sdk_keys_to_app_ids,omitempty"`
HashedSDKKeysToEntities map[string]configEntities `json:"hashed_sdk_keys_to_entities,omitempty"`
HashedSDKKeyUsed string `json:"hashed_sdk_key_used,omitempty"`
SDKFlags map[string]bool `json:"sdk_flags,omitempty"`
}

type configEntities struct {
Configs []string `json:"configs"`
Gates []string `json:"gates"`
}

type idList struct {
Expand All @@ -101,30 +107,31 @@ const (
)

type store struct {
featureGates map[string]configSpec
dynamicConfigs map[string]configSpec
layerConfigs map[string]configSpec
experimentToLayer map[string]string
sdkKeysToAppID map[string]string
hashedSDKKeysToAppID map[string]string
idLists map[string]*idList
lastSyncTime int64
initialSyncTime int64
initReason evaluationReason
initializedIDLists bool
transport *transport
configSyncInterval time.Duration
idListSyncInterval time.Duration
shutdown bool
rulesUpdatedCallback func(rules string, time int64)
errorBoundary *errorBoundary
dataAdapter IDataAdapter
syncFailureCount int
diagnostics *diagnostics
mu sync.RWMutex
sdkKey string
isPolling bool
bootstrapValues string
featureGates map[string]configSpec
dynamicConfigs map[string]configSpec
layerConfigs map[string]configSpec
experimentToLayer map[string]string
sdkKeysToAppID map[string]string
hashedSDKKeysToAppID map[string]string
hashedSDKKeysToEntities map[string]configEntities
idLists map[string]*idList
lastSyncTime int64
initialSyncTime int64
initReason evaluationReason
initializedIDLists bool
transport *transport
configSyncInterval time.Duration
idListSyncInterval time.Duration
shutdown bool
rulesUpdatedCallback func(rules string, time int64)
errorBoundary *errorBoundary
dataAdapter IDataAdapter
syncFailureCount int
diagnostics *diagnostics
mu sync.RWMutex
sdkKey string
isPolling bool
bootstrapValues string
}

var syncOutdatedMax = 2 * time.Minute
Expand Down Expand Up @@ -271,6 +278,13 @@ func (s *store) getAppIDForSDKKey(clientKey string) (string, bool) {
return appId, ok
}

func (s *store) getEntitiesForSDKKey(clientKey string) (configEntities, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
entities, ok := s.hashedSDKKeysToEntities[getDJB2Hash(clientKey)]
return entities, ok
}

func (s *store) fetchConfigSpecsFromAdapter() {
s.addDiagnostics().dataStoreConfigSpecs().fetch().start().mark()
defer func() {
Expand Down Expand Up @@ -447,6 +461,7 @@ func (s *store) setConfigSpecs(specs downloadConfigSpecResponse) (bool, bool) {
s.experimentToLayer = newExperimentToLayer
s.sdkKeysToAppID = specs.SDKKeysToAppID
s.hashedSDKKeysToAppID = specs.HashedSDKKeysToAppID
s.hashedSDKKeysToEntities = specs.HashedSDKKeysToEntities
s.lastSyncTime = specs.Time
s.mu.Unlock()
return true, true
Expand Down

0 comments on commit a5d67e5

Please sign in to comment.