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).
5 
6 import org.rocksdb.*;
7 
8 import static java.nio.charset.StandardCharsets.UTF_8;
9 
10 /**
11  * Demonstrates using Transactions on an OptimisticTransactionDB with
12  * varying isolation guarantees
13  */
14 public class OptimisticTransactionSample {
15   private static final String dbPath = "/tmp/rocksdb_optimistic_transaction_example";
16 
main(final String args[])17   public static final void main(final String args[]) throws RocksDBException {
18 
19     try(final Options options = new Options()
20         .setCreateIfMissing(true);
21         final OptimisticTransactionDB txnDb =
22             OptimisticTransactionDB.open(options, dbPath)) {
23 
24       try (final WriteOptions writeOptions = new WriteOptions();
25            final ReadOptions readOptions = new ReadOptions()) {
26 
27         ////////////////////////////////////////////////////////
28         //
29         // Simple OptimisticTransaction Example ("Read Committed")
30         //
31         ////////////////////////////////////////////////////////
32         readCommitted(txnDb, writeOptions, readOptions);
33 
34 
35         ////////////////////////////////////////////////////////
36         //
37         // "Repeatable Read" (Snapshot Isolation) Example
38         //   -- Using a single Snapshot
39         //
40         ////////////////////////////////////////////////////////
41         repeatableRead(txnDb, writeOptions, readOptions);
42 
43 
44         ////////////////////////////////////////////////////////
45         //
46         // "Read Committed" (Monotonic Atomic Views) Example
47         //   --Using multiple Snapshots
48         //
49         ////////////////////////////////////////////////////////
50         readCommitted_monotonicAtomicViews(txnDb, writeOptions, readOptions);
51       }
52     }
53   }
54 
55   /**
56    * Demonstrates "Read Committed" isolation
57    */
readCommitted(final OptimisticTransactionDB txnDb, final WriteOptions writeOptions, final ReadOptions readOptions)58   private static void readCommitted(final OptimisticTransactionDB txnDb,
59       final WriteOptions writeOptions, final ReadOptions readOptions)
60       throws RocksDBException {
61     final byte key1[] = "abc".getBytes(UTF_8);
62     final byte value1[] = "def".getBytes(UTF_8);
63 
64     final byte key2[] = "xyz".getBytes(UTF_8);
65     final byte value2[] = "zzz".getBytes(UTF_8);
66 
67     // Start a transaction
68     try(final Transaction txn = txnDb.beginTransaction(writeOptions)) {
69       // Read a key in this transaction
70       byte[] value = txn.get(readOptions, key1);
71       assert(value == null);
72 
73       // Write a key in this transaction
74       txn.put(key1, value1);
75 
76       // Read a key OUTSIDE this transaction. Does not affect txn.
77       value = txnDb.get(readOptions, key1);
78       assert(value == null);
79 
80       // Write a key OUTSIDE of this transaction.
81       // Does not affect txn since this is an unrelated key.
82       // If we wrote key 'abc' here, the transaction would fail to commit.
83       txnDb.put(writeOptions, key2, value2);
84 
85       // Commit transaction
86       txn.commit();
87     }
88   }
89 
90   /**
91    * Demonstrates "Repeatable Read" (Snapshot Isolation) isolation
92    */
repeatableRead(final OptimisticTransactionDB txnDb, final WriteOptions writeOptions, final ReadOptions readOptions)93   private static void repeatableRead(final OptimisticTransactionDB txnDb,
94       final WriteOptions writeOptions, final ReadOptions readOptions)
95       throws RocksDBException {
96 
97     final byte key1[] = "ghi".getBytes(UTF_8);
98     final byte value1[] = "jkl".getBytes(UTF_8);
99 
100     // Set a snapshot at start of transaction by setting setSnapshot(true)
101     try(final OptimisticTransactionOptions txnOptions =
102             new OptimisticTransactionOptions().setSetSnapshot(true);
103         final Transaction txn =
104             txnDb.beginTransaction(writeOptions, txnOptions)) {
105 
106       final Snapshot snapshot = txn.getSnapshot();
107 
108       // Write a key OUTSIDE of transaction
109       txnDb.put(writeOptions, key1, value1);
110 
111       // Read a key using the snapshot.
112       readOptions.setSnapshot(snapshot);
113       final byte[] value = txn.getForUpdate(readOptions, key1, true);
114       assert (value == null);
115 
116       try {
117         // Attempt to commit transaction
118         txn.commit();
119         throw new IllegalStateException();
120       } catch(final RocksDBException e) {
121         // Transaction could not commit since the write outside of the txn
122         // conflicted with the read!
123         assert(e.getStatus().getCode() == Status.Code.Busy);
124       }
125 
126       txn.rollback();
127     } finally {
128       // Clear snapshot from read options since it is no longer valid
129       readOptions.setSnapshot(null);
130     }
131   }
132 
133   /**
134    * Demonstrates "Read Committed" (Monotonic Atomic Views) isolation
135    *
136    * In this example, we set the snapshot multiple times.  This is probably
137    * only necessary if you have very strict isolation requirements to
138    * implement.
139    */
readCommitted_monotonicAtomicViews( final OptimisticTransactionDB txnDb, final WriteOptions writeOptions, final ReadOptions readOptions)140   private static void readCommitted_monotonicAtomicViews(
141       final OptimisticTransactionDB txnDb, final WriteOptions writeOptions,
142       final ReadOptions readOptions) throws RocksDBException {
143 
144     final byte keyX[] = "x".getBytes(UTF_8);
145     final byte valueX[] = "x".getBytes(UTF_8);
146 
147     final byte keyY[] = "y".getBytes(UTF_8);
148     final byte valueY[] = "y".getBytes(UTF_8);
149 
150     try (final OptimisticTransactionOptions txnOptions =
151              new OptimisticTransactionOptions().setSetSnapshot(true);
152          final Transaction txn =
153              txnDb.beginTransaction(writeOptions, txnOptions)) {
154 
155       // Do some reads and writes to key "x"
156       Snapshot snapshot = txnDb.getSnapshot();
157       readOptions.setSnapshot(snapshot);
158       byte[] value = txn.get(readOptions, keyX);
159       txn.put(valueX, valueX);
160 
161       // Do a write outside of the transaction to key "y"
162       txnDb.put(writeOptions, keyY, valueY);
163 
164       // Set a new snapshot in the transaction
165       txn.setSnapshot();
166       snapshot = txnDb.getSnapshot();
167       readOptions.setSnapshot(snapshot);
168 
169       // Do some reads and writes to key "y"
170       // Since the snapshot was advanced, the write done outside of the
171       // transaction does not conflict.
172       value = txn.getForUpdate(readOptions, keyY, true);
173       txn.put(keyY, valueY);
174 
175       // Commit.  Since the snapshot was advanced, the write done outside of the
176       // transaction does not prevent this transaction from Committing.
177       txn.commit();
178 
179     } finally {
180       // Clear snapshot from read options since it is no longer valid
181       readOptions.setSnapshot(null);
182     }
183   }
184 }
185