RocksJava - Add errorIfLogFileExists parameter to RocksDB.openReadOnly (#7046)

Summary:
Expose from C++ API to Java API.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/7046

Reviewed By: riversand963

Differential Revision: D23726297

Pulled By: pdillinger

fbshipit-source-id: fc66bf626ce6fe9797e7d021ac849eacab91bf6d
main
Adam Retter 4 years ago committed by Facebook GitHub Bot
parent b9750c7c3c
commit 3ac07a12fe
  1. 20
      db/c.cc
  2. 4
      db/db_impl/db_impl.h
  3. 14
      db/db_impl/db_impl_open.cc
  4. 14
      db/db_impl/db_impl_readonly.cc
  5. 2
      db/db_impl/db_impl_readonly.h
  6. 4
      db/db_impl/db_impl_secondary.cc
  7. 4
      db/db_impl/db_impl_secondary.h
  8. 4
      include/rocksdb/c.h
  9. 4
      include/rocksdb/db.h
  10. 40
      java/rocksjni/rocksjni.cc
  11. 100
      java/src/main/java/org/rocksdb/RocksDB.java
  12. 27
      java/src/test/java/org/rocksdb/ReadOnlyTest.java

@ -504,13 +504,13 @@ rocksdb_t* rocksdb_open_with_ttl(
return result; return result;
} }
rocksdb_t* rocksdb_open_for_read_only( rocksdb_t* rocksdb_open_for_read_only(const rocksdb_options_t* options,
const rocksdb_options_t* options, const char* name,
const char* name, unsigned char error_if_wal_file_exists,
unsigned char error_if_log_file_exist, char** errptr) {
char** errptr) {
DB* db; DB* db;
if (SaveError(errptr, DB::OpenForReadOnly(options->rep, std::string(name), &db, error_if_log_file_exist))) { if (SaveError(errptr, DB::OpenForReadOnly(options->rep, std::string(name),
&db, error_if_wal_file_exists))) {
return nullptr; return nullptr;
} }
rocksdb_t* result = new rocksdb_t; rocksdb_t* result = new rocksdb_t;
@ -747,7 +747,7 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
int num_column_families, const char* const* column_family_names, int num_column_families, const char* const* column_family_names,
const rocksdb_options_t* const* column_family_options, const rocksdb_options_t* const* column_family_options,
rocksdb_column_family_handle_t** column_family_handles, rocksdb_column_family_handle_t** column_family_handles,
unsigned char error_if_log_file_exist, char** errptr) { unsigned char error_if_wal_file_exists, char** errptr) {
std::vector<ColumnFamilyDescriptor> column_families; std::vector<ColumnFamilyDescriptor> column_families;
for (int i = 0; i < num_column_families; i++) { for (int i = 0; i < num_column_families; i++) {
column_families.push_back(ColumnFamilyDescriptor( column_families.push_back(ColumnFamilyDescriptor(
@ -757,8 +757,10 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
DB* db; DB* db;
std::vector<ColumnFamilyHandle*> handles; std::vector<ColumnFamilyHandle*> handles;
if (SaveError(errptr, DB::OpenForReadOnly(DBOptions(db_options->rep), if (SaveError(errptr,
std::string(name), column_families, &handles, &db, error_if_log_file_exist))) { DB::OpenForReadOnly(DBOptions(db_options->rep),
std::string(name), column_families,
&handles, &db, error_if_wal_file_exists))) {
return nullptr; return nullptr;
} }

@ -1186,8 +1186,8 @@ class DBImpl : public DB {
// skipped. // skipped.
virtual Status Recover( virtual Status Recover(
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
bool read_only = false, bool error_if_log_file_exist = false, bool read_only = false, bool error_if_wal_file_exists = false,
bool error_if_data_exists_in_logs = false, bool error_if_data_exists_in_wals = false,
uint64_t* recovered_seq = nullptr); uint64_t* recovered_seq = nullptr);
virtual bool OwnTablesAndLogs() const { return true; } virtual bool OwnTablesAndLogs() const { return true; }

@ -364,7 +364,7 @@ IOStatus Directories::SetDirectories(FileSystem* fs, const std::string& dbname,
Status DBImpl::Recover( Status DBImpl::Recover(
const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only, const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
bool error_if_log_file_exist, bool error_if_data_exists_in_logs, bool error_if_wal_file_exists, bool error_if_data_exists_in_wals,
uint64_t* recovered_seq) { uint64_t* recovered_seq) {
mutex_.AssertHeld(); mutex_.AssertHeld();
@ -588,11 +588,11 @@ Status DBImpl::Recover(
} }
if (logs.size() > 0) { if (logs.size() > 0) {
if (error_if_log_file_exist) { if (error_if_wal_file_exists) {
return Status::Corruption( return Status::Corruption(
"The db was opened in readonly mode with error_if_log_file_exist" "The db was opened in readonly mode with error_if_wal_file_exists"
"flag but a log file already exists"); "flag but a WAL file already exists");
} else if (error_if_data_exists_in_logs) { } else if (error_if_data_exists_in_wals) {
for (auto& log : logs) { for (auto& log : logs) {
std::string fname = LogFileName(immutable_db_options_.wal_dir, log); std::string fname = LogFileName(immutable_db_options_.wal_dir, log);
uint64_t bytes; uint64_t bytes;
@ -600,8 +600,8 @@ Status DBImpl::Recover(
if (s.ok()) { if (s.ok()) {
if (bytes > 0) { if (bytes > 0) {
return Status::Corruption( return Status::Corruption(
"error_if_data_exists_in_logs is set but there are data " "error_if_data_exists_in_wals is set but there are data "
" in log files."); " in WAL files.");
} }
} }
} }

@ -151,7 +151,7 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
} // namespace } // namespace
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname, Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
DB** dbptr, bool /*error_if_log_file_exist*/) { DB** dbptr, bool /*error_if_wal_file_exists*/) {
// If dbname does not exist in the file system, should not do anything // If dbname does not exist in the file system, should not do anything
Status s = OpenForReadOnlyCheckExistence(options, dbname); Status s = OpenForReadOnlyCheckExistence(options, dbname);
if (!s.ok()) { if (!s.ok()) {
@ -188,7 +188,7 @@ Status DB::OpenForReadOnly(
const DBOptions& db_options, const std::string& dbname, const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
bool error_if_log_file_exist) { bool error_if_wal_file_exists) {
// If dbname does not exist in the file system, should not do anything // If dbname does not exist in the file system, should not do anything
Status s = OpenForReadOnlyCheckExistence(db_options, dbname); Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
if (!s.ok()) { if (!s.ok()) {
@ -197,14 +197,14 @@ Status DB::OpenForReadOnly(
return DBImplReadOnly::OpenForReadOnlyWithoutCheck( return DBImplReadOnly::OpenForReadOnlyWithoutCheck(
db_options, dbname, column_families, handles, dbptr, db_options, dbname, column_families, handles, dbptr,
error_if_log_file_exist); error_if_wal_file_exists);
} }
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck( Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
const DBOptions& db_options, const std::string& dbname, const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
bool error_if_log_file_exist) { bool error_if_wal_file_exists) {
*dbptr = nullptr; *dbptr = nullptr;
handles->clear(); handles->clear();
@ -212,7 +212,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
DBImplReadOnly* impl = new DBImplReadOnly(db_options, dbname); DBImplReadOnly* impl = new DBImplReadOnly(db_options, dbname);
impl->mutex_.Lock(); impl->mutex_.Lock();
Status s = impl->Recover(column_families, true /* read only */, Status s = impl->Recover(column_families, true /* read only */,
error_if_log_file_exist); error_if_wal_file_exists);
if (s.ok()) { if (s.ok()) {
// set column family handles // set column family handles
for (auto cf : column_families) { for (auto cf : column_families) {
@ -253,7 +253,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
Status DB::OpenForReadOnly(const Options& /*options*/, Status DB::OpenForReadOnly(const Options& /*options*/,
const std::string& /*dbname*/, DB** /*dbptr*/, const std::string& /*dbname*/, DB** /*dbptr*/,
bool /*error_if_log_file_exist*/) { bool /*error_if_wal_file_exists*/) {
return Status::NotSupported("Not supported in ROCKSDB_LITE."); return Status::NotSupported("Not supported in ROCKSDB_LITE.");
} }
@ -261,7 +261,7 @@ Status DB::OpenForReadOnly(
const DBOptions& /*db_options*/, const std::string& /*dbname*/, const DBOptions& /*db_options*/, const std::string& /*dbname*/,
const std::vector<ColumnFamilyDescriptor>& /*column_families*/, const std::vector<ColumnFamilyDescriptor>& /*column_families*/,
std::vector<ColumnFamilyHandle*>* /*handles*/, DB** /*dbptr*/, std::vector<ColumnFamilyHandle*>* /*handles*/, DB** /*dbptr*/,
bool /*error_if_log_file_exist*/) { bool /*error_if_wal_file_exists*/) {
return Status::NotSupported("Not supported in ROCKSDB_LITE."); return Status::NotSupported("Not supported in ROCKSDB_LITE.");
} }
#endif // !ROCKSDB_LITE #endif // !ROCKSDB_LITE

@ -138,7 +138,7 @@ class DBImplReadOnly : public DBImpl {
const DBOptions& db_options, const std::string& dbname, const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
bool error_if_log_file_exist = false); bool error_if_wal_file_exists = false);
friend class DB; friend class DB;
}; };
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

@ -28,8 +28,8 @@ DBImplSecondary::~DBImplSecondary() {}
Status DBImplSecondary::Recover( Status DBImplSecondary::Recover(
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
bool /*readonly*/, bool /*error_if_log_file_exist*/, bool /*readonly*/, bool /*error_if_wal_file_exists*/,
bool /*error_if_data_exists_in_logs*/, uint64_t*) { bool /*error_if_data_exists_in_wals*/, uint64_t*) {
mutex_.AssertHeld(); mutex_.AssertHeld();
JobContext job_context(0); JobContext job_context(0);

@ -77,8 +77,8 @@ class DBImplSecondary : public DBImpl {
// Recover by replaying MANIFEST and WAL. Also initialize manifest_reader_ // Recover by replaying MANIFEST and WAL. Also initialize manifest_reader_
// and log_readers_ to facilitate future operations. // and log_readers_ to facilitate future operations.
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families, Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
bool read_only, bool error_if_log_file_exist, bool read_only, bool error_if_wal_file_exists,
bool error_if_data_exists_in_logs, bool error_if_data_exists_in_wals,
uint64_t* = nullptr) override; uint64_t* = nullptr) override;
// Implementations of the DB interface // Implementations of the DB interface

@ -136,7 +136,7 @@ extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_with_ttl(
extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_for_read_only( extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_for_read_only(
const rocksdb_options_t* options, const char* name, const rocksdb_options_t* options, const char* name,
unsigned char error_if_log_file_exist, char** errptr); unsigned char error_if_wal_file_exists, char** errptr);
extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_as_secondary( extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_as_secondary(
const rocksdb_options_t* options, const char* name, const rocksdb_options_t* options, const char* name,
@ -232,7 +232,7 @@ rocksdb_open_for_read_only_column_families(
const char* const* column_family_names, const char* const* column_family_names,
const rocksdb_options_t* const* column_family_options, const rocksdb_options_t* const* column_family_options,
rocksdb_column_family_handle_t** column_family_handles, rocksdb_column_family_handle_t** column_family_handles,
unsigned char error_if_log_file_exist, char** errptr); unsigned char error_if_wal_file_exists, char** errptr);
extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_as_secondary_column_families( extern ROCKSDB_LIBRARY_API rocksdb_t* rocksdb_open_as_secondary_column_families(
const rocksdb_options_t* options, const char* name, const rocksdb_options_t* options, const char* name,

@ -157,7 +157,7 @@ class DB {
// return Status::NotSupported. // return Status::NotSupported.
static Status OpenForReadOnly(const Options& options, const std::string& name, static Status OpenForReadOnly(const Options& options, const std::string& name,
DB** dbptr, DB** dbptr,
bool error_if_log_file_exist = false); bool error_if_wal_file_exists = false);
// Open the database for read only with column families. When opening DB with // Open the database for read only with column families. When opening DB with
// read only, you can specify only a subset of column families in the // read only, you can specify only a subset of column families in the
@ -171,7 +171,7 @@ class DB {
const DBOptions& db_options, const std::string& name, const DBOptions& db_options, const std::string& name,
const std::vector<ColumnFamilyDescriptor>& column_families, const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
bool error_if_log_file_exist = false); bool error_if_wal_file_exists = false);
// The following OpenAsSecondary functions create a secondary instance that // The following OpenAsSecondary functions create a secondary instance that
// can dynamically tail the MANIFEST of a primary that must have already been // can dynamically tail the MANIFEST of a primary that must have already been

@ -72,15 +72,19 @@ jlong Java_org_rocksdb_RocksDB_open__JLjava_lang_String_2(
/* /*
* Class: org_rocksdb_RocksDB * Class: org_rocksdb_RocksDB
* Method: openROnly * Method: openROnly
* Signature: (JLjava/lang/String;)J * Signature: (JLjava/lang/String;Z)J
*/ */
jlong Java_org_rocksdb_RocksDB_openROnly__JLjava_lang_String_2( jlong Java_org_rocksdb_RocksDB_openROnly__JLjava_lang_String_2Z(
JNIEnv* env, jclass, jlong jopt_handle, jstring jdb_path) { JNIEnv* env, jclass, jlong jopt_handle, jstring jdb_path,
jboolean jerror_if_wal_file_exists) {
const bool error_if_wal_file_exists = jerror_if_wal_file_exists == JNI_TRUE;
return rocksdb_open_helper( return rocksdb_open_helper(
env, jopt_handle, jdb_path, env, jopt_handle, jdb_path,
[](const ROCKSDB_NAMESPACE::Options& options, const std::string& db_path, [error_if_wal_file_exists](const ROCKSDB_NAMESPACE::Options& options,
ROCKSDB_NAMESPACE::DB** db) { const std::string& db_path,
return ROCKSDB_NAMESPACE::DB::OpenForReadOnly(options, db_path, db); ROCKSDB_NAMESPACE::DB** db) {
return ROCKSDB_NAMESPACE::DB::OpenForReadOnly(options, db_path, db,
error_if_wal_file_exists);
}); });
} }
@ -172,21 +176,25 @@ jlongArray rocksdb_open_helper(
/* /*
* Class: org_rocksdb_RocksDB * Class: org_rocksdb_RocksDB
* Method: openROnly * Method: openROnly
* Signature: (JLjava/lang/String;[[B[J)[J * Signature: (JLjava/lang/String;[[B[JZ)[J
*/ */
jlongArray Java_org_rocksdb_RocksDB_openROnly__JLjava_lang_String_2_3_3B_3J( jlongArray Java_org_rocksdb_RocksDB_openROnly__JLjava_lang_String_2_3_3B_3JZ(
JNIEnv* env, jclass, jlong jopt_handle, jstring jdb_path, JNIEnv* env, jclass, jlong jopt_handle, jstring jdb_path,
jobjectArray jcolumn_names, jlongArray jcolumn_options) { jobjectArray jcolumn_names, jlongArray jcolumn_options,
jboolean jerror_if_wal_file_exists) {
const bool error_if_wal_file_exists = jerror_if_wal_file_exists == JNI_TRUE;
return rocksdb_open_helper( return rocksdb_open_helper(
env, jopt_handle, jdb_path, jcolumn_names, jcolumn_options, env, jopt_handle, jdb_path, jcolumn_names, jcolumn_options,
[](const ROCKSDB_NAMESPACE::DBOptions& options, [error_if_wal_file_exists](
const std::string& db_path, const ROCKSDB_NAMESPACE::DBOptions& options,
const std::vector<ROCKSDB_NAMESPACE::ColumnFamilyDescriptor>& const std::string& db_path,
column_families, const std::vector<ROCKSDB_NAMESPACE::ColumnFamilyDescriptor>&
std::vector<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>* handles, column_families,
ROCKSDB_NAMESPACE::DB** db) { std::vector<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>* handles,
ROCKSDB_NAMESPACE::DB** db) {
return ROCKSDB_NAMESPACE::DB::OpenForReadOnly( return ROCKSDB_NAMESPACE::DB::OpenForReadOnly(
options, db_path, column_families, handles, db); options, db_path, column_families, handles, db,
error_if_wal_file_exists);
}); });
} }

@ -329,10 +329,61 @@ public class RocksDB extends RocksObject {
throws RocksDBException { throws RocksDBException {
// This allows to use the rocksjni default Options instead of // This allows to use the rocksjni default Options instead of
// the c++ one. // the c++ one.
Options options = new Options(); final Options options = new Options();
return openReadOnly(options, path); return openReadOnly(options, path);
} }
/**
* The factory constructor of RocksDB that opens a RocksDB instance in
* Read-Only mode given the path to the database using the specified
* options and db path.
*
* Options instance *should* not be disposed before all DBs using this options
* instance have been closed. If user doesn't call options dispose explicitly,
* then this options instance will be GC'd automatically.
*
* @param options {@link Options} instance.
* @param path the path to the RocksDB.
* @return a {@link RocksDB} instance on success, null if the specified
* {@link RocksDB} can not be opened.
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public static RocksDB openReadOnly(final Options options, final String path)
throws RocksDBException {
return openReadOnly(options, path, false);
}
/**
* The factory constructor of RocksDB that opens a RocksDB instance in
* Read-Only mode given the path to the database using the specified
* options and db path.
*
* Options instance *should* not be disposed before all DBs using this options
* instance have been closed. If user doesn't call options dispose explicitly,
* then this options instance will be GC'd automatically.
*
* @param options {@link Options} instance.
* @param path the path to the RocksDB.
* @param errorIfWalFileExists true to raise an error when opening the db
* if a Write Ahead Log file exists, false otherwise.
* @return a {@link RocksDB} instance on success, null if the specified
* {@link RocksDB} can not be opened.
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public static RocksDB openReadOnly(final Options options, final String path,
final boolean errorIfWalFileExists) throws RocksDBException {
// when non-default Options is used, keeping an Options reference
// in RocksDB can prevent Java to GC during the life-time of
// the currently-created RocksDB.
final RocksDB db = new RocksDB(openROnly(options.nativeHandle_, path, errorIfWalFileExists));
db.storeOptionsInstance(options);
return db;
}
/** /**
* The factory constructor of RocksDB that opens a RocksDB instance in * The factory constructor of RocksDB that opens a RocksDB instance in
* Read-Only mode given the path to the database using the default * Read-Only mode given the path to the database using the default
@ -355,8 +406,7 @@ public class RocksDB extends RocksObject {
// This allows to use the rocksjni default Options instead of // This allows to use the rocksjni default Options instead of
// the c++ one. // the c++ one.
final DBOptions options = new DBOptions(); final DBOptions options = new DBOptions();
return openReadOnly(options, path, columnFamilyDescriptors, return openReadOnly(options, path, columnFamilyDescriptors, columnFamilyHandles, false);
columnFamilyHandles);
} }
/** /**
@ -364,26 +414,27 @@ public class RocksDB extends RocksObject {
* Read-Only mode given the path to the database using the specified * Read-Only mode given the path to the database using the specified
* options and db path. * options and db path.
* *
* Options instance *should* not be disposed before all DBs using this options * <p>This open method allows to open RocksDB using a subset of available
* instance have been closed. If user doesn't call options dispose explicitly, * column families</p>
* then this options instance will be GC'd automatically. * <p>Options instance *should* not be disposed before all DBs using this
* options instance have been closed. If user doesn't call options dispose
* explicitly,then this options instance will be GC'd automatically.</p>
* *
* @param options {@link Options} instance. * @param options {@link DBOptions} instance.
* @param path the path to the RocksDB. * @param path the path to the RocksDB.
* @param columnFamilyDescriptors list of column family descriptors
* @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
* on open.
* @return a {@link RocksDB} instance on success, null if the specified * @return a {@link RocksDB} instance on success, null if the specified
* {@link RocksDB} can not be opened. * {@link RocksDB} can not be opened.
* *
* @throws RocksDBException thrown if error happens in underlying * @throws RocksDBException thrown if error happens in underlying
* native library. * native library.
*/ */
public static RocksDB openReadOnly(final Options options, final String path) public static RocksDB openReadOnly(final DBOptions options, final String path,
throws RocksDBException { final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
// when non-default Options is used, keeping an Options reference final List<ColumnFamilyHandle> columnFamilyHandles) throws RocksDBException {
// in RocksDB can prevent Java to GC during the life-time of return openReadOnly(options, path, columnFamilyDescriptors, columnFamilyHandles, false);
// the currently-created RocksDB.
final RocksDB db = new RocksDB(openROnly(options.nativeHandle_, path));
db.storeOptionsInstance(options);
return db;
} }
/** /**
@ -402,6 +453,8 @@ public class RocksDB extends RocksObject {
* @param columnFamilyDescriptors list of column family descriptors * @param columnFamilyDescriptors list of column family descriptors
* @param columnFamilyHandles will be filled with ColumnFamilyHandle instances * @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
* on open. * on open.
* @param errorIfWalFileExists true to raise an error when opening the db
* if a Write Ahead Log file exists, false otherwise.
* @return a {@link RocksDB} instance on success, null if the specified * @return a {@link RocksDB} instance on success, null if the specified
* {@link RocksDB} can not be opened. * {@link RocksDB} can not be opened.
* *
@ -410,7 +463,7 @@ public class RocksDB extends RocksObject {
*/ */
public static RocksDB openReadOnly(final DBOptions options, final String path, public static RocksDB openReadOnly(final DBOptions options, final String path,
final List<ColumnFamilyDescriptor> columnFamilyDescriptors, final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
final List<ColumnFamilyHandle> columnFamilyHandles) final List<ColumnFamilyHandle> columnFamilyHandles, final boolean errorIfWalFileExists)
throws RocksDBException { throws RocksDBException {
// when non-default Options is used, keeping an Options reference // when non-default Options is used, keeping an Options reference
// in RocksDB can prevent Java to GC during the life-time of // in RocksDB can prevent Java to GC during the life-time of
@ -425,8 +478,8 @@ public class RocksDB extends RocksObject {
cfOptionHandles[i] = cfDescriptor.getOptions().nativeHandle_; cfOptionHandles[i] = cfDescriptor.getOptions().nativeHandle_;
} }
final long[] handles = openROnly(options.nativeHandle_, path, cfNames, final long[] handles =
cfOptionHandles); openROnly(options.nativeHandle_, path, cfNames, cfOptionHandles, errorIfWalFileExists);
final RocksDB db = new RocksDB(handles[0]); final RocksDB db = new RocksDB(handles[0]);
db.storeOptionsInstance(options); db.storeOptionsInstance(options);
@ -4381,8 +4434,8 @@ public class RocksDB extends RocksObject {
final String path, final byte[][] columnFamilyNames, final String path, final byte[][] columnFamilyNames,
final long[] columnFamilyOptions) throws RocksDBException; final long[] columnFamilyOptions) throws RocksDBException;
private native static long openROnly(final long optionsHandle, private native static long openROnly(final long optionsHandle, final String path,
final String path) throws RocksDBException; final boolean errorIfWalFileExists) throws RocksDBException;
/** /**
* @param optionsHandle Native handle pointing to an Options object * @param optionsHandle Native handle pointing to an Options object
@ -4396,10 +4449,9 @@ public class RocksDB extends RocksObject {
* *
* @throws RocksDBException thrown if the database could not be opened * @throws RocksDBException thrown if the database could not be opened
*/ */
private native static long[] openROnly(final long optionsHandle, private native static long[] openROnly(final long optionsHandle, final String path,
final String path, final byte[][] columnFamilyNames, final byte[][] columnFamilyNames, final long[] columnFamilyOptions,
final long[] columnFamilyOptions final boolean errorIfWalFileExists) throws RocksDBException;
) throws RocksDBException;
private native static long openAsSecondary(final long optionsHandle, final String path, private native static long openAsSecondary(final long optionsHandle, final String path,
final String secondaryPath) throws RocksDBException; final String secondaryPath) throws RocksDBException;

@ -302,4 +302,31 @@ public class ReadOnlyTest {
} }
} }
} }
@Test(expected = RocksDBException.class)
public void errorIfWalFileExists() throws RocksDBException {
try (final Options options = new Options().setCreateIfMissing(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
// no-op
}
try (final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()) {
final List<ColumnFamilyDescriptor> cfDescriptors =
Arrays.asList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts));
final List<ColumnFamilyHandle> readOnlyColumnFamilyHandleList = new ArrayList<>();
try (final DBOptions options = new DBOptions();
final RocksDB rDb = RocksDB.openReadOnly(options, dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, readOnlyColumnFamilyHandleList, true);) {
try {
// no-op... should have raised an error as errorIfWalFileExists=true
} finally {
for (final ColumnFamilyHandle columnFamilyHandle : readOnlyColumnFamilyHandleList) {
columnFamilyHandle.close();
}
}
}
}
}
} }

Loading…
Cancel
Save