30
30
* GitHub history for details.
31
31
*/
32
32
33
- package org .opensearch .indices ;
33
+ package org .opensearch .common . cache . tier . keystore ;
34
34
35
35
import org .opensearch .common .metrics .CounterMetric ;
36
- import org .roaringbitmap .RoaringBitmap ;
37
36
38
37
import java .util .HashSet ;
39
38
import java .util .concurrent .locks .Lock ;
40
39
import java .util .concurrent .locks .ReentrantReadWriteLock ;
41
40
41
+ import org .roaringbitmap .RoaringBitmap ;
42
+
42
43
/**
43
44
* This class implements KeyLookupStore<Integer> using a roaring bitmap with a modulo applied to values.
44
45
* The modulo increases the density of values, which makes RBMs more memory-efficient. The recommended modulo is ~2^28.
48
49
* The store estimates its memory footprint and will stop adding more values once it reaches its memory cap.
49
50
*/
50
51
public class RBMIntKeyLookupStore implements KeyLookupStore <Integer > {
51
- protected final int modulo ;
52
- protected class KeyStoreStats {
53
- protected int size ;
54
- protected long memSizeCapInBytes ;
55
- protected CounterMetric numAddAttempts ;
56
- protected CounterMetric numCollisions ;
57
- protected boolean guaranteesNoFalseNegatives ;
58
- protected int maxNumEntries ;
59
- protected boolean atCapacity ;
60
- protected CounterMetric numRemovalAttempts ;
61
- protected CounterMetric numSuccessfulRemovals ;
62
- protected KeyStoreStats (long memSizeCapInBytes , int maxNumEntries ) {
63
- this .size = 0 ;
64
- this .numAddAttempts = new CounterMetric ();
65
- this .numCollisions = new CounterMetric ();
66
- this .memSizeCapInBytes = memSizeCapInBytes ;
67
- this .maxNumEntries = maxNumEntries ;
68
- this .atCapacity = false ;
69
- this .numRemovalAttempts = new CounterMetric ();
70
- this .numSuccessfulRemovals = new CounterMetric ();
52
+ /**
53
+ * An enum representing modulo values for use in the keystore
54
+ */
55
+ public enum KeystoreModuloValue {
56
+ NONE (0 ), // No modulo applied
57
+ TWO_TO_THIRTY_ONE ((int ) Math .pow (2 , 31 )),
58
+ TWO_TO_TWENTY_NINE ((int ) Math .pow (2 , 29 )), // recommended value
59
+ TWO_TO_TWENTY_EIGHT ((int ) Math .pow (2 , 28 )),
60
+ TWO_TO_TWENTY_SIX ((int ) Math .pow (2 , 26 ));
61
+
62
+ private final int value ;
63
+
64
+ private KeystoreModuloValue (int value ) {
65
+ this .value = value ;
66
+ }
67
+
68
+ public int getValue () {
69
+ return this .value ;
71
70
}
72
71
}
73
72
74
- protected KeyStoreStats stats ;
73
+ protected final int modulo ;
74
+ KeyStoreStats stats ;
75
75
protected RoaringBitmap rbm ;
76
76
private HashSet <Integer > collidedInts ;
77
77
protected RBMSizeEstimator sizeEstimator ;
78
78
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock ();
79
79
protected final Lock readLock = lock .readLock ();
80
80
protected final Lock writeLock = lock .writeLock ();
81
81
82
- RBMIntKeyLookupStore (int modulo , long memSizeCapInBytes ) {
83
- this .modulo = modulo ;
82
+ // Default constructor sets modulo = 2^28
83
+ public RBMIntKeyLookupStore (long memSizeCapInBytes ) {
84
+ this (KeystoreModuloValue .TWO_TO_TWENTY_EIGHT , memSizeCapInBytes );
85
+ }
86
+
87
+ public RBMIntKeyLookupStore (KeystoreModuloValue moduloValue , long memSizeCapInBytes ) {
88
+ this .modulo = moduloValue .getValue ();
84
89
sizeEstimator = new RBMSizeEstimator (modulo );
85
90
this .stats = new KeyStoreStats (memSizeCapInBytes , calculateMaxNumEntries (memSizeCapInBytes ));
86
91
this .rbm = new RoaringBitmap ();
@@ -94,11 +99,11 @@ protected int calculateMaxNumEntries(long memSizeCapInBytes) {
94
99
return sizeEstimator .getNumEntriesFromSizeInBytes (memSizeCapInBytes );
95
100
}
96
101
97
- protected final int transform (int value ) {
102
+ private final int transform (int value ) {
98
103
return modulo == 0 ? value : value % modulo ;
99
104
}
100
105
101
- protected void handleCollisions (int transformedValue ) {
106
+ private void handleCollisions (int transformedValue ) {
102
107
stats .numCollisions .inc ();
103
108
collidedInts .add (transformedValue );
104
109
}
@@ -108,18 +113,21 @@ public boolean add(Integer value) throws Exception {
108
113
if (value == null ) {
109
114
return false ;
110
115
}
111
- writeLock .lock ();
112
116
stats .numAddAttempts .inc ();
117
+ if (stats .size .count () == stats .maxNumEntries ) {
118
+ stats .atCapacity .set (true );
119
+ return false ;
120
+ }
121
+ int transformedValue = transform (value );
122
+
123
+ writeLock .lock ();
113
124
try {
114
- if (stats .size == stats .maxNumEntries ) {
115
- stats .atCapacity = true ;
116
- return false ;
117
- }
118
- int transformedValue = transform (value );
119
- boolean alreadyContained = contains (value );
125
+ boolean alreadyContained ;
126
+ // saves calling transform() an additional time
127
+ alreadyContained = rbm .contains (transformedValue );
120
128
if (!alreadyContained ) {
121
129
rbm .add (transformedValue );
122
- stats .size ++ ;
130
+ stats .size . inc () ;
123
131
return true ;
124
132
}
125
133
handleCollisions (transformedValue );
@@ -159,7 +167,7 @@ public boolean remove(Integer value) throws Exception {
159
167
int transformedValue = transform (value );
160
168
readLock .lock ();
161
169
try {
162
- if (!contains (value )) {
170
+ if (!rbm . contains (transformedValue )) { // saves additional transform() call
163
171
return false ;
164
172
}
165
173
stats .numRemovalAttempts .inc ();
@@ -172,7 +180,7 @@ public boolean remove(Integer value) throws Exception {
172
180
writeLock .lock ();
173
181
try {
174
182
rbm .remove (transformedValue );
175
- stats .size -- ;
183
+ stats .size . dec () ;
176
184
stats .numSuccessfulRemovals .inc ();
177
185
return true ;
178
186
} finally {
@@ -184,14 +192,14 @@ public boolean remove(Integer value) throws Exception {
184
192
public int getSize () {
185
193
readLock .lock ();
186
194
try {
187
- return stats .size ;
195
+ return ( int ) stats .size . count () ;
188
196
} finally {
189
197
readLock .unlock ();
190
198
}
191
199
}
192
200
193
201
@ Override
194
- public int getTotalAdds () {
202
+ public int getAddAttempts () {
195
203
return (int ) stats .numAddAttempts .count ();
196
204
}
197
205
@@ -200,7 +208,6 @@ public int getCollisions() {
200
208
return (int ) stats .numCollisions .count ();
201
209
}
202
210
203
-
204
211
@ Override
205
212
public boolean isCollision (Integer value1 , Integer value2 ) {
206
213
if (value1 == null || value2 == null ) {
@@ -211,7 +218,7 @@ public boolean isCollision(Integer value1, Integer value2) {
211
218
212
219
@ Override
213
220
public long getMemorySizeInBytes () {
214
- return sizeEstimator .getSizeInBytes (stats .size ) + RBMSizeEstimator .getHashsetMemSizeInBytes (collidedInts .size ());
221
+ return sizeEstimator .getSizeInBytes (( int ) stats .size . count () ) + RBMSizeEstimator .getHashsetMemSizeInBytes (collidedInts .size ());
215
222
}
216
223
217
224
@ Override
@@ -221,14 +228,14 @@ public long getMemorySizeCapInBytes() {
221
228
222
229
@ Override
223
230
public boolean isFull () {
224
- return stats .atCapacity ;
231
+ return stats .atCapacity . get () ;
225
232
}
226
233
227
234
@ Override
228
235
public void regenerateStore (Integer [] newValues ) throws Exception {
229
236
rbm .clear ();
230
237
collidedInts = new HashSet <>();
231
- stats .size = 0 ;
238
+ stats .size = new CounterMetric () ;
232
239
stats .numAddAttempts = new CounterMetric ();
233
240
stats .numCollisions = new CounterMetric ();
234
241
stats .guaranteesNoFalseNegatives = true ;
@@ -241,12 +248,11 @@ public void regenerateStore(Integer[] newValues) throws Exception {
241
248
}
242
249
}
243
250
244
-
245
-
246
251
@ Override
247
252
public void clear () throws Exception {
248
- regenerateStore (new Integer []{});
253
+ regenerateStore (new Integer [] {});
249
254
}
255
+
250
256
public int getNumRemovalAttempts () {
251
257
return (int ) stats .numRemovalAttempts .count ();
252
258
}
0 commit comments