package org.deephacks.confit.internal.berkeley; import com.google.common.base.Optional; import com.sleepycat.je.Cursor; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.Environment; import com.sleepycat.je.LockMode; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.Sequence; import com.sleepycat.je.SequenceConfig; import com.sleepycat.je.Transaction; import org.deephacks.confit.serialization.BytesUtils; public class TxDatabase { private final Database db; private final Environment env; private final SequenceConfig conf; private static final ThreadLocal<Transaction> TX = new ThreadLocal<>(); public TxDatabase(Database db) { this.db = db; this.env = db.getEnvironment(); this.conf = new SequenceConfig(); conf.setAllowCreate(true); } public Database getDb() { return db; } public Environment getEnv() { return env; } public Cursor openCursor() { return db.openCursor(getTx(), null); } public boolean put(byte[] key, byte[] value) { Transaction tx = getTx(); DatabaseEntry dbKey = new DatabaseEntry(key); DatabaseEntry dbValue = new DatabaseEntry(value); if (OperationStatus.KEYEXIST == db.putNoOverwrite(tx, dbKey, dbValue)) { return false; } return true; } public Optional<byte[]> get(byte[] key) { Transaction tx = getTx(); DatabaseEntry dbKey = new DatabaseEntry(key); DatabaseEntry dbValue = new DatabaseEntry(); if (OperationStatus.NOTFOUND == db.get(tx, dbKey, dbValue, LockMode.READ_COMMITTED)) { return Optional.absent(); } return Optional.fromNullable(dbValue.getData()); } public void list(byte[] min, byte[] max, ForEachKey forEach) { DatabaseEntry dbKey = new DatabaseEntry(min); DatabaseEntry dbValue = new DatabaseEntry(); try (Cursor cursor = openCursor()) { if (cursor.getSearchKeyRange(dbKey, dbValue, LockMode.DEFAULT) != OperationStatus.SUCCESS) { return; } byte[] key = dbKey.getData(); if (BytesUtils.compareTo(key, 0, key.length, min, 0, min.length) >= 0) { forEach.match(key, dbValue.getData()); } while (cursor.getNextNoDup(dbKey, dbValue, LockMode.DEFAULT) == OperationStatus.SUCCESS) { key = dbKey.getData(); if (BytesUtils.compareTo(key, 0, key.length, max, 0, max.length) > 0) { return; } if (!forEach.match(key, dbValue.getData())) { return; } } } } public Transaction getTx() { Transaction tx = TX.get(); if (tx == null) { tx = env.beginTransaction(null, null); TX.set(tx); } return tx; } public void commit() { Transaction tx = TX.get(); if (tx == null) { return; } TX.set(null); tx.commit(); } public void abort() { Transaction tx = TX.get(); if (tx == null) { return; } TX.set(null); tx.abort(); } public long increment(byte[] key) { Transaction tx = getTx(); Sequence sequence = db.openSequence(tx, new DatabaseEntry(key), conf); return sequence.get(tx, 1); } public static interface ForEachKey { public boolean match(byte[] key, byte[] data); } }