2021-10-20 01:51:34 +02:00
|
|
|
package it.cavallium.dbengine.database.disk;
|
|
|
|
|
|
|
|
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
|
|
|
|
2021-10-30 11:13:46 +02:00
|
|
|
import io.micrometer.core.instrument.MeterRegistry;
|
2023-03-06 12:19:08 +01:00
|
|
|
import it.cavallium.buffer.Buf;
|
2021-10-20 01:51:34 +02:00
|
|
|
import it.cavallium.dbengine.database.LLDelta;
|
|
|
|
import it.cavallium.dbengine.database.LLUtils;
|
2023-03-27 22:00:32 +02:00
|
|
|
import it.cavallium.dbengine.database.serialization.SerializationFunction;
|
2023-02-09 23:34:25 +01:00
|
|
|
import it.cavallium.dbengine.utils.DBException;
|
2021-10-20 01:51:34 +02:00
|
|
|
import java.io.IOException;
|
2022-04-30 01:49:44 +02:00
|
|
|
import java.util.concurrent.locks.StampedLock;
|
2021-10-20 01:51:34 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2023-03-27 22:00:32 +02:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2021-10-20 01:51:34 +02:00
|
|
|
import org.rocksdb.ColumnFamilyHandle;
|
|
|
|
import org.rocksdb.ReadOptions;
|
|
|
|
import org.rocksdb.RocksDBException;
|
|
|
|
import org.rocksdb.Transaction;
|
|
|
|
import org.rocksdb.TransactionDB;
|
|
|
|
import org.rocksdb.TransactionOptions;
|
|
|
|
import org.rocksdb.WriteOptions;
|
|
|
|
|
|
|
|
public final class PessimisticRocksDBColumn extends AbstractRocksDBColumn<TransactionDB> {
|
|
|
|
|
|
|
|
private static final TransactionOptions DEFAULT_TX_OPTIONS = new TransactionOptions();
|
|
|
|
|
|
|
|
public PessimisticRocksDBColumn(TransactionDB db,
|
2022-03-30 15:15:53 +02:00
|
|
|
String dbName,
|
2022-05-20 10:20:00 +02:00
|
|
|
ColumnFamilyHandle cfh,
|
2022-04-28 23:23:26 +02:00
|
|
|
MeterRegistry meterRegistry,
|
2022-04-30 01:49:44 +02:00
|
|
|
StampedLock closeLock) {
|
2023-02-09 23:34:25 +01:00
|
|
|
super(db, dbName, cfh, meterRegistry, closeLock);
|
2021-10-20 01:51:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-05-20 10:20:00 +02:00
|
|
|
protected boolean commitOptimistically(Transaction tx) throws RocksDBException {
|
|
|
|
tx.commit();
|
2021-10-20 01:51:34 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-05-20 10:20:00 +02:00
|
|
|
protected Transaction beginTransaction(@NotNull WriteOptions writeOptions,
|
|
|
|
TransactionOptions txOpts) {
|
|
|
|
return getDb().beginTransaction(writeOptions, txOpts);
|
2021-10-20 01:51:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-05-20 10:20:00 +02:00
|
|
|
public @NotNull UpdateAtomicResult updateAtomicImpl(@NotNull ReadOptions readOptions,
|
|
|
|
@NotNull WriteOptions writeOptions,
|
2023-02-09 23:34:25 +01:00
|
|
|
Buf key,
|
2023-03-27 22:00:32 +02:00
|
|
|
SerializationFunction<@Nullable Buf, @Nullable Buf> updater,
|
2023-02-09 23:34:25 +01:00
|
|
|
UpdateAtomicResultMode returnMode) {
|
2022-04-01 01:30:56 +02:00
|
|
|
long initNanoTime = System.nanoTime();
|
|
|
|
try {
|
|
|
|
var cfh = getCfh();
|
2023-02-09 23:34:25 +01:00
|
|
|
var keyArray = LLUtils.asArray(key);
|
|
|
|
if (LLUtils.isInNonBlockingThread()) {
|
2022-04-01 01:30:56 +02:00
|
|
|
throw new UnsupportedOperationException("Called update in a nonblocking thread");
|
|
|
|
}
|
2022-05-20 10:20:00 +02:00
|
|
|
try (var txOpts = new TransactionOptions();
|
2022-05-12 19:14:27 +02:00
|
|
|
var tx = beginTransaction(writeOptions, txOpts)) {
|
2023-04-18 19:30:18 +02:00
|
|
|
Buf prevData;
|
|
|
|
Buf newData;
|
2023-02-09 23:34:25 +01:00
|
|
|
boolean changed;
|
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
|
logger.trace(MARKER_ROCKSDB, "Reading {} (before update lock)", LLUtils.toStringSafe(key));
|
|
|
|
}
|
|
|
|
var prevDataArray = tx.getForUpdate(readOptions, cfh, keyArray, true);
|
2022-04-01 01:30:56 +02:00
|
|
|
try {
|
2021-12-29 00:31:35 +01:00
|
|
|
if (logger.isTraceEnabled()) {
|
2023-02-09 23:34:25 +01:00
|
|
|
logger.trace(MARKER_ROCKSDB,
|
|
|
|
"Reading {}: {} (before update)",
|
|
|
|
LLUtils.toStringSafe(key),
|
|
|
|
LLUtils.toStringSafe(prevDataArray)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (prevDataArray != null) {
|
|
|
|
readValueFoundWithoutBloomBufferSize.record(prevDataArray.length);
|
|
|
|
prevData = Buf.wrap(prevDataArray);
|
|
|
|
} else {
|
|
|
|
readValueNotFoundWithoutBloomBufferSize.record(0);
|
2023-04-18 19:30:18 +02:00
|
|
|
prevData = null;
|
2023-02-09 23:34:25 +01:00
|
|
|
}
|
|
|
|
if (prevData != null) {
|
2023-04-17 23:31:16 +02:00
|
|
|
prevData.freeze();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
newData = updater.apply(prevData);
|
|
|
|
} catch (Exception ex) {
|
|
|
|
throw new DBException("Failed to update key " + LLUtils.toStringSafe(key) + ". The previous value was:\n" + LLUtils.toStringSafe(prevData, 8192), ex);
|
2022-04-01 01:30:56 +02:00
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
var newDataArray = newData == null ? null : LLUtils.asArray(newData);
|
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
|
logger.trace(MARKER_ROCKSDB,
|
|
|
|
"Updating {}. previous data: {}, updated data: {}",
|
|
|
|
LLUtils.toStringSafe(key),
|
|
|
|
LLUtils.toStringSafe(prevDataArray),
|
|
|
|
LLUtils.toStringSafe(newDataArray)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (prevData != null && newData == null) {
|
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
|
logger.trace(MARKER_ROCKSDB, "Deleting {} (after update)", LLUtils.toStringSafe(key));
|
2022-05-20 18:31:05 +02:00
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
writeValueBufferSize.record(0);
|
|
|
|
tx.delete(cfh, keyArray, true);
|
|
|
|
changed = true;
|
|
|
|
tx.commit();
|
|
|
|
} else if (newData != null && (prevData == null || !LLUtils.equals(prevData, newData))) {
|
2022-05-20 23:59:56 +02:00
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
|
logger.trace(MARKER_ROCKSDB,
|
2023-02-09 23:34:25 +01:00
|
|
|
"Writing {}: {} (after update)",
|
2022-05-20 23:59:56 +02:00
|
|
|
LLUtils.toStringSafe(key),
|
2023-02-09 23:34:25 +01:00
|
|
|
LLUtils.toStringSafe(newData)
|
2022-05-20 23:59:56 +02:00
|
|
|
);
|
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
writeValueBufferSize.record(newDataArray.length);
|
|
|
|
tx.put(cfh, keyArray, newDataArray);
|
|
|
|
changed = true;
|
|
|
|
tx.commit();
|
|
|
|
} else {
|
|
|
|
changed = false;
|
|
|
|
tx.rollback();
|
2022-05-20 18:31:05 +02:00
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
} finally {
|
|
|
|
tx.undoGetForUpdate(cfh, keyArray);
|
2022-05-20 23:59:56 +02:00
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
recordAtomicUpdateTime(changed, prevData != null, newData != null, initNanoTime);
|
|
|
|
return switch (returnMode) {
|
|
|
|
case NOTHING -> RESULT_NOTHING;
|
|
|
|
case CURRENT -> new UpdateAtomicResultCurrent(newData);
|
|
|
|
case PREVIOUS -> new UpdateAtomicResultPrevious(prevData);
|
|
|
|
case BINARY_CHANGED -> new UpdateAtomicResultBinaryChanged(changed);
|
|
|
|
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(prevData, newData));
|
|
|
|
};
|
2021-10-20 01:51:34 +02:00
|
|
|
}
|
2023-02-09 23:34:25 +01:00
|
|
|
} catch (Exception ex) {
|
|
|
|
throw new DBException("Failed to update key " + LLUtils.toStringSafe(key), ex);
|
2021-10-20 01:51:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean supportsTransactions() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|