3
3
import com .google .common .collect .ImmutableMap ;
4
4
import com .google .common .collect .ImmutableSet ;
5
5
import com .launchdarkly .sdk .server .DataModelDependencies .KindAndKey ;
6
+ import com .launchdarkly .sdk .server .interfaces .DataSourceStatusProvider ;
7
+ import com .launchdarkly .sdk .server .interfaces .DataSourceStatusProvider .ErrorInfo ;
8
+ import com .launchdarkly .sdk .server .interfaces .DataSourceStatusProvider .ErrorKind ;
9
+ import com .launchdarkly .sdk .server .interfaces .DataSourceStatusProvider .State ;
10
+ import com .launchdarkly .sdk .server .interfaces .DataSourceStatusProvider .Status ;
11
+ import com .launchdarkly .sdk .server .interfaces .DataSourceUpdates ;
6
12
import com .launchdarkly .sdk .server .interfaces .DataStore ;
7
13
import com .launchdarkly .sdk .server .interfaces .DataStoreStatusProvider ;
8
14
import com .launchdarkly .sdk .server .interfaces .DataStoreTypes .DataKind ;
9
15
import com .launchdarkly .sdk .server .interfaces .DataStoreTypes .FullDataSet ;
10
16
import com .launchdarkly .sdk .server .interfaces .DataStoreTypes .ItemDescriptor ;
11
17
import com .launchdarkly .sdk .server .interfaces .DataStoreTypes .KeyedItems ;
12
- import com .launchdarkly .sdk .server .interfaces .DataSourceUpdates ;
13
18
import com .launchdarkly .sdk .server .interfaces .FlagChangeEvent ;
14
19
import com .launchdarkly .sdk .server .interfaces .FlagChangeListener ;
15
20
21
+ import java .time .Instant ;
16
22
import java .util .HashMap ;
17
23
import java .util .HashSet ;
18
24
import java .util .Map ;
33
39
final class DataSourceUpdatesImpl implements DataSourceUpdates {
34
40
private final DataStore store ;
35
41
private final EventBroadcasterImpl <FlagChangeListener , FlagChangeEvent > flagChangeEventNotifier ;
42
+ private final EventBroadcasterImpl <DataSourceStatusProvider .StatusListener , DataSourceStatusProvider .Status > dataSourceStatusNotifier ;
36
43
private final DataModelDependencies .DependencyTracker dependencyTracker = new DataModelDependencies .DependencyTracker ();
37
44
private final DataStoreStatusProvider dataStoreStatusProvider ;
38
45
46
+ private volatile DataSourceStatusProvider .Status currentStatus ;
47
+ private volatile boolean lastStoreUpdateFailed = false ;
48
+
39
49
DataSourceUpdatesImpl (
40
50
DataStore store ,
51
+ DataStoreStatusProvider dataStoreStatusProvider ,
41
52
EventBroadcasterImpl <FlagChangeListener , FlagChangeEvent > flagChangeEventNotifier ,
42
- DataStoreStatusProvider dataStoreStatusProvider
53
+ EventBroadcasterImpl < DataSourceStatusProvider . StatusListener , DataSourceStatusProvider . Status > dataSourceStatusNotifier
43
54
) {
44
55
this .store = store ;
45
56
this .flagChangeEventNotifier = flagChangeEventNotifier ;
57
+ this .dataSourceStatusNotifier = dataSourceStatusNotifier ;
46
58
this .dataStoreStatusProvider = dataStoreStatusProvider ;
59
+
60
+ currentStatus = new DataSourceStatusProvider .Status (
61
+ DataSourceStatusProvider .State .INITIALIZING ,
62
+ Instant .now (),
63
+ null
64
+ );
47
65
}
48
66
49
67
@ Override
50
- public void init (FullDataSet <ItemDescriptor > allData ) {
68
+ public boolean init (FullDataSet <ItemDescriptor > allData ) {
51
69
Map <DataKind , Map <String , ItemDescriptor >> oldData = null ;
52
-
53
- if (hasFlagChangeEventListeners ()) {
54
- // Query the existing data if any, so that after the update we can send events for whatever was changed
55
- oldData = new HashMap <>();
56
- for (DataKind kind : ALL_DATA_KINDS ) {
57
- KeyedItems <ItemDescriptor > items = store .getAll (kind );
58
- oldData .put (kind , ImmutableMap .copyOf (items .getItems ()));
70
+
71
+ try {
72
+ if (hasFlagChangeEventListeners ()) {
73
+ // Query the existing data if any, so that after the update we can send events for whatever was changed
74
+ oldData = new HashMap <>();
75
+ for (DataKind kind : ALL_DATA_KINDS ) {
76
+ KeyedItems <ItemDescriptor > items = store .getAll (kind );
77
+ oldData .put (kind , ImmutableMap .copyOf (items .getItems ()));
78
+ }
59
79
}
80
+ store .init (DataModelDependencies .sortAllCollections (allData ));
81
+ lastStoreUpdateFailed = false ;
82
+ } catch (RuntimeException e ) {
83
+ reportStoreFailure (e );
84
+ return false ;
60
85
}
61
86
62
- store .init (DataModelDependencies .sortAllCollections (allData ));
63
-
64
87
// We must always update the dependency graph even if we don't currently have any event listeners, because if
65
88
// listeners are added later, we don't want to have to reread the whole data store to compute the graph
66
89
updateDependencyTrackerFromFullDataSet (allData );
@@ -70,11 +93,20 @@ public void init(FullDataSet<ItemDescriptor> allData) {
70
93
if (oldData != null ) {
71
94
sendChangeEvents (computeChangedItemsForFullDataSet (oldData , fullDataSetToMap (allData )));
72
95
}
96
+
97
+ return true ;
73
98
}
74
99
75
100
@ Override
76
- public void upsert (DataKind kind , String key , ItemDescriptor item ) {
77
- boolean successfullyUpdated = store .upsert (kind , key , item );
101
+ public boolean upsert (DataKind kind , String key , ItemDescriptor item ) {
102
+ boolean successfullyUpdated ;
103
+ try {
104
+ successfullyUpdated = store .upsert (kind , key , item );
105
+ lastStoreUpdateFailed = false ;
106
+ } catch (RuntimeException e ) {
107
+ reportStoreFailure (e );
108
+ return false ;
109
+ }
78
110
79
111
if (successfullyUpdated ) {
80
112
dependencyTracker .updateDependenciesFrom (kind , key , item );
@@ -84,21 +116,49 @@ public void upsert(DataKind kind, String key, ItemDescriptor item) {
84
116
sendChangeEvents (affectedItems );
85
117
}
86
118
}
119
+
120
+ return true ;
87
121
}
88
122
89
123
@ Override
90
124
public DataStoreStatusProvider getDataStoreStatusProvider () {
91
125
return dataStoreStatusProvider ;
92
126
}
93
127
128
+ @ Override
129
+ public void updateStatus (DataSourceStatusProvider .State newState , DataSourceStatusProvider .ErrorInfo newError ) {
130
+ if (newState == null ) {
131
+ return ;
132
+ }
133
+ DataSourceStatusProvider .Status newStatus ;
134
+ synchronized (this ) {
135
+ if (newState == DataSourceStatusProvider .State .INTERRUPTED && currentStatus .getState () == DataSourceStatusProvider .State .INITIALIZING ) {
136
+ newState = DataSourceStatusProvider .State .INITIALIZING ; // see comment on updateStatus in the DataSourceUpdates interface
137
+ }
138
+ if (newState == currentStatus .getState () && newError == null ) {
139
+ return ;
140
+ }
141
+ currentStatus = new DataSourceStatusProvider .Status (
142
+ newState ,
143
+ newState == currentStatus .getState () ? currentStatus .getStateSince () : Instant .now (),
144
+ newError == null ? currentStatus .getLastError () : newError
145
+ );
146
+ newStatus = currentStatus ;
147
+ }
148
+ dataSourceStatusNotifier .broadcast (newStatus );
149
+ }
150
+
151
+ Status getLastStatus () {
152
+ synchronized (this ) {
153
+ return currentStatus ;
154
+ }
155
+ }
156
+
94
157
private boolean hasFlagChangeEventListeners () {
95
- return flagChangeEventNotifier != null && flagChangeEventNotifier .hasListeners ();
158
+ return flagChangeEventNotifier .hasListeners ();
96
159
}
97
160
98
161
private void sendChangeEvents (Iterable <KindAndKey > affectedItems ) {
99
- if (flagChangeEventNotifier == null ) {
100
- return ;
101
- }
102
162
for (KindAndKey item : affectedItems ) {
103
163
if (item .kind == FEATURES ) {
104
164
flagChangeEventNotifier .broadcast (new FlagChangeEvent (item .key ));
@@ -153,6 +213,15 @@ private Set<KindAndKey> computeChangedItemsForFullDataSet(Map<DataKind, Map<Stri
153
213
// version numbers are different, the higher one is the more recent version).
154
214
}
155
215
}
156
- return affectedItems ;
216
+ return affectedItems ;
217
+ }
218
+
219
+ private void reportStoreFailure (RuntimeException e ) {
220
+ if (!lastStoreUpdateFailed ) {
221
+ LDClient .logger .warn ("Unexpected data store error when trying to store an update received from the data source: {}" , e .toString ());
222
+ lastStoreUpdateFailed = true ;
223
+ }
224
+ LDClient .logger .debug (e .toString (), e );
225
+ updateStatus (State .INTERRUPTED , ErrorInfo .fromException (ErrorKind .STORE_ERROR , e ));
157
226
}
158
227
}
0 commit comments