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 public Transaction
beginTransaction(final WriteOptions writeOptions
) {
109 return new Transaction(this, beginTransaction(nativeHandle_
,
110 writeOptions
.nativeHandle_
));
114 public Transaction
beginTransaction(final WriteOptions writeOptions
,
115 final TransactionOptions transactionOptions
) {
116 return new Transaction(this, beginTransaction(nativeHandle_
,
117 writeOptions
.nativeHandle_
, transactionOptions
.nativeHandle_
));
120 // TODO(AR) consider having beingTransaction(... oldTransaction) set a
121 // reference count inside Transaction, so that we can always call
122 // Transaction#close but the object is only disposed when there are as many
123 // closes as beginTransaction. Makes the try-with-resources paradigm easier for
127 public Transaction
beginTransaction(final WriteOptions writeOptions
,
128 final Transaction oldTransaction
) {
129 final long jtxnHandle
= beginTransaction_withOld(nativeHandle_
,
130 writeOptions
.nativeHandle_
, oldTransaction
.nativeHandle_
);
132 // RocksJava relies on the assumption that
133 // we do not allocate a new Transaction object
134 // when providing an old_txn
135 assert(jtxnHandle
== oldTransaction
.nativeHandle_
);
137 return oldTransaction
;
141 public Transaction
beginTransaction(final WriteOptions writeOptions
,
142 final TransactionOptions transactionOptions
,
143 final Transaction oldTransaction
) {
144 final long jtxn_handle
= beginTransaction_withOld(nativeHandle_
,
145 writeOptions
.nativeHandle_
, transactionOptions
.nativeHandle_
,
146 oldTransaction
.nativeHandle_
);
148 // RocksJava relies on the assumption that
149 // we do not allocate a new Transaction object
150 // when providing an old_txn
151 assert(jtxn_handle
== oldTransaction
.nativeHandle_
);
153 return oldTransaction
;
156 public Transaction
getTransactionByName(final String transactionName
) {
157 final long jtxnHandle
= getTransactionByName(nativeHandle_
, transactionName
);
158 if(jtxnHandle
== 0) {
162 final Transaction txn
= new Transaction(this, jtxnHandle
);
164 // this instance doesn't own the underlying C++ object
165 txn
.disOwnNativeHandle();
170 public List
<Transaction
> getAllPreparedTransactions() {
171 final long[] jtxnHandles
= getAllPreparedTransactions(nativeHandle_
);
173 final List
<Transaction
> txns
= new ArrayList
<>();
174 for(final long jtxnHandle
: jtxnHandles
) {
175 final Transaction txn
= new Transaction(this, jtxnHandle
);
177 // this instance doesn't own the underlying C++ object
178 txn
.disOwnNativeHandle();
185 public static class KeyLockInfo
{
186 private final String key
;
187 private final long[] transactionIDs
;
188 private final boolean exclusive
;
190 public KeyLockInfo(final String key
, final long transactionIDs
[],
191 final boolean exclusive
) {
193 this.transactionIDs
= transactionIDs
;
194 this.exclusive
= exclusive
;
202 public String
getKey() {
207 * Get the Transaction IDs.
209 * @return the Transaction IDs.
211 public long[] getTransactionIDs() {
212 return transactionIDs
;
216 * Get the Lock status.
218 * @return true if the lock is exclusive, false if the lock is shared.
220 public boolean isExclusive() {
226 * Returns map of all locks held.
228 * @return a map of all the locks held.
230 public Map
<Long
, KeyLockInfo
> getLockStatusData() {
231 return getLockStatusData(nativeHandle_
);
235 * Called from C++ native method {@link #getDeadlockInfoBuffer(long)}
236 * to construct a DeadlockInfo object.
238 * @param transactionID The transaction id
239 * @param columnFamilyId The id of the {@link ColumnFamilyHandle}
240 * @param waitingKey the key that we are waiting on
241 * @param exclusive true if the lock is exclusive, false if the lock is shared
243 * @return The waiting transactions
245 private DeadlockInfo
newDeadlockInfo(
246 final long transactionID
, final long columnFamilyId
,
247 final String waitingKey
, final boolean exclusive
) {
248 return new DeadlockInfo(transactionID
, columnFamilyId
,
249 waitingKey
, exclusive
);
252 public static class DeadlockInfo
{
253 private final long transactionID
;
254 private final long columnFamilyId
;
255 private final String waitingKey
;
256 private final boolean exclusive
;
258 private DeadlockInfo(final long transactionID
, final long columnFamilyId
,
259 final String waitingKey
, final boolean exclusive
) {
260 this.transactionID
= transactionID
;
261 this.columnFamilyId
= columnFamilyId
;
262 this.waitingKey
= waitingKey
;
263 this.exclusive
= exclusive
;
267 * Get the Transaction ID.
269 * @return the transaction ID
271 public long getTransactionID() {
272 return transactionID
;
276 * Get the Column Family ID.
278 * @return The column family ID
280 public long getColumnFamilyId() {
281 return columnFamilyId
;
285 * Get the key that we are waiting on.
287 * @return the key that we are waiting on
289 public String
getWaitingKey() {
294 * Get the Lock status.
296 * @return true if the lock is exclusive, false if the lock is shared.
298 public boolean isExclusive() {
303 public static class DeadlockPath
{
304 final DeadlockInfo
[] path
;
305 final boolean limitExceeded
;
307 public DeadlockPath(final DeadlockInfo
[] path
, final boolean limitExceeded
) {
309 this.limitExceeded
= limitExceeded
;
312 public boolean isEmpty() {
313 return path
.length
== 0 && !limitExceeded
;
317 public DeadlockPath
[] getDeadlockInfoBuffer() {
318 return getDeadlockInfoBuffer(nativeHandle_
);
321 public void setDeadlockInfoBufferSize(final int targetSize
) {
322 setDeadlockInfoBufferSize(nativeHandle_
, targetSize
);
325 private void storeTransactionDbOptions(
326 final TransactionDBOptions transactionDbOptions
) {
327 this.transactionDbOptions_
= transactionDbOptions
;
330 private static native long open(final long optionsHandle
,
331 final long transactionDbOptionsHandle
, final String path
)
332 throws RocksDBException
;
333 private static native long[] open(final long dbOptionsHandle
,
334 final long transactionDbOptionsHandle
, final String path
,
335 final byte[][] columnFamilyNames
, final long[] columnFamilyOptions
);
336 private native long beginTransaction(final long handle
,
337 final long writeOptionsHandle
);
338 private native long beginTransaction(final long handle
,
339 final long writeOptionsHandle
, final long transactionOptionsHandle
);
340 private native long beginTransaction_withOld(final long handle
,
341 final long writeOptionsHandle
, final long oldTransactionHandle
);
342 private native long beginTransaction_withOld(final long handle
,
343 final long writeOptionsHandle
, final long transactionOptionsHandle
,
344 final long oldTransactionHandle
);
345 private native long getTransactionByName(final long handle
,
347 private native long[] getAllPreparedTransactions(final long handle
);
348 private native Map
<Long
, KeyLockInfo
> getLockStatusData(
350 private native DeadlockPath
[] getDeadlockInfoBuffer(final long handle
);
351 private native void setDeadlockInfoBufferSize(final long handle
,
352 final int targetSize
);
353 @Override protected final native void disposeInternal(final long handle
);