public void tryReadahead(boolean cacheMiss) {
            if (System.currentTimeMillis() - resetTimestamp < READAHEAD_RESET_COLD_DOWN_MILLS) {
                // skip readahead when readahead is in cold down
                return;
            }
            cacheMissCount += cacheMiss ? 1 : 0;
            if (inflightReadaheadCf != null) {
                return;
            }
            nextReadaheadSize = Math.min(nextReadaheadSize + cacheMissCount * READAHEAD_SIZE_UNIT, MAX_READAHEAD_SIZE);
            cacheMissCount = 0;
            if (requireReset) {
                nextReadaheadOffset = 0L;
                nextReadaheadSize = READAHEAD_SIZE_UNIT;
                readaheadMarkOffset = 0L;
                requireReset = false;
            }
            if (nextReadOffset >= nextReadaheadOffset) {
                nextReadaheadOffset = nextReadOffset;
            } else if (nextReadOffset <= readaheadMarkOffset) {
                // if the user read doesn't reach the readahead mark, we don't need to readahead
                return;
            }
            if (dataBlockCache.available() < nextReadaheadSize + READAHEAD_AVAILABLE_BYTES_THRESHOLD) {
                return;
            }
            readaheadMarkOffset = nextReadaheadOffset;
            inflightReadaheadCf = getBlocks(nextReadaheadOffset, -1L, nextReadaheadSize, true).thenAccept(blocks -> {
                nextReadaheadOffset = blocks.isEmpty() ? nextReadaheadOffset : blocks.get(blocks.size() - 1).index.endOffset();
                blocks.forEach(Block::release);
            });
            // For get block indexes and load data block are sync success,
            // the whenComplete will invoke first before assign CompletableFuture to inflightReadaheadCf
            inflightReadaheadCf.whenComplete((nil, ex) -> {
                Throwable cause = FutureUtil.cause(ex);
                if (cause != null && !isRecoverable(cause)) {
                    LOGGER.error("Readahead failed", ex);
                }
                inflightReadaheadCf = null;
            });
        }
The <code>Block#markReadCompleted</code> should be invoked after the Block was removed from <code>blocksMap</code>.
s3stream/src/main/java/com/automq/stream/s3/cache/blockcache/StreamReader.java