@@ -34,58 +34,53 @@ import (
3434//
3535// We have found this to almost always be undesirable for users.
3636type Store struct {
37- // Represents a remote store, like Redis. This is optional; if present, it's only used
38- // before the in-memory store is initialized .
39- persistentStore subsystems. DataStore
37+ // Represents the SDK's source of truth for flag evals before initialization, or permanently if there are
38+ // no initializers/synchronizers configured. This is option; if not defined, only the memoryStore is used .
39+ persistentStore * persistentStore
4040
41+ // Represents the SDK's source of truth for flag evaluations (once initialized). Before initialization,
42+ // the persistentStore may be used if configured.
43+ memoryStore subsystems.DataStore
44+
45+ // Points to the active store. Swapped upon initialization.
46+ active subsystems.DataStore
47+
48+ quality DataQuality
49+
50+ // Protects the availability, persistentStore, quality, and active fields.
51+ mu sync.RWMutex
52+
53+ loggers ldlog.Loggers
54+ }
55+
56+ type persistentStore struct {
57+ impl subsystems.DataStore
4158 // The persistentStore is read-only, or read-write. In read-only mode, the store
4259 // is *never* written to, and only read before the in-memory store is initialized.
4360 // This is equivalent to the concept of "daemon mode".
4461 //
4562 // In read-write mode, data from initializers/synchronizers is written to the store
4663 // as it is received. This is equivalent to the normal "persistent store" configuration
4764 // that an SDK can use to collaborate with zero or more other SDKs with a (possibly shared) database.
48- persistentStoreMode subsystems.DataStoreMode
49-
65+ mode subsystems.DataStoreMode
5066 // This exists as a quirk of the DataSourceUpdateSink interface, which store implements. The DataSourceUpdateSink
5167 // has a method to return a DataStoreStatusProvider so that a DataSource can monitor the state of the store. This
5268 // was originally used in fdv1 to know when the store went offline/online, so that data could be committed back
5369 // to the store when it came back online. In fdv2 system, this is handled by the FDv2 struct itself, so the
5470 // data source doesn't need any knowledge of it. We can delete this piece of infrastructure when we no longer
5571 // need to support fdv1 (or we could refactor the fdv2 data sources to use a different set of interfaces that don't
5672 // require this.)
57- persistentStoreStatusProvider interfaces.DataStoreStatusProvider
58-
59- // Represents the store that all flag/segment data queries are served from after data is received from
60- // initializers/synchronizers. Before the in-memory store is initialized, queries are served from the
61- // persistentStore (if configured).
62- memoryStore subsystems.DataStore
63-
64- active subsystems.DataStore
65-
66- // Whether the memoryStore's data should be considered authoritative, or fresh - that is, if it is known
67- // to be the latest data. Data from a baked in file for example would not be considered refreshed. The purpose
68- // of this is to know if we should commit data to the persistentStore. For example, if we initialize with "stale"
69- // data from a local file (refreshed=false), we may not want to pollute a connected Redis database with it.
70- // TODO: this could also be called "Authoritative". "It was the latest at some point.. that point being when we asked
71- // if it was the latest".
72- availability DataAvailability
73-
74- // Protects the refreshed, persistentStore, persistentStoreMode, and active fields.
75- mu sync.RWMutex
76-
77- loggers ldlog.Loggers
73+ statusProvider interfaces.DataStoreStatusProvider
7874}
7975
8076// NewStore creates a new store. By default the store is in-memory. To add a persistent store, call SwapToPersistent. Ensure this is
8177// called at configuration time, only once and before the store is ever accessed.
8278func NewStore (loggers ldlog.Loggers ) * Store {
8379 s := & Store {
84- persistentStore : nil ,
85- persistentStoreMode : subsystems .DataStoreModeRead ,
86- memoryStore : datastore .NewInMemoryDataStore (loggers ),
87- availability : Defaults ,
88- loggers : loggers ,
80+ persistentStore : nil ,
81+ memoryStore : datastore .NewInMemoryDataStore (loggers ),
82+ quality : QualityNone ,
83+ loggers : loggers ,
8984 }
9085 s .active = s .memoryStore
9186 return s
@@ -96,7 +91,7 @@ func (s *Store) Close() error {
9691 s .mu .Lock ()
9792 defer s .mu .Unlock ()
9893 if s .persistentStore != nil {
99- return s .persistentStore .Close ()
94+ return s .persistentStore .impl . Close ()
10095 }
10196 return nil
10297}
@@ -109,17 +104,10 @@ func (s *Store) getActive() subsystems.DataStore {
109104 return s .active
110105}
111106
112- // DataAvailability returns the status of the store's data. Defaults means there is no data, Cached means there is
113- // data, but it's not guaranteed to be recent, and Refreshed means the data has been refreshed from the server.
114- func (s * Store ) DataAvailability () DataAvailability {
115- s .mu .RLock ()
116- defer s .mu .RUnlock ()
117- return s .availability
118- }
119-
120107// Mirroring returns true data is being mirrored to a persistent store.
121108func (s * Store ) mirroring () bool {
122- return s .persistentStore != nil && s .persistentStoreMode == subsystems .DataStoreModeReadWrite
109+ return s .persistentStore != nil && s .persistentStore .mode == subsystems .DataStoreModeReadWrite &&
110+ s .quality == QualityTrusted
123111}
124112
125113// nolint:revive // Standard DataSourceUpdateSink method
@@ -131,15 +119,11 @@ func (s *Store) Init(allData []ldstoretypes.Collection, payloadVersion *int) boo
131119 // TODO: handle errors from initializing the memory or persistent stores.
132120 if err := s .memoryStore .Init (allData ); err == nil {
133121 s .active = s .memoryStore
134- if payloadVersion != nil {
135- s .availability = Refreshed
136- } else {
137- s .availability = Cached
138- }
122+ s .quality = QualityTrusted
139123 }
140124
141125 if s .mirroring () {
142- _ = s .persistentStore .Init (allData ) // TODO: insert in topo-sort order
126+ _ = s .persistentStore .impl . Init (allData ) // TODO: insert in topo-sort order
143127 }
144128 return true
145129}
@@ -158,7 +142,7 @@ func (s *Store) Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes
158142 _ , memErr = s .memoryStore .Upsert (kind , key , item )
159143
160144 if s .mirroring () {
161- _ , persErr = s .persistentStore .Upsert (kind , key , item )
145+ _ , persErr = s .persistentStore .impl . Upsert (kind , key , item )
162146 }
163147 return memErr == nil && persErr == nil
164148}
@@ -167,7 +151,10 @@ func (s *Store) Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes
167151func (s * Store ) GetDataStoreStatusProvider () interfaces.DataStoreStatusProvider {
168152 s .mu .RLock ()
169153 defer s .mu .RUnlock ()
170- return s .persistentStoreStatusProvider
154+ if s .persistentStore == nil {
155+ return nil
156+ }
157+ return s .persistentStore .statusProvider
171158}
172159
173160// WithPersistence exists only because of the way the SDK's configuration builders work - we need a ClientContext
@@ -176,24 +163,23 @@ func (s *Store) GetDataStoreStatusProvider() interfaces.DataStoreStatusProvider
176163func (s * Store ) WithPersistence (persistent subsystems.DataStore , mode subsystems.DataStoreMode , statusProvider interfaces.DataStoreStatusProvider ) * Store {
177164 s .mu .Lock ()
178165 defer s .mu .Unlock ()
179- s .persistentStore = persistent
180- s .persistentStoreMode = mode
181- s .persistentStoreStatusProvider = statusProvider
182- s .active = s .persistentStore
183-
184- if s .persistentStore .IsInitialized () {
185- s .availability = Cached
186- } else {
187- s .availability = Defaults
166+
167+ s .persistentStore = & persistentStore {
168+ impl : persistent ,
169+ mode : mode ,
170+ statusProvider : statusProvider ,
188171 }
172+
173+ s .active = s .persistentStore .impl
174+ s .quality = QualityUntrusted
189175 return s
190176}
191177
192178func (s * Store ) Commit () error {
193179 s .mu .RLock ()
194180 defer s .mu .RUnlock ()
195181
196- if s .availability == Refreshed && s . mirroring () {
182+ if s .mirroring () {
197183 flags , err := s .memoryStore .GetAll (datakinds .Features )
198184 if err != nil {
199185 return err
@@ -202,7 +188,7 @@ func (s *Store) Commit() error {
202188 if err != nil {
203189 return err
204190 }
205- return s .persistentStore .Init ([]ldstoretypes.Collection {
191+ return s .persistentStore .impl . Init ([]ldstoretypes.Collection {
206192 {Kind : datakinds .Features , Items : flags },
207193 {Kind : datakinds .Segments , Items : segments },
208194 })
@@ -221,3 +207,17 @@ func (s *Store) Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.ItemDe
221207func (s * Store ) IsInitialized () bool {
222208 return s .getActive ().IsInitialized ()
223209}
210+
211+ type DataQuality int
212+
213+ const (
214+ QualityNone = DataQuality (0 )
215+ QualityUntrusted = DataQuality (1 )
216+ QualityTrusted = DataQuality (2 )
217+ )
218+
219+ func (s * Store ) DataQuality () DataQuality {
220+ s .mu .RLock ()
221+ defer s .mu .RUnlock ()
222+ return s .quality
223+ }
0 commit comments