content: // Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
  //  This source code is licensed under both the GPLv2 (found in the
  //  COPYING file in the root directory) and Apache 2.0 License
  //  (found in the LICENSE.Apache file in the root directory).
  
  package org.rocksdb;
  
  import java.util.List;
  
  /**
   * Database with TTL support.
   *
   * <p><strong>Use case</strong></p>
   * <p>This API should be used to open the db when key-values inserted are
   * meant to be removed from the db in a non-strict 'ttl' amount of time
   * Therefore, this guarantees that key-values inserted will remain in the
   * db for &gt;= ttl amount of time and the db will make efforts to remove the
   * key-values as soon as possible after ttl seconds of their insertion.
   * </p>
   *
   * <p><strong>Behaviour</strong></p>
   * <p>TTL is accepted in seconds
   * (int32_t)Timestamp(creation) is suffixed to values in Put internally
   * Expired TTL values deleted in compaction only:(Timestamp+ttl&lt;time_now)
   * Get/Iterator may return expired entries(compaction not run on them yet)
   * Different TTL may be used during different Opens
   * </p>
   *
   * <p><strong>Example</strong></p>
   * <ul>
   * <li>Open1 at t=0 with ttl=4 and insert k1,k2, close at t=2</li>
   * <li>Open2 at t=3 with ttl=5. Now k1,k2 should be deleted at t&gt;=5</li>
   * </ul>
   *
   * <p>
   * read_only=true opens in the usual read-only mode. Compactions will not be
   *  triggered(neither manual nor automatic), so no expired entries removed
   * </p>
   *
   * <p><strong>Constraints</strong></p>
   * <p>Not specifying/passing or non-positive TTL behaves
   * like TTL = infinity</p>
   *
   * <p><strong>!!!WARNING!!!</strong></p>
   * <p>Calling DB::Open directly to re-open a db created by this API will get
   * corrupt values(timestamp suffixed) and no ttl effect will be there
   * during the second Open, so use this API consistently to open the db
   * Be careful when passing ttl with a small positive value because the
   * whole database may be deleted in a small amount of time.</p>
   */
  public class TtlDB extends RocksDB {
  
    /**
     * <p>Opens a TtlDB.</p>
     *
     * <p>Database is opened in read-write mode without default TTL.</p>
     *
     * @param options {@link org.rocksdb.Options} instance.
     * @param db_path path to database.
     *
     * @return TtlDB instance.
     *
     * @throws RocksDBException thrown if an error occurs within the native
     *     part of the library.
     */
    public static TtlDB open(final Options options, final String db_path)
        throws RocksDBException {
      return open(options, db_path, 0, false);
    }
  
    /**
     * <p>Opens a TtlDB.</p>
     *
     * @param options {@link org.rocksdb.Options} instance.
     * @param db_path path to database.
     * @param ttl time to live for new entries.
     * @param readOnly boolean value indicating if database if db is
     *     opened read-only.
     *
     * @return TtlDB instance.
     *
     * @throws RocksDBException thrown if an error occurs within the native
     *     part of the library.
     */
    public static TtlDB open(final Options options, final String db_path,
        final int ttl, final boolean readOnly) throws RocksDBException {
      return new TtlDB(open(options.nativeHandle_, db_path, ttl, readOnly));
    }
  
    /**
     * <p>Opens a TtlDB.</p>
     *
     * @param options {@link org.rocksdb.Options} instance.
     * @param db_path path to database.
     * @param columnFamilyDescriptors list of column family descriptors
     * @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
     *     on open.
     * @param ttlValues time to live values per column family handle
     * @param readOnly boolean value indicating if database if db is
     *     opened read-only.
     *
     * @return TtlDB instance.
     *
     * @throws RocksDBException thrown if an error occurs within the native
     *     part of the library.
     * @throws java.lang.IllegalArgumentException when there is not a ttl value
     *     per given column family handle.
     */
    public static TtlDB open(final DBOptions options, final String db_path,
        final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
        final List<ColumnFamilyHandle> columnFamilyHandles,
        final List<Integer> ttlValues, final boolean readOnly)
        throws RocksDBException {
      if (columnFamilyDescriptors.size() != ttlValues.size()) {
        throw new IllegalArgumentException("There must be a ttl value per column"
            + "family handle.");
      }
  
      final byte[][] cfNames = new byte[columnFamilyDescriptors.size()][];
      final long[] cfOptionHandles = new long[columnFamilyDescriptors.size()];
      for (int i = 0; i < columnFamilyDescriptors.size(); i++) {
        final ColumnFamilyDescriptor cfDescriptor =
            columnFamilyDescriptors.get(i);
        cfNames[i] = cfDescriptor.columnFamilyName();
        cfOptionHandles[i] = cfDescriptor.columnFamilyOptions().nativeHandle_;
      }
  
      final int ttlVals[] = new int[ttlValues.size()];
      for(int i = 0; i < ttlValues.size(); i++) {
        ttlVals[i] = ttlValues.get(i);
      }
      final long[] handles = openCF(options.nativeHandle_, db_path,
              cfNames, cfOptionHandles, ttlVals, readOnly);
  
      final TtlDB ttlDB = new TtlDB(handles[0]);
      for (int i = 1; i < handles.length; i++) {
        columnFamilyHandles.add(new ColumnFamilyHandle(ttlDB, handles[i]));
      }
      return ttlDB;
    }
  
    /**
     * <p>Creates a new ttl based column family with a name defined
     * in given ColumnFamilyDescriptor and allocates a
     * ColumnFamilyHandle within an internal structure.</p>
     *
     * <p>The ColumnFamilyHandle is automatically disposed with DB
     * disposal.</p>
     *
     * @param columnFamilyDescriptor column family to be created.
     * @param ttl TTL to set for this column family.
     *
     * @return {@link org.rocksdb.ColumnFamilyHandle} instance.
     *
     * @throws RocksDBException thrown if error happens in underlying
     *    native library.
     */
    public ColumnFamilyHandle createColumnFamilyWithTtl(
        final ColumnFamilyDescriptor columnFamilyDescriptor,
        final int ttl) throws RocksDBException {
      return new ColumnFamilyHandle(this,
          createColumnFamilyWithTtl(nativeHandle_,
              columnFamilyDescriptor.columnFamilyName(),
              columnFamilyDescriptor.columnFamilyOptions().nativeHandle_, ttl));
    }
  
    /**
     * <p>Close the TtlDB instance and release resource.</p>
     *
     * <p>Internally, TtlDB owns the {@code rocksdb::DB} pointer
     * to its associated {@link org.rocksdb.RocksDB}. The release
     * of that RocksDB pointer is handled in the destructor of the
     * c++ {@code rocksdb::TtlDB} and should be transparent to
     * Java developers.</p>
     */
    @Override
    public void close() {
        super.close();
    }
  
    /**
     * <p>A protected constructor that will be used in the static
     * factory method
     * {@link #open(Options, String, int, boolean)}
     * and
     * {@link #open(DBOptions, String, java.util.List, java.util.List,
     * java.util.List, boolean)}.
     * </p>
     *
     * @param nativeHandle The native handle of the C++ TtlDB object
     */
    protected TtlDB(final long nativeHandle) {
      super(nativeHandle);
    }
  
    @Override protected void finalize() throws Throwable {
      close(); //TODO(AR) revisit here when implementing AutoCloseable
      super.finalize();
    }
  
    private native static long open(final long optionsHandle,
        final String db_path, final int ttl, final boolean readOnly)
        throws RocksDBException;
    private native static long[] openCF(final long optionsHandle,
        final String db_path, final byte[][] columnFamilyNames,
        final long[] columnFamilyOptions, final int[] ttlValues,
        final boolean readOnly) throws RocksDBException;
    private native long createColumnFamilyWithTtl(final long handle,
        final byte[] columnFamilyName, final long columnFamilyOptions, int ttl)
        throws RocksDBException;
  }
  