package it.cavallium.dbengine.database.disk; import static it.cavallium.dbengine.database.LLUtils.generateCustomReadOptions; import io.netty5.buffer.api.Drop; import io.netty5.buffer.api.Owned; import io.netty5.util.Send; import io.netty5.buffer.api.internal.ResourceSupport; import it.cavallium.dbengine.database.LLRange; import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.rocksdb.ReadOptions; import org.rocksdb.RocksDBException; import reactor.core.publisher.Flux; import reactor.util.function.Tuples; public final class LLLocalMigrationReactiveRocksIterator extends ResourceSupport { private static final Logger logger = LogManager.getLogger(LLLocalMigrationReactiveRocksIterator.class); private static final Drop DROP = new Drop<>() { @Override public void drop(LLLocalMigrationReactiveRocksIterator obj) { try { if (obj.rangeShared != null) { obj.rangeShared.close(); } } catch (Throwable ex) { logger.error("Failed to close range", ex); } } @Override public Drop fork() { return this; } @Override public void attach(LLLocalMigrationReactiveRocksIterator obj) { } }; private final RocksDBColumn db; private LLRange rangeShared; private Supplier readOptions; @SuppressWarnings({"unchecked", "rawtypes"}) public LLLocalMigrationReactiveRocksIterator(RocksDBColumn db, Send range, Supplier readOptions) { super((Drop) (Drop) DROP); try (range) { this.db = db; this.rangeShared = range.receive(); this.readOptions = readOptions; } } public record ByteEntry(byte[] key, byte[] value) {} public Flux flux() { return Flux.generate(() -> { var readOptions = generateCustomReadOptions(this.readOptions.get(), false, false, false); return new RocksIterWithReadOpts(readOptions, db.newRocksIterator(false, readOptions, rangeShared, false)); }, (tuple, sink) -> { try { var rocksIterator = tuple.iter(); if (rocksIterator.isValid()) { byte[] key = rocksIterator.key(); byte[] value = rocksIterator.value(); rocksIterator.next(false); sink.next(new ByteEntry(key, value)); } else { sink.complete(); } } catch (RocksDBException ex) { sink.error(ex); } return tuple; }, RocksIterWithReadOpts::close); } @Override protected final RuntimeException createResourceClosedException() { return new IllegalStateException("Closed"); } @Override protected Owned prepareSend() { var range = this.rangeShared.send(); var readOptions = this.readOptions; return drop -> new LLLocalMigrationReactiveRocksIterator(db, range, readOptions ); } protected void makeInaccessible() { this.rangeShared = null; this.readOptions = null; } }