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 >= 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<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>=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