4646public class LocalStreamRangeIndexCache implements S3StreamClient .StreamLifeCycleListener {
4747 private static final short VERSION = 0 ;
4848 private static final Logger LOGGER = LoggerFactory .getLogger (LocalStreamRangeIndexCache .class );
49- private static final int COMPACT_NUM = Systems .getEnvInt ("AUTOMQ_STREAM_RANGE_INDEX_COMPACT_NUM" , 5 );
50- private static final int SPARSE_PADDING = Systems .getEnvInt ("AUTOMQ_STREAM_RANGE_INDEX_SPARSE_PADDING " , 1 );
49+ private static final int COMPACT_NUM = Systems .getEnvInt ("AUTOMQ_STREAM_RANGE_INDEX_COMPACT_NUM" , 3 );
50+ public static final int MAX_INDEX_SIZE = Systems .getEnvInt ("AUTOMQ_STREAM_RANGE_INDEX_MAX_SIZE " , 5 * 1024 * 1024 );
5151 private final Map <Long , SparseRangeIndex > streamRangeIndexMap = new HashMap <>();
5252 private final ReadWriteLock lock = new ReentrantReadWriteLock ();
5353 private final Lock readLock = lock .readLock ();
5454 private final Lock writeLock = lock .writeLock ();
5555 private final ScheduledExecutorService executorService = Executors .newSingleThreadScheduledExecutor (
5656 ThreadUtils .createThreadFactory ("upload-index" , true ));
5757 private final Queue <CompletableFuture <Void >> uploadQueue = new LinkedList <>();
58+ private final CompletableFuture <Void > initCf = new CompletableFuture <>();
5859 private long nodeId = -1 ;
5960 private ObjectStorage objectStorage ;
60- private CompletableFuture < Void > initCf = new CompletableFuture <>() ;
61+ private int totalSize = 0 ;
6162
6263 public void start () {
6364 executorService .scheduleAtFixedRate (this ::batchUpload , 0 , 10 , TimeUnit .MILLISECONDS );
6465 executorService .scheduleAtFixedRate (this ::flush , 1 , 1 , TimeUnit .MINUTES );
6566 }
6667
67- // for test
68- void reset () {
69- writeLock .lock ();
70- try {
71- streamRangeIndexMap .clear ();
72- initCf = new CompletableFuture <>();
73- } finally {
74- writeLock .unlock ();
75- }
68+ public int totalSize () {
69+ return totalSize ;
70+ }
71+
72+ CompletableFuture <Void > initCf () {
73+ return initCf ;
7674 }
7775
7876 // test only
@@ -158,7 +156,8 @@ public void init(int nodeId, ObjectStorage objectStorage) {
158156 writeLock .lock ();
159157 try {
160158 for (Map .Entry <Long , List <RangeIndex >> entry : LocalStreamRangeIndexCache .fromBuffer (data ).entrySet ()) {
161- this .streamRangeIndexMap .put (entry .getKey (), new SparseRangeIndex (COMPACT_NUM , SPARSE_PADDING , entry .getValue ()));
159+ this .streamRangeIndexMap .put (entry .getKey (), new SparseRangeIndex (COMPACT_NUM , entry .getValue ()));
160+ this .totalSize += entry .getValue ().size () * RangeIndex .OBJECT_SIZE ;
162161 }
163162 } finally {
164163 writeLock .unlock ();
@@ -207,16 +206,44 @@ public CompletableFuture<Void> append(Map<Long, RangeIndex> rangeIndexMap) {
207206 for (Map .Entry <Long , RangeIndex > entry : rangeIndexMap .entrySet ()) {
208207 long streamId = entry .getKey ();
209208 RangeIndex rangeIndex = entry .getValue ();
210- streamRangeIndexMap .computeIfAbsent (streamId ,
211- k -> new SparseRangeIndex (COMPACT_NUM , SPARSE_PADDING )).append (rangeIndex );
209+ totalSize += streamRangeIndexMap .computeIfAbsent (streamId ,
210+ k -> new SparseRangeIndex (COMPACT_NUM )).append (rangeIndex );
212211 }
212+ evictIfNecessary ();
213213 } finally {
214214 writeLock .unlock ();
215215 }
216216 return null ;
217217 });
218218 }
219219
220+ private void evictIfNecessary () {
221+ if (totalSize <= MAX_INDEX_SIZE ) {
222+ return ;
223+ }
224+ boolean evicted = false ;
225+ boolean hasSufficientIndex = true ;
226+ List <SparseRangeIndex > streamRangeIndexList = new ArrayList <>(streamRangeIndexMap .values ());
227+ Collections .shuffle (streamRangeIndexList );
228+ while (totalSize > MAX_INDEX_SIZE ) {
229+ // try to evict from each stream in round-robin manner
230+ for (SparseRangeIndex sparseRangeIndex : streamRangeIndexList ) {
231+ if (sparseRangeIndex .length () <= 1 + COMPACT_NUM && hasSufficientIndex ) {
232+ // skip evict if there is still sufficient stream to be evicted
233+ continue ;
234+ }
235+ totalSize -= sparseRangeIndex .evictOnce ();
236+ evicted = true ;
237+ if (totalSize <= MAX_INDEX_SIZE ) {
238+ break ;
239+ }
240+ }
241+ if (!evicted ) {
242+ hasSufficientIndex = false ;
243+ }
244+ }
245+ }
246+
220247 public CompletableFuture <Void > compact (Map <Long , RangeIndex > rangeIndexMap , Set <Long > compactedObjectIds ) {
221248 return exec (() -> {
222249 writeLock .lock ();
@@ -225,8 +252,8 @@ public CompletableFuture<Void> compact(Map<Long, RangeIndex> rangeIndexMap, Set<
225252 Iterator <Map .Entry <Long , SparseRangeIndex >> iterator = streamRangeIndexMap .entrySet ().iterator ();
226253 while (iterator .hasNext ()) {
227254 Map .Entry <Long , SparseRangeIndex > entry = iterator .next ();
228- entry .getValue ().compact (null , compactedObjectIds );
229- if (entry .getValue ().size () == 0 ) {
255+ totalSize += entry .getValue ().compact (null , compactedObjectIds );
256+ if (entry .getValue ().length () == 0 ) {
230257 iterator .remove ();
231258 }
232259 }
@@ -237,10 +264,10 @@ public CompletableFuture<Void> compact(Map<Long, RangeIndex> rangeIndexMap, Set<
237264 RangeIndex rangeIndex = entry .getValue ();
238265 streamRangeIndexMap .compute (streamId , (k , v ) -> {
239266 if (v == null ) {
240- v = new SparseRangeIndex (COMPACT_NUM , SPARSE_PADDING );
267+ v = new SparseRangeIndex (COMPACT_NUM );
241268 }
242- v .compact (rangeIndex , compactedObjectIds );
243- if (v .size () == 0 ) {
269+ totalSize += v .compact (rangeIndex , compactedObjectIds );
270+ if (v .length () == 0 ) {
244271 // remove stream with empty index
245272 return null ;
246273 }
@@ -270,11 +297,7 @@ public CompletableFuture<Void> updateIndexFromRequest(CommitStreamSetObjectReque
270297 }
271298
272299 public static ByteBuf toBuffer (Map <Long , SparseRangeIndex > streamRangeIndexMap ) {
273- int capacity = Short .BYTES // version
274- + Integer .BYTES // stream num
275- + streamRangeIndexMap .values ().stream ().mapToInt (index -> Long .BYTES // stream id
276- + Integer .BYTES // range index num
277- + index .getRangeIndexList ().size () * (3 * Long .BYTES )).sum ();
300+ int capacity = bufferSize (streamRangeIndexMap );
278301 ByteBuf buffer = ByteBufAlloc .byteBuffer (capacity );
279302 try {
280303 buffer .writeShort (VERSION );
@@ -295,6 +318,14 @@ public static ByteBuf toBuffer(Map<Long, SparseRangeIndex> streamRangeIndexMap)
295318 return buffer ;
296319 }
297320
321+ private static int bufferSize (Map <Long , SparseRangeIndex > streamRangeIndexMap ) {
322+ return Short .BYTES // version
323+ + Integer .BYTES // stream num
324+ + streamRangeIndexMap .values ().stream ().mapToInt (index -> Long .BYTES // stream id
325+ + Integer .BYTES // range index num
326+ + index .getRangeIndexList ().size () * (3 * Long .BYTES )).sum ();
327+ }
328+
298329 public static Map <Long , List <RangeIndex >> fromBuffer (ByteBuf data ) {
299330 Map <Long , List <RangeIndex >> rangeIndexMap = new HashMap <>();
300331 short version = data .readShort ();
0 commit comments