@@ -34,58 +34,53 @@ import (
34
34
//
35
35
// We have found this to almost always be undesirable for users.
36
36
type 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
40
40
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
41
58
// The persistentStore is read-only, or read-write. In read-only mode, the store
42
59
// is *never* written to, and only read before the in-memory store is initialized.
43
60
// This is equivalent to the concept of "daemon mode".
44
61
//
45
62
// In read-write mode, data from initializers/synchronizers is written to the store
46
63
// as it is received. This is equivalent to the normal "persistent store" configuration
47
64
// 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
50
66
// This exists as a quirk of the DataSourceUpdateSink interface, which store implements. The DataSourceUpdateSink
51
67
// has a method to return a DataStoreStatusProvider so that a DataSource can monitor the state of the store. This
52
68
// was originally used in fdv1 to know when the store went offline/online, so that data could be committed back
53
69
// to the store when it came back online. In fdv2 system, this is handled by the FDv2 struct itself, so the
54
70
// data source doesn't need any knowledge of it. We can delete this piece of infrastructure when we no longer
55
71
// need to support fdv1 (or we could refactor the fdv2 data sources to use a different set of interfaces that don't
56
72
// 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
78
74
}
79
75
80
76
// NewStore creates a new store. By default the store is in-memory. To add a persistent store, call SwapToPersistent. Ensure this is
81
77
// called at configuration time, only once and before the store is ever accessed.
82
78
func NewStore (loggers ldlog.Loggers ) * Store {
83
79
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 ,
89
84
}
90
85
s .active = s .memoryStore
91
86
return s
@@ -96,7 +91,7 @@ func (s *Store) Close() error {
96
91
s .mu .Lock ()
97
92
defer s .mu .Unlock ()
98
93
if s .persistentStore != nil {
99
- return s .persistentStore .Close ()
94
+ return s .persistentStore .impl . Close ()
100
95
}
101
96
return nil
102
97
}
@@ -109,17 +104,10 @@ func (s *Store) getActive() subsystems.DataStore {
109
104
return s .active
110
105
}
111
106
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
-
120
107
// Mirroring returns true data is being mirrored to a persistent store.
121
108
func (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
123
111
}
124
112
125
113
// nolint:revive // Standard DataSourceUpdateSink method
@@ -131,15 +119,11 @@ func (s *Store) Init(allData []ldstoretypes.Collection, payloadVersion *int) boo
131
119
// TODO: handle errors from initializing the memory or persistent stores.
132
120
if err := s .memoryStore .Init (allData ); err == nil {
133
121
s .active = s .memoryStore
134
- if payloadVersion != nil {
135
- s .availability = Refreshed
136
- } else {
137
- s .availability = Cached
138
- }
122
+ s .quality = QualityTrusted
139
123
}
140
124
141
125
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
143
127
}
144
128
return true
145
129
}
@@ -158,7 +142,7 @@ func (s *Store) Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes
158
142
_ , memErr = s .memoryStore .Upsert (kind , key , item )
159
143
160
144
if s .mirroring () {
161
- _ , persErr = s .persistentStore .Upsert (kind , key , item )
145
+ _ , persErr = s .persistentStore .impl . Upsert (kind , key , item )
162
146
}
163
147
return memErr == nil && persErr == nil
164
148
}
@@ -167,7 +151,10 @@ func (s *Store) Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes
167
151
func (s * Store ) GetDataStoreStatusProvider () interfaces.DataStoreStatusProvider {
168
152
s .mu .RLock ()
169
153
defer s .mu .RUnlock ()
170
- return s .persistentStoreStatusProvider
154
+ if s .persistentStore == nil {
155
+ return nil
156
+ }
157
+ return s .persistentStore .statusProvider
171
158
}
172
159
173
160
// 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
176
163
func (s * Store ) WithPersistence (persistent subsystems.DataStore , mode subsystems.DataStoreMode , statusProvider interfaces.DataStoreStatusProvider ) * Store {
177
164
s .mu .Lock ()
178
165
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 ,
188
171
}
172
+
173
+ s .active = s .persistentStore .impl
174
+ s .quality = QualityUntrusted
189
175
return s
190
176
}
191
177
192
178
func (s * Store ) Commit () error {
193
179
s .mu .RLock ()
194
180
defer s .mu .RUnlock ()
195
181
196
- if s .availability == Refreshed && s . mirroring () {
182
+ if s .mirroring () {
197
183
flags , err := s .memoryStore .GetAll (datakinds .Features )
198
184
if err != nil {
199
185
return err
@@ -202,7 +188,7 @@ func (s *Store) Commit() error {
202
188
if err != nil {
203
189
return err
204
190
}
205
- return s .persistentStore .Init ([]ldstoretypes.Collection {
191
+ return s .persistentStore .impl . Init ([]ldstoretypes.Collection {
206
192
{Kind : datakinds .Features , Items : flags },
207
193
{Kind : datakinds .Segments , Items : segments },
208
194
})
@@ -221,3 +207,17 @@ func (s *Store) Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.ItemDe
221
207
func (s * Store ) IsInitialized () bool {
222
208
return s .getActive ().IsInitialized ()
223
209
}
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