24
24
25
25
import javax .annotation .Nullable ;
26
26
27
+ import com .google .common .annotations .VisibleForTesting ;
28
+
29
+ import com .google .common .base .Function ;
27
30
import org .slf4j .Logger ;
28
31
import org .slf4j .LoggerFactory ;
29
32
37
40
* This abstraction represents both the HeartBeatState and the ApplicationState in an EndpointState
38
41
* instance. Any state for a given endpoint can be retrieved from this instance.
39
42
*/
40
-
41
-
42
43
public class EndpointState
43
44
{
44
45
protected static final Logger logger = LoggerFactory .getLogger (EndpointState .class );
45
46
46
47
public final static IVersionedSerializer <EndpointState > serializer = new EndpointStateSerializer ();
47
48
48
- private volatile HeartBeatState hbState ;
49
- private final AtomicReference <Map <ApplicationState , VersionedValue >> applicationState ;
49
+ private static class View
50
+ {
51
+ final HeartBeatState hbState ;
52
+ final Map <ApplicationState , VersionedValue > applicationState ;
53
+
54
+ private View (HeartBeatState hbState , Map <ApplicationState , VersionedValue > applicationState )
55
+ {
56
+ this .hbState = hbState ;
57
+ this .applicationState = applicationState ;
58
+ }
59
+ }
60
+
61
+ private final AtomicReference <View > ref ;
50
62
51
63
/* fields below do not get serialized */
52
64
private volatile long updateTimestamp ;
53
65
private volatile boolean isAlive ;
54
66
55
67
public EndpointState (HeartBeatState initialHbState )
56
68
{
57
- this (initialHbState , new EnumMap <ApplicationState , VersionedValue >(ApplicationState .class ));
69
+ this (initialHbState , new EnumMap <>(ApplicationState .class ));
58
70
}
59
71
60
72
public EndpointState (EndpointState other )
61
73
{
62
- this (new HeartBeatState (other .hbState ), new EnumMap <>(other .applicationState .get ()));
74
+ ref = new AtomicReference <>(other .ref .get ());
75
+ updateTimestamp = System .nanoTime ();
76
+ isAlive = true ;
63
77
}
64
78
65
- EndpointState (HeartBeatState initialHbState , Map <ApplicationState , VersionedValue > states )
79
+ @ VisibleForTesting
80
+ public EndpointState (HeartBeatState initialHbState , Map <ApplicationState , VersionedValue > states )
66
81
{
67
- hbState = initialHbState ;
68
- applicationState = new AtomicReference <Map <ApplicationState , VersionedValue >>(new EnumMap <>(states ));
82
+ ref = new AtomicReference <>(new View (initialHbState , new EnumMap <>(states )));
69
83
updateTimestamp = System .nanoTime ();
70
84
isAlive = true ;
71
85
}
72
86
73
- HeartBeatState getHeartBeatState ()
87
+ @ VisibleForTesting
88
+ public HeartBeatState getHeartBeatState ()
89
+ {
90
+ return ref .get ().hbState ;
91
+ }
92
+
93
+ public void updateHeartBeat ()
94
+ {
95
+ updateHeartBeat (HeartBeatState ::updateHeartBeat );
96
+ }
97
+
98
+ public void forceNewerGenerationUnsafe ()
99
+ {
100
+ updateHeartBeat (HeartBeatState ::forceNewerGenerationUnsafe );
101
+ }
102
+
103
+ @ VisibleForTesting
104
+ public void forceHighestPossibleVersionUnsafe ()
74
105
{
75
- return hbState ;
106
+ updateHeartBeat ( HeartBeatState :: forceHighestPossibleVersionUnsafe ) ;
76
107
}
77
108
78
- void setHeartBeatState ( HeartBeatState newHbState )
109
+ void unsafeSetEmptyHeartBeatState ( )
79
110
{
80
- updateTimestamp ();
81
- hbState = newHbState ;
111
+ updateHeartBeat (ignore -> HeartBeatState .empty ());
112
+ }
113
+
114
+ private void updateHeartBeat (Function <HeartBeatState , HeartBeatState > fn )
115
+ {
116
+ HeartBeatState previous = null ;
117
+ HeartBeatState update = null ;
118
+ while (true )
119
+ {
120
+ View view = ref .get ();
121
+ if (previous == null || view .hbState != previous ) // if this races with updating states then can avoid bumping versions
122
+ update = fn .apply (view .hbState );
123
+ if (ref .compareAndSet (view , new View (update , view .applicationState )))
124
+ return ;
125
+ previous = view .hbState ;
126
+ }
82
127
}
83
128
84
129
public VersionedValue getApplicationState (ApplicationState key )
85
130
{
86
- return applicationState .get ().get (key );
131
+ return ref .get (). applicationState .get (key );
87
132
}
88
133
89
134
public boolean containsApplicationState (ApplicationState key )
90
135
{
91
- return applicationState .get ().containsKey (key );
136
+ return ref .get (). applicationState .containsKey (key );
92
137
}
93
138
94
139
public Set <Map .Entry <ApplicationState , VersionedValue >> states ()
95
140
{
96
- return applicationState .get ().entrySet ();
141
+ return ref .get (). applicationState .entrySet ();
97
142
}
98
143
99
144
public void addApplicationState (ApplicationState key , VersionedValue value )
@@ -107,36 +152,47 @@ public void addApplicationStates(Map<ApplicationState, VersionedValue> values)
107
152
}
108
153
109
154
public void addApplicationStates (Set <Map .Entry <ApplicationState , VersionedValue >> values )
155
+ {
156
+ addApplicationStates (values , null );
157
+ }
158
+
159
+ public void addApplicationStates (Set <Map .Entry <ApplicationState , VersionedValue >> values , @ Nullable HeartBeatState hbState )
110
160
{
111
161
while (true )
112
162
{
113
- Map <ApplicationState , VersionedValue > orig = applicationState .get ();
163
+ View view = this .ref .get ();
164
+ Map <ApplicationState , VersionedValue > orig = view .applicationState ;
114
165
Map <ApplicationState , VersionedValue > copy = new EnumMap <>(orig );
115
166
116
167
for (Map .Entry <ApplicationState , VersionedValue > value : values )
117
168
copy .put (value .getKey (), value .getValue ());
118
169
119
- if (applicationState .compareAndSet (orig , copy ))
170
+ if (this .ref .compareAndSet (view , new View (hbState == null ? view .hbState : hbState , copy )))
171
+ {
172
+ if (hbState != null )
173
+ updateTimestamp ();
120
174
return ;
175
+ }
121
176
}
122
177
}
123
178
124
179
void removeMajorVersion3LegacyApplicationStates ()
125
180
{
126
181
while (hasLegacyFields ())
127
182
{
128
- Map <ApplicationState , VersionedValue > orig = applicationState .get ();
183
+ View view = ref .get ();
184
+ Map <ApplicationState , VersionedValue > orig = view .applicationState ;
129
185
Map <ApplicationState , VersionedValue > updatedStates = filterMajorVersion3LegacyApplicationStates (orig );
130
186
// avoid updating if no state is removed
131
187
if (orig .size () == updatedStates .size ()
132
- || applicationState .compareAndSet (orig , updatedStates ))
188
+ || ref .compareAndSet (view , new View ( view . hbState , updatedStates ) ))
133
189
return ;
134
190
}
135
191
}
136
192
137
193
private boolean hasLegacyFields ()
138
194
{
139
- Set <ApplicationState > statesPresent = applicationState .get ().keySet ();
195
+ Set <ApplicationState > statesPresent = ref .get (). applicationState .keySet ();
140
196
if (statesPresent .isEmpty ())
141
197
return false ;
142
198
return (statesPresent .contains (ApplicationState .STATUS ) && statesPresent .contains (ApplicationState .STATUS_WITH_PORT ))
@@ -193,16 +249,18 @@ void markDead()
193
249
194
250
public boolean isStateEmpty ()
195
251
{
196
- return applicationState .get ().isEmpty ();
252
+ return ref .get (). applicationState .isEmpty ();
197
253
}
198
254
199
255
/**
200
256
* @return true if {@link HeartBeatState#isEmpty()} is true and no STATUS application state exists
201
257
*/
202
258
public boolean isEmptyWithoutStatus ()
203
259
{
204
- Map <ApplicationState , VersionedValue > state = applicationState .get ();
205
- return hbState .isEmpty () && !(state .containsKey (ApplicationState .STATUS_WITH_PORT ) || state .containsKey (ApplicationState .STATUS ));
260
+ View view = ref .get ();
261
+ Map <ApplicationState , VersionedValue > state = view .applicationState ;
262
+ boolean hasStatus = state .containsKey (ApplicationState .STATUS_WITH_PORT ) || state .containsKey (ApplicationState .STATUS );
263
+ return view .hbState .isEmpty () && !hasStatus ;
206
264
}
207
265
208
266
public boolean isRpcReady ()
@@ -253,7 +311,8 @@ public CassandraVersion getReleaseVersion()
253
311
254
312
public String toString ()
255
313
{
256
- return "EndpointState: HeartBeatState = " + hbState + ", AppStateMap = " + applicationState .get ();
314
+ View view = ref .get ();
315
+ return "EndpointState: HeartBeatState = " + view .hbState + ", AppStateMap = " + view .applicationState ;
257
316
}
258
317
}
259
318
0 commit comments