19
19
package org .apache .cassandra .service .accord ;
20
20
21
21
import java .io .IOException ;
22
- import java .util .HashMap ;
22
+ import java .util .ArrayList ;
23
+ import java .util .Comparator ;
23
24
import java .util .HashSet ;
25
+ import java .util .List ;
24
26
import java .util .Map ;
25
27
import java .util .Objects ;
26
28
import java .util .Set ;
29
+ import java .util .function .BiConsumer ;
27
30
28
31
import com .google .common .annotations .VisibleForTesting ;
32
+ import com .google .common .base .Predicates ;
29
33
import com .google .common .collect .Iterators ;
30
- import org .slf4j .Logger ;
31
- import org .slf4j .LoggerFactory ;
34
+ import com .google .common .primitives .Ints ;
32
35
33
36
import accord .api .ConfigurationService ;
34
37
import accord .local .Node ;
35
38
import accord .primitives .Range ;
36
39
import accord .primitives .Ranges ;
37
40
import accord .topology .Topology ;
38
41
import accord .utils .Invariants ;
42
+ import accord .utils .ReducingRangeMap ;
39
43
import accord .utils .async .AsyncResult ;
40
- import org .agrona .collections .Int2ObjectHashMap ;
44
+ import org .agrona .collections .Long2LongHashMap ;
41
45
import org .apache .cassandra .db .TypeSizes ;
42
46
import org .apache .cassandra .io .UnversionedSerializer ;
43
47
import org .apache .cassandra .io .util .DataInputPlus ;
60
64
*/
61
65
public class WatermarkCollector implements ConfigurationService .Listener
62
66
{
63
- private static final Logger logger = LoggerFactory .getLogger (WatermarkCollector .class );
67
+ private static final Comparator <Map .Entry <Range , Long >> sortByEpochThenRange = (a , b ) -> {
68
+ int c = Long .compareUnsigned (a .getValue (), b .getValue ());
69
+ if (c == 0 ) c = a .getKey ().compare (b .getKey ());
70
+ return c ;
71
+ };
64
72
65
- final Map < Range , Long > closed ;
66
- final Map < Range , Long > retired ;
67
- final Int2ObjectHashMap < Long > synced ;
73
+ ReducingRangeMap < Long > closed ;
74
+ ReducingRangeMap < Long > retired ;
75
+ final Long2LongHashMap synced ;
68
76
69
77
WatermarkCollector ()
70
78
{
71
- closed = new HashMap <>();
72
- retired = new HashMap <>();
73
- synced = new Int2ObjectHashMap <>( );
79
+ closed = new ReducingRangeMap <>();
80
+ retired = new ReducingRangeMap <>();
81
+ synced = new Long2LongHashMap (- 1 );
74
82
}
75
83
76
84
@ Override public AsyncResult <Void > onTopologyUpdate (Topology topology , boolean isLoad , boolean startSync )
@@ -79,40 +87,36 @@ public class WatermarkCollector implements ConfigurationService.Listener
79
87
}
80
88
81
89
@ Override
82
- public void onRemoteSyncComplete (Node .Id node , long epoch )
90
+ public synchronized void onRemoteSyncComplete (Node .Id node , long epoch )
83
91
{
84
- synced .compute (node .id , (k , prev ) -> prev == null ? epoch : Long .max (prev , epoch ));
92
+ synced .compute (node .id , (k , prev ) -> prev == - 1 ? epoch : Long .max (prev , epoch ));
85
93
}
86
94
87
95
@ Override
88
- public void onEpochClosed (Ranges ranges , long epoch )
96
+ public synchronized void onEpochClosed (Ranges ranges , long epoch )
89
97
{
90
- synchronized (this )
91
- {
92
- for (Range range : ranges )
93
- this .closed .compute (range , (k , prev ) -> prev == null ? epoch : Long .max (prev , epoch ));
94
- }
98
+ closed = ReducingRangeMap .merge (closed , ReducingRangeMap .create (ranges , epoch ), Long ::max );
95
99
}
96
100
97
101
@ Override
98
- public void onEpochRetired (Ranges ranges , long epoch )
102
+ public synchronized void onEpochRetired (Ranges ranges , long epoch )
99
103
{
100
- synchronized (this )
101
- {
102
- for (Range range : ranges )
103
- this .retired .compute (range , (k , prev ) -> prev == null ? epoch : Long .max (prev , epoch ));
104
- }
104
+ retired = ReducingRangeMap .merge (retired , ReducingRangeMap .create (ranges , epoch ), Long ::max );
105
105
}
106
106
107
107
public final IVerbHandler <Void > handler = new IVerbHandler <Void >()
108
108
{
109
- public void doVerb (Message <Void > message ) throws IOException
109
+ public void doVerb (Message <Void > message )
110
110
{
111
111
Invariants .require (AccordService .started ());
112
112
Snapshot snapshot ;
113
113
synchronized (WatermarkCollector .this )
114
114
{
115
- snapshot = new Snapshot (new HashMap <>(closed ), new HashMap <>(retired ), new Int2ObjectHashMap <>(synced ));
115
+ List <Map .Entry <Range , Long >> closedSnapshot = closed .foldlWithBounds ((epoch , list , start , end ) -> { list .add (Map .entry (start .rangeFactory ().newRange (start , end ), epoch )); return list ; }, new ArrayList <>(), Predicates .alwaysFalse ());
116
+ List <Map .Entry <Range , Long >> retiredSnapshot = retired .foldlWithBounds ((epoch , list , start , end ) -> { list .add (Map .entry (start .rangeFactory ().newRange (start , end ), epoch )); return list ; }, new ArrayList <>(), Predicates .alwaysFalse ());
117
+ Long2LongHashMap syncedSnapshot = new Long2LongHashMap (synced .size (), 0.6f , -1 );
118
+ syncedSnapshot .putAll (synced );
119
+ snapshot = new Snapshot (closedSnapshot , retiredSnapshot , syncedSnapshot );
116
120
}
117
121
MessagingService .instance ().respond (snapshot , message );
118
122
}
@@ -139,32 +143,47 @@ static void fetchAndReportWatermarksAsync(AccordConfigurationService configServi
139
143
140
144
Snapshot snapshot = m .payload ;
141
145
long minEpoch = configService .minEpoch ();
142
- for (Map .Entry <Range , Long > e : snapshot .closed .entrySet ())
143
- {
144
- Ranges r = Ranges .of (e .getKey ());
145
- configService .receiveClosed (r , e .getValue ());
146
- }
147
- for (Map .Entry <Range , Long > e : snapshot .retired .entrySet ())
148
- {
149
- Ranges r = Ranges .of (e .getKey ());
150
- configService .receiveRetired (r , e .getValue ());
151
- }
152
- for (Map .Entry <Integer , Long > e : snapshot .synced .entrySet ())
146
+ forEachEpoch (configService ::receiveClosed , snapshot .closed );
147
+ forEachEpoch (configService ::receiveRetired , snapshot .retired );
148
+ for (Map .Entry <Long , Long > e : snapshot .synced .entrySet ())
153
149
{
154
- Node .Id node = new Node .Id (e .getKey ());
150
+ Node .Id node = new Node .Id (Ints . saturatedCast ( e .getKey () ));
155
151
for (long epoch = minEpoch ; epoch <= e .getValue (); epoch ++)
156
152
configService .receiveRemoteSyncComplete (node , epoch );
157
153
}
158
154
});
159
155
}
160
156
157
+ private static void forEachEpoch (BiConsumer <Ranges , Long > forEachEpoch , List <Map .Entry <Range , Long >> rangesAndEpochs )
158
+ {
159
+ if (rangesAndEpochs .isEmpty ())
160
+ return ;
161
+
162
+ rangesAndEpochs .sort (sortByEpochThenRange );
163
+ long collectingEpoch = rangesAndEpochs .get (0 ).getValue ();
164
+ List <Range > ranges = new ArrayList <>();
165
+ for (Map .Entry <Range , Long > e : rangesAndEpochs )
166
+ {
167
+ Range range = e .getKey ();
168
+ long epoch = e .getValue ();
169
+ if (epoch != collectingEpoch )
170
+ {
171
+ forEachEpoch .accept (Ranges .of (ranges .toArray (Range []::new )), collectingEpoch );
172
+ collectingEpoch = epoch ;
173
+ ranges .clear ();
174
+ }
175
+ ranges .add (range );
176
+ }
177
+ forEachEpoch .accept (Ranges .of (ranges .toArray (Range []::new )), collectingEpoch );
178
+ }
179
+
161
180
public static class Snapshot
162
181
{
163
- public final Map <Range , Long > closed ;
164
- public final Map <Range , Long > retired ;
165
- public final Int2ObjectHashMap < Long > synced ;
182
+ public final List < Map . Entry <Range , Long > > closed ;
183
+ public final List < Map . Entry <Range , Long > > retired ;
184
+ public final Long2LongHashMap synced ;
166
185
167
- public Snapshot (Map <Range , Long > closed , Map <Range , Long > retired , Int2ObjectHashMap < Long > synced )
186
+ public Snapshot (List < Map . Entry <Range , Long >> closed , List < Map . Entry <Range , Long >> retired , Long2LongHashMap synced )
168
187
{
169
188
this .closed = closed ;
170
189
this .retired = retired ;
@@ -193,21 +212,21 @@ public int hashCode()
193
212
public void serialize (Snapshot t , DataOutputPlus out ) throws IOException
194
213
{
195
214
out .writeUnsignedVInt32 (t .closed .size ());
196
- for (Map .Entry <Range , Long > e : t .closed . entrySet () )
215
+ for (Map .Entry <Range , Long > e : t .closed )
197
216
{
198
217
TokenRange .serializer .serialize ((TokenRange ) e .getKey (), out );
199
218
out .writeUnsignedVInt (e .getValue ());
200
219
}
201
220
out .writeUnsignedVInt32 (t .retired .size ());
202
- for (Map .Entry <Range , Long > e : t .retired . entrySet () )
221
+ for (Map .Entry <Range , Long > e : t .retired )
203
222
{
204
223
TokenRange .serializer .serialize ((TokenRange ) e .getKey (), out );
205
224
out .writeUnsignedVInt (e .getValue ());
206
225
}
207
226
out .writeUnsignedVInt32 (t .synced .size ());
208
- for (Map .Entry <Integer , Long > e : t .synced .entrySet ())
227
+ for (Map .Entry <Long , Long > e : t .synced .entrySet ())
209
228
{
210
- out .writeUnsignedVInt32 (e .getKey ());
229
+ out .writeUnsignedVInt (e .getKey ());
211
230
out .writeUnsignedVInt (e .getValue ());
212
231
}
213
232
}
@@ -217,25 +236,20 @@ public void serialize(Snapshot t, DataOutputPlus out) throws IOException
217
236
public Snapshot deserialize (DataInputPlus in ) throws IOException
218
237
{
219
238
int closedSize = in .readUnsignedVInt32 ();
220
- Map <Range , Long > closed = new HashMap <>();
239
+ List < Map . Entry <Range , Long >> closed = new ArrayList <>();
221
240
for (int i = 0 ; i < closedSize ; i ++)
222
- {
223
- closed .put (TokenRange .serializer .deserialize (in ),
224
- in .readUnsignedVInt ());
225
- }
241
+ closed .add (Map .entry (TokenRange .serializer .deserialize (in ), in .readUnsignedVInt ()));
242
+
226
243
int retiredSize = in .readUnsignedVInt32 ();
227
- Map <Range , Long > retired = new HashMap <>();
244
+ List < Map . Entry <Range , Long >> retired = new ArrayList <>();
228
245
for (int i = 0 ; i < retiredSize ; i ++)
229
- {
230
- retired .put (TokenRange .serializer .deserialize (in ),
231
- in .readUnsignedVInt ());
232
- }
246
+ retired .add (Map .entry (TokenRange .serializer .deserialize (in ), in .readUnsignedVInt ()));
247
+
233
248
int syncedSize = in .readUnsignedVInt32 ();
234
- Int2ObjectHashMap < Long > synced = new Int2ObjectHashMap <>( );
249
+ Long2LongHashMap synced = new Long2LongHashMap (- 1 );
235
250
for (int i = 0 ; i < syncedSize ; i ++)
236
251
{
237
- synced .put (in .readUnsignedVInt32 (),
238
- (Long ) in .readUnsignedVInt ());
252
+ synced .put (in .readUnsignedVInt (), in .readUnsignedVInt ());
239
253
}
240
254
return new Snapshot (closed , retired , synced );
241
255
}
@@ -245,19 +259,19 @@ public long serializedSize(Snapshot t)
245
259
{
246
260
int size = 0 ;
247
261
size += TypeSizes .sizeofUnsignedVInt (t .closed .size ());
248
- for (Map .Entry <Range , Long > e : t .closed . entrySet () )
262
+ for (Map .Entry <Range , Long > e : t .closed )
249
263
{
250
264
size += TokenRange .serializer .serializedSize ((TokenRange ) e .getKey ());
251
265
size += TypeSizes .sizeofUnsignedVInt (e .getValue ());
252
266
}
253
267
size += TypeSizes .sizeofUnsignedVInt (t .retired .size ());
254
- for (Map .Entry <Range , Long > e : t .retired . entrySet () )
268
+ for (Map .Entry <Range , Long > e : t .retired )
255
269
{
256
270
size += TokenRange .serializer .serializedSize ((TokenRange ) e .getKey ());
257
271
size += TypeSizes .sizeofUnsignedVInt (e .getValue ());
258
272
}
259
273
size += TypeSizes .sizeofUnsignedVInt (t .synced .size ());
260
- for (Map .Entry <Integer , Long > e : t .synced .entrySet ())
274
+ for (Map .Entry <Long , Long > e : t .synced .entrySet ())
261
275
{
262
276
size += TypeSizes .sizeofUnsignedVInt (e .getKey ());
263
277
size += TypeSizes .sizeofUnsignedVInt (e .getValue ());
0 commit comments