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 package org.rocksdb;
7 
8 import java.util.List;
9 
10 /**
11  * Database with TTL support.
12  *
13  * <p><strong>Use case</strong></p>
14  * <p>This API should be used to open the db when key-values inserted are
15  * meant to be removed from the db in a non-strict 'ttl' amount of time
16  * Therefore, this guarantees that key-values inserted will remain in the
17  * db for &gt;= ttl amount of time and the db will make efforts to remove the
18  * key-values as soon as possible after ttl seconds of their insertion.
19  * </p>
20  *
21  * <p><strong>Behaviour</strong></p>
22  * <p>TTL is accepted in seconds
23  * (int32_t)Timestamp(creation) is suffixed to values in Put internally
24  * Expired TTL values deleted in compaction only:(Timestamp+ttl&lt;time_now)
25  * Get/Iterator may return expired entries(compaction not run on them yet)
26  * Different TTL may be used during different Opens
27  * </p>
28  *
29  * <p><strong>Example</strong></p>
30  * <ul>
31  * <li>Open1 at t=0 with ttl=4 and insert k1,k2, close at t=2</li>
32  * <li>Open2 at t=3 with ttl=5. Now k1,k2 should be deleted at t&gt;=5</li>
33  * </ul>
34  *
35  * <p>
36  * read_only=true opens in the usual read-only mode. Compactions will not be
37  *  triggered(neither manual nor automatic), so no expired entries removed
38  * </p>
39  *
40  * <p><strong>Constraints</strong></p>
41  * <p>Not specifying/passing or non-positive TTL behaves
42  * like TTL = infinity</p>
43  *
44  * <p><strong>!!!WARNING!!!</strong></p>
45  * <p>Calling DB::Open directly to re-open a db created by this API will get
46  * corrupt values(timestamp suffixed) and no ttl effect will be there
47  * during the second Open, so use this API consistently to open the db
48  * Be careful when passing ttl with a small positive value because the
49  * whole database may be deleted in a small amount of time.</p>
50  */
51 public class TtlDB extends RocksDB {
52 
53   /**
54    * <p>Opens a TtlDB.</p>
55    *
56    * <p>Database is opened in read-write mode without default TTL.</p>
57    *
58    * @param options {@link org.rocksdb.Options} instance.
59    * @param db_path path to database.
60    *
61    * @return TtlDB instance.
62    *
63    * @throws RocksDBException thrown if an error occurs within the native
64    *     part of the library.
65    */
open(final Options options, final String db_path)66   public static TtlDB open(final Options options, final String db_path)
67       throws RocksDBException {
68     return open(options, db_path, 0, false);
69   }
70 
71   /**
72    * <p>Opens a TtlDB.</p>
73    *
74    * @param options {@link org.rocksdb.Options} instance.
75    * @param db_path path to database.
76    * @param ttl time to live for new entries.
77    * @param readOnly boolean value indicating if database if db is
78    *     opened read-only.
79    *
80    * @return TtlDB instance.
81    *
82    * @throws RocksDBException thrown if an error occurs within the native
83    *     part of the library.
84    */
open(final Options options, final String db_path, final int ttl, final boolean readOnly)85   public static TtlDB open(final Options options, final String db_path,
86       final int ttl, final boolean readOnly) throws RocksDBException {
87     return new TtlDB(open(options.nativeHandle_, db_path, ttl, readOnly));
88   }
89 
90   /**
91    * <p>Opens a TtlDB.</p>
92    *
93    * @param options {@link org.rocksdb.Options} instance.
94    * @param db_path path to database.
95    * @param columnFamilyDescriptors list of column family descriptors
96    * @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
97    *     on open.
98    * @param ttlValues time to live values per column family handle
99    * @param readOnly boolean value indicating if database if db is
100    *     opened read-only.
101    *
102    * @return TtlDB instance.
103    *
104    * @throws RocksDBException thrown if an error occurs within the native
105    *     part of the library.
106    * @throws java.lang.IllegalArgumentException when there is not a ttl value
107    *     per given column family handle.
108    */
open(final DBOptions options, final String db_path, final List<ColumnFamilyDescriptor> columnFamilyDescriptors, final List<ColumnFamilyHandle> columnFamilyHandles, final List<Integer> ttlValues, final boolean readOnly)109   public static TtlDB open(final DBOptions options, final String db_path,
110       final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
111       final List<ColumnFamilyHandle> columnFamilyHandles,
112       final List<Integer> ttlValues, final boolean readOnly)
113       throws RocksDBException {
114     if (columnFamilyDescriptors.size() != ttlValues.size()) {
115       throw new IllegalArgumentException("There must be a ttl value per column"
116           + "family handle.");
117     }
118 
119     final byte[][] cfNames = new byte[columnFamilyDescriptors.size()][];
120     final long[] cfOptionHandles = new long[columnFamilyDescriptors.size()];
121     for (int i = 0; i < columnFamilyDescriptors.size(); i++) {
122       final ColumnFamilyDescriptor cfDescriptor =
123           columnFamilyDescriptors.get(i);
124       cfNames[i] = cfDescriptor.getName();
125       cfOptionHandles[i] = cfDescriptor.getOptions().nativeHandle_;
126     }
127 
128     final int ttlVals[] = new int[ttlValues.size()];
129     for(int i = 0; i < ttlValues.size(); i++) {
130       ttlVals[i] = ttlValues.get(i);
131     }
132     final long[] handles = openCF(options.nativeHandle_, db_path,
133             cfNames, cfOptionHandles, ttlVals, readOnly);
134 
135     final TtlDB ttlDB = new TtlDB(handles[0]);
136     for (int i = 1; i < handles.length; i++) {
137       columnFamilyHandles.add(new ColumnFamilyHandle(ttlDB, handles[i]));
138     }
139     return ttlDB;
140   }
141 
142   /**
143    * <p>Close the TtlDB instance and release resource.</p>
144    *
145    * This is similar to {@link #close()} except that it
146    * throws an exception if any error occurs.
147    *
148    * This will not fsync the WAL files.
149    * If syncing is required, the caller must first call {@link #syncWal()}
150    * or {@link #write(WriteOptions, WriteBatch)} using an empty write batch
151    * with {@link WriteOptions#setSync(boolean)} set to true.
152    *
153    * See also {@link #close()}.
154    *
155    * @throws RocksDBException if an error occurs whilst closing.
156    */
closeE()157   public void closeE() throws RocksDBException {
158     if (owningHandle_.compareAndSet(true, false)) {
159       try {
160         closeDatabase(nativeHandle_);
161       } finally {
162         disposeInternal();
163       }
164     }
165   }
166 
167   /**
168    * <p>Close the TtlDB instance and release resource.</p>
169    *
170    *
171    * This will not fsync the WAL files.
172    * If syncing is required, the caller must first call {@link #syncWal()}
173    * or {@link #write(WriteOptions, WriteBatch)} using an empty write batch
174    * with {@link WriteOptions#setSync(boolean)} set to true.
175    *
176    * See also {@link #close()}.
177    */
178   @Override
close()179   public void close() {
180     if (owningHandle_.compareAndSet(true, false)) {
181       try {
182         closeDatabase(nativeHandle_);
183       } catch (final RocksDBException e) {
184         // silently ignore the error report
185       } finally {
186         disposeInternal();
187       }
188     }
189   }
190 
191   /**
192    * <p>Creates a new ttl based column family with a name defined
193    * in given ColumnFamilyDescriptor and allocates a
194    * ColumnFamilyHandle within an internal structure.</p>
195    *
196    * <p>The ColumnFamilyHandle is automatically disposed with DB
197    * disposal.</p>
198    *
199    * @param columnFamilyDescriptor column family to be created.
200    * @param ttl TTL to set for this column family.
201    *
202    * @return {@link org.rocksdb.ColumnFamilyHandle} instance.
203    *
204    * @throws RocksDBException thrown if error happens in underlying
205    *    native library.
206    */
createColumnFamilyWithTtl( final ColumnFamilyDescriptor columnFamilyDescriptor, final int ttl)207   public ColumnFamilyHandle createColumnFamilyWithTtl(
208       final ColumnFamilyDescriptor columnFamilyDescriptor,
209       final int ttl) throws RocksDBException {
210     return new ColumnFamilyHandle(this,
211         createColumnFamilyWithTtl(nativeHandle_,
212             columnFamilyDescriptor.getName(),
213             columnFamilyDescriptor.getOptions().nativeHandle_, ttl));
214   }
215 
216   /**
217    * <p>A protected constructor that will be used in the static
218    * factory method
219    * {@link #open(Options, String, int, boolean)}
220    * and
221    * {@link #open(DBOptions, String, java.util.List, java.util.List,
222    * java.util.List, boolean)}.
223    * </p>
224    *
225    * @param nativeHandle The native handle of the C++ TtlDB object
226    */
TtlDB(final long nativeHandle)227   protected TtlDB(final long nativeHandle) {
228     super(nativeHandle);
229   }
230 
disposeInternal(final long handle)231   @Override protected native void disposeInternal(final long handle);
232 
open(final long optionsHandle, final String db_path, final int ttl, final boolean readOnly)233   private native static long open(final long optionsHandle,
234       final String db_path, final int ttl, final boolean readOnly)
235       throws RocksDBException;
openCF(final long optionsHandle, final String db_path, final byte[][] columnFamilyNames, final long[] columnFamilyOptions, final int[] ttlValues, final boolean readOnly)236   private native static long[] openCF(final long optionsHandle,
237       final String db_path, final byte[][] columnFamilyNames,
238       final long[] columnFamilyOptions, final int[] ttlValues,
239       final boolean readOnly) throws RocksDBException;
createColumnFamilyWithTtl(final long handle, final byte[] columnFamilyName, final long columnFamilyOptions, int ttl)240   private native long createColumnFamilyWithTtl(final long handle,
241       final byte[] columnFamilyName, final long columnFamilyOptions, int ttl)
242       throws RocksDBException;
closeDatabase(final long handle)243   private native static void closeDatabase(final long handle)
244       throws RocksDBException;
245 }
246