1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
8 import java
.util
.ArrayList
;
9 import java
.util
.HashMap
;
10 import java
.util
.List
;
14 * Database with Transaction support
16 public class TransactionDB
extends RocksDB
17 implements TransactionalDB
<TransactionOptions
> {
19 private TransactionDBOptions transactionDbOptions_
;
22 * Private constructor.
24 * @param nativeHandle The native handle of the C++ TransactionDB object
26 private TransactionDB(final long nativeHandle
) {
31 * Open a TransactionDB, similar to {@link RocksDB#open(Options, String)}.
33 * @param options {@link org.rocksdb.Options} instance.
34 * @param transactionDbOptions {@link org.rocksdb.TransactionDBOptions}
36 * @param path the path to the rocksdb.
38 * @return a {@link TransactionDB} instance on success, null if the specified
39 * {@link TransactionDB} can not be opened.
41 * @throws RocksDBException if an error occurs whilst opening the database.
43 public static TransactionDB
open(final Options options
,
44 final TransactionDBOptions transactionDbOptions
, final String path
)
45 throws RocksDBException
{
46 final TransactionDB tdb
= new TransactionDB(open(options
.nativeHandle_
,
47 transactionDbOptions
.nativeHandle_
, path
));
49 // when non-default Options is used, keeping an Options reference
50 // in RocksDB can prevent Java to GC during the life-time of
51 // the currently-created RocksDB.
52 tdb
.storeOptionsInstance(options
);
53 tdb
.storeTransactionDbOptions(transactionDbOptions
);
59 * Open a TransactionDB, similar to
60 * {@link RocksDB#open(DBOptions, String, List, List)}.
62 * @param dbOptions {@link org.rocksdb.DBOptions} instance.
63 * @param transactionDbOptions {@link org.rocksdb.TransactionDBOptions}
65 * @param path the path to the rocksdb.
66 * @param columnFamilyDescriptors list of column family descriptors
67 * @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
69 * @return a {@link TransactionDB} instance on success, null if the specified
70 * {@link TransactionDB} can not be opened.
72 * @throws RocksDBException if an error occurs whilst opening the database.
74 public static TransactionDB
open(final DBOptions dbOptions
,
75 final TransactionDBOptions transactionDbOptions
,
77 final List
<ColumnFamilyDescriptor
> columnFamilyDescriptors
,
78 final List
<ColumnFamilyHandle
> columnFamilyHandles
)
79 throws RocksDBException
{
81 final byte[][] cfNames
= new byte[columnFamilyDescriptors
.size()][];
82 final long[] cfOptionHandles
= new long[columnFamilyDescriptors
.size()];
83 for (int i
= 0; i
< columnFamilyDescriptors
.size(); i
++) {
84 final ColumnFamilyDescriptor cfDescriptor
= columnFamilyDescriptors
86 cfNames
[i
] = cfDescriptor
.columnFamilyName();
87 cfOptionHandles
[i
] = cfDescriptor
.columnFamilyOptions().nativeHandle_
;
90 final long[] handles
= open(dbOptions
.nativeHandle_
,
91 transactionDbOptions
.nativeHandle_
, path
, cfNames
, cfOptionHandles
);
92 final TransactionDB tdb
= new TransactionDB(handles
[0]);
94 // when non-default Options is used, keeping an Options reference
95 // in RocksDB can prevent Java to GC during the life-time of
96 // the currently-created RocksDB.
97 tdb
.storeOptionsInstance(dbOptions
);
98 tdb
.storeTransactionDbOptions(transactionDbOptions
);
100 for (int i
= 1; i
< handles
.length
; i
++) {
101 columnFamilyHandles
.add(new ColumnFamilyHandle(tdb
, handles
[i
]));
108 * This is similar to {@link #close()} except that it
109 * throws an exception if any error occurs.
111 * This will not fsync the WAL files.
112 * If syncing is required, the caller must first call {@link #syncWal()}
113 * or {@link #write(WriteOptions, WriteBatch)} using an empty write batch
114 * with {@link WriteOptions#setSync(boolean)} set to true.
116 * See also {@link #close()}.
118 * @throws RocksDBException if an error occurs whilst closing.
120 public void closeE() throws RocksDBException
{
121 if (owningHandle_
.compareAndSet(true, false)) {
123 closeDatabase(nativeHandle_
);
131 * This is similar to {@link #closeE()} except that it
132 * silently ignores any errors.
134 * This will not fsync the WAL files.
135 * If syncing is required, the caller must first call {@link #syncWal()}
136 * or {@link #write(WriteOptions, WriteBatch)} using an empty write batch
137 * with {@link WriteOptions#setSync(boolean)} set to true.
139 * See also {@link #close()}.
142 public void close() {
143 if (owningHandle_
.compareAndSet(true, false)) {
145 closeDatabase(nativeHandle_
);
146 } catch (final RocksDBException e
) {
147 // silently ignore the error report
155 public Transaction
beginTransaction(final WriteOptions writeOptions
) {
156 return new Transaction(this, beginTransaction(nativeHandle_
,
157 writeOptions
.nativeHandle_
));
161 public Transaction
beginTransaction(final WriteOptions writeOptions
,
162 final TransactionOptions transactionOptions
) {
163 return new Transaction(this, beginTransaction(nativeHandle_
,
164 writeOptions
.nativeHandle_
, transactionOptions
.nativeHandle_
));
167 // TODO(AR) consider having beingTransaction(... oldTransaction) set a
168 // reference count inside Transaction, so that we can always call
169 // Transaction#close but the object is only disposed when there are as many
170 // closes as beginTransaction. Makes the try-with-resources paradigm easier for
174 public Transaction
beginTransaction(final WriteOptions writeOptions
,
175 final Transaction oldTransaction
) {
176 final long jtxnHandle
= beginTransaction_withOld(nativeHandle_
,
177 writeOptions
.nativeHandle_
, oldTransaction
.nativeHandle_
);
179 // RocksJava relies on the assumption that
180 // we do not allocate a new Transaction object
181 // when providing an old_txn
182 assert(jtxnHandle
== oldTransaction
.nativeHandle_
);
184 return oldTransaction
;
188 public Transaction
beginTransaction(final WriteOptions writeOptions
,
189 final TransactionOptions transactionOptions
,
190 final Transaction oldTransaction
) {
191 final long jtxn_handle
= beginTransaction_withOld(nativeHandle_
,
192 writeOptions
.nativeHandle_
, transactionOptions
.nativeHandle_
,
193 oldTransaction
.nativeHandle_
);
195 // RocksJava relies on the assumption that
196 // we do not allocate a new Transaction object
197 // when providing an old_txn
198 assert(jtxn_handle
== oldTransaction
.nativeHandle_
);
200 return oldTransaction
;
203 public Transaction
getTransactionByName(final String transactionName
) {
204 final long jtxnHandle
= getTransactionByName(nativeHandle_
, transactionName
);
205 if(jtxnHandle
== 0) {
209 final Transaction txn
= new Transaction(this, jtxnHandle
);
211 // this instance doesn't own the underlying C++ object
212 txn
.disOwnNativeHandle();
217 public List
<Transaction
> getAllPreparedTransactions() {
218 final long[] jtxnHandles
= getAllPreparedTransactions(nativeHandle_
);
220 final List
<Transaction
> txns
= new ArrayList
<>();
221 for(final long jtxnHandle
: jtxnHandles
) {
222 final Transaction txn
= new Transaction(this, jtxnHandle
);
224 // this instance doesn't own the underlying C++ object
225 txn
.disOwnNativeHandle();
232 public static class KeyLockInfo
{
233 private final String key
;
234 private final long[] transactionIDs
;
235 private final boolean exclusive
;
237 public KeyLockInfo(final String key
, final long transactionIDs
[],
238 final boolean exclusive
) {
240 this.transactionIDs
= transactionIDs
;
241 this.exclusive
= exclusive
;
249 public String
getKey() {
254 * Get the Transaction IDs.
256 * @return the Transaction IDs.
258 public long[] getTransactionIDs() {
259 return transactionIDs
;
263 * Get the Lock status.
265 * @return true if the lock is exclusive, false if the lock is shared.
267 public boolean isExclusive() {
273 * Returns map of all locks held.
275 * @return a map of all the locks held.
277 public Map
<Long
, KeyLockInfo
> getLockStatusData() {
278 return getLockStatusData(nativeHandle_
);
282 * Called from C++ native method {@link #getDeadlockInfoBuffer(long)}
283 * to construct a DeadlockInfo object.
285 * @param transactionID The transaction id
286 * @param columnFamilyId The id of the {@link ColumnFamilyHandle}
287 * @param waitingKey the key that we are waiting on
288 * @param exclusive true if the lock is exclusive, false if the lock is shared
290 * @return The waiting transactions
292 private DeadlockInfo
newDeadlockInfo(
293 final long transactionID
, final long columnFamilyId
,
294 final String waitingKey
, final boolean exclusive
) {
295 return new DeadlockInfo(transactionID
, columnFamilyId
,
296 waitingKey
, exclusive
);
299 public static class DeadlockInfo
{
300 private final long transactionID
;
301 private final long columnFamilyId
;
302 private final String waitingKey
;
303 private final boolean exclusive
;
305 private DeadlockInfo(final long transactionID
, final long columnFamilyId
,
306 final String waitingKey
, final boolean exclusive
) {
307 this.transactionID
= transactionID
;
308 this.columnFamilyId
= columnFamilyId
;
309 this.waitingKey
= waitingKey
;
310 this.exclusive
= exclusive
;
314 * Get the Transaction ID.
316 * @return the transaction ID
318 public long getTransactionID() {
319 return transactionID
;
323 * Get the Column Family ID.
325 * @return The column family ID
327 public long getColumnFamilyId() {
328 return columnFamilyId
;
332 * Get the key that we are waiting on.
334 * @return the key that we are waiting on
336 public String
getWaitingKey() {
341 * Get the Lock status.
343 * @return true if the lock is exclusive, false if the lock is shared.
345 public boolean isExclusive() {
350 public static class DeadlockPath
{
351 final DeadlockInfo
[] path
;
352 final boolean limitExceeded
;
354 public DeadlockPath(final DeadlockInfo
[] path
, final boolean limitExceeded
) {
356 this.limitExceeded
= limitExceeded
;
359 public boolean isEmpty() {
360 return path
.length
== 0 && !limitExceeded
;
364 public DeadlockPath
[] getDeadlockInfoBuffer() {
365 return getDeadlockInfoBuffer(nativeHandle_
);
368 public void setDeadlockInfoBufferSize(final int targetSize
) {
369 setDeadlockInfoBufferSize(nativeHandle_
, targetSize
);
372 private void storeTransactionDbOptions(
373 final TransactionDBOptions transactionDbOptions
) {
374 this.transactionDbOptions_
= transactionDbOptions
;
377 @Override protected final native void disposeInternal(final long handle
);
379 private static native long open(final long optionsHandle
,
380 final long transactionDbOptionsHandle
, final String path
)
381 throws RocksDBException
;
382 private static native long[] open(final long dbOptionsHandle
,
383 final long transactionDbOptionsHandle
, final String path
,
384 final byte[][] columnFamilyNames
, final long[] columnFamilyOptions
);
385 private native static void closeDatabase(final long handle
)
386 throws RocksDBException
;
387 private native long beginTransaction(final long handle
,
388 final long writeOptionsHandle
);
389 private native long beginTransaction(final long handle
,
390 final long writeOptionsHandle
, final long transactionOptionsHandle
);
391 private native long beginTransaction_withOld(final long handle
,
392 final long writeOptionsHandle
, final long oldTransactionHandle
);
393 private native long beginTransaction_withOld(final long handle
,
394 final long writeOptionsHandle
, final long transactionOptionsHandle
,
395 final long oldTransactionHandle
);
396 private native long getTransactionByName(final long handle
,
398 private native long[] getAllPreparedTransactions(final long handle
);
399 private native Map
<Long
, KeyLockInfo
> getLockStatusData(
401 private native DeadlockPath
[] getDeadlockInfoBuffer(final long handle
);
402 private native void setDeadlockInfoBufferSize(final long handle
,
403 final int targetSize
);