2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// 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).
|
2015-01-31 19:43:09 +00:00
|
|
|
//
|
|
|
|
// This file implements the "bridge" between Java and C++ and enables
|
2020-02-20 20:07:53 +00:00
|
|
|
// calling c++ ROCKSDB_NAMESPACE::WriteBatch methods testing from Java side.
|
2022-10-25 16:26:51 +00:00
|
|
|
#include "rocksdb/write_batch.h"
|
|
|
|
|
2015-01-31 19:43:09 +00:00
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include "db/memtable.h"
|
|
|
|
#include "db/write_batch_internal.h"
|
|
|
|
#include "include/org_rocksdb_WriteBatch.h"
|
|
|
|
#include "include/org_rocksdb_WriteBatchTest.h"
|
|
|
|
#include "include/org_rocksdb_WriteBatchTestInternalHelper.h"
|
2016-06-21 01:01:03 +00:00
|
|
|
#include "include/org_rocksdb_WriteBatch_Handler.h"
|
2017-04-06 02:02:00 +00:00
|
|
|
#include "options/cf_options.h"
|
2015-01-31 19:43:09 +00:00
|
|
|
#include "rocksdb/db.h"
|
|
|
|
#include "rocksdb/env.h"
|
|
|
|
#include "rocksdb/memtablerep.h"
|
|
|
|
#include "rocksdb/status.h"
|
2016-06-21 01:01:03 +00:00
|
|
|
#include "rocksdb/write_buffer_manager.h"
|
2015-01-31 19:43:09 +00:00
|
|
|
#include "rocksjni/portal.h"
|
2019-05-30 18:21:38 +00:00
|
|
|
#include "test_util/testharness.h"
|
2019-05-31 00:39:43 +00:00
|
|
|
#include "util/string_util.h"
|
2015-01-31 19:43:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Class: org_rocksdb_WriteBatchTest
|
|
|
|
* Method: getContents
|
2016-02-01 20:00:40 +00:00
|
|
|
* Signature: (J)[B
|
2015-01-31 19:43:09 +00:00
|
|
|
*/
|
2018-04-13 00:55:14 +00:00
|
|
|
jbyteArray Java_org_rocksdb_WriteBatchTest_getContents(JNIEnv* env,
|
|
|
|
jclass /*jclazz*/,
|
|
|
|
jlong jwb_handle) {
|
2020-02-20 20:07:53 +00:00
|
|
|
auto* b = reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatch*>(jwb_handle);
|
2015-01-31 19:43:09 +00:00
|
|
|
assert(b != nullptr);
|
|
|
|
|
|
|
|
// todo: Currently the following code is directly copied from
|
|
|
|
// db/write_bench_test.cc. It could be implemented in java once
|
|
|
|
// all the necessary components can be accessed via jni api.
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::InternalKeyComparator cmp(
|
|
|
|
ROCKSDB_NAMESPACE::BytewiseComparator());
|
|
|
|
auto factory = std::make_shared<ROCKSDB_NAMESPACE::SkipListFactory>();
|
|
|
|
ROCKSDB_NAMESPACE::Options options;
|
|
|
|
ROCKSDB_NAMESPACE::WriteBufferManager wb(options.db_write_buffer_size);
|
2015-01-31 19:43:09 +00:00
|
|
|
options.memtable_factory = factory;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::MemTable* mem = new ROCKSDB_NAMESPACE::MemTable(
|
2021-05-05 20:59:21 +00:00
|
|
|
cmp, ROCKSDB_NAMESPACE::ImmutableOptions(options),
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::MutableCFOptions(options), &wb,
|
|
|
|
ROCKSDB_NAMESPACE::kMaxSequenceNumber, 0 /* column_family_id */);
|
2015-01-31 19:43:09 +00:00
|
|
|
mem->Ref();
|
|
|
|
std::string state;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::ColumnFamilyMemTablesDefault cf_mems_default(mem);
|
|
|
|
ROCKSDB_NAMESPACE::Status s =
|
|
|
|
ROCKSDB_NAMESPACE::WriteBatchInternal::InsertInto(b, &cf_mems_default,
|
|
|
|
nullptr, nullptr);
|
2019-09-09 18:22:28 +00:00
|
|
|
unsigned int count = 0;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::Arena arena;
|
2024-03-22 20:40:42 +00:00
|
|
|
ROCKSDB_NAMESPACE::ScopedArenaPtr<ROCKSDB_NAMESPACE::InternalIterator> iter(
|
Support returning write unix time in iterator property (#12428)
Summary:
This PR adds support to return data's approximate unix write time in the iterator property API. The general implementation is:
1) If the entry comes from a SST file, the sequence number to time mapping recorded in that file's table properties will be used to deduce the entry's write time from its sequence number. If no such recording is available, `std::numeric_limits<uint64_t>::max()` is returned to indicate the write time is unknown except if the entry's sequence number is zero, in which case, 0 is returned. This also means that even if `preclude_last_level_data_seconds` and `preserve_internal_time_seconds` can be toggled off between DB reopens, as long as the SST file's table property has the mapping available, the entry's write time can be deduced and returned.
2) If the entry comes from memtable, we will use the DB's sequence number to write time mapping to do similar things. A copy of the DB's seqno to write time mapping is kept in SuperVersion to allow iterators to have lock free access. This also means a new `SuperVersion` is installed each time DB's seqno to time mapping updates, which is originally proposed by Peter in https://github.com/facebook/rocksdb/issues/11928 . Similarly, if the feature is not enabled, `std::numeric_limits<uint64_t>::max()` is returned to indicate the write time is unknown.
Needed follow up:
1) The write time for `kTypeValuePreferredSeqno` should be special cased, where it's already specified by the user, so we can directly return it.
2) Flush job can be updated to use DB's seqno to time mapping copy in the SuperVersion.
3) Handle the case when `TimedPut` is called with a write time that is `std::numeric_limits<uint64_t>::max()`. We can make it a regular `Put`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12428
Test Plan: Added unit test
Reviewed By: pdillinger
Differential Revision: D54967067
Pulled By: jowlyzhang
fbshipit-source-id: c795b1b7ec142e09e53f2ed3461cf719833cb37a
2024-03-15 22:37:37 +00:00
|
|
|
mem->NewIterator(ROCKSDB_NAMESPACE::ReadOptions(),
|
|
|
|
/*seqno_to_time_mapping=*/nullptr, &arena));
|
2015-01-31 19:43:09 +00:00
|
|
|
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::ParsedInternalKey ikey;
|
2017-07-24 18:28:20 +00:00
|
|
|
ikey.clear();
|
2020-10-28 17:11:13 +00:00
|
|
|
ROCKSDB_NAMESPACE::Status pik_status = ROCKSDB_NAMESPACE::ParseInternalKey(
|
|
|
|
iter->key(), &ikey, true /* log_err_key */);
|
|
|
|
pik_status.PermitUncheckedError();
|
|
|
|
assert(pik_status.ok());
|
2015-01-31 19:43:09 +00:00
|
|
|
switch (ikey.type) {
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeValue:
|
2015-01-31 19:43:09 +00:00
|
|
|
state.append("Put(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(", ");
|
|
|
|
state.append(iter->value().ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeMerge:
|
2015-01-31 19:43:09 +00:00
|
|
|
state.append("Merge(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(", ");
|
|
|
|
state.append(iter->value().ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeDeletion:
|
2015-01-31 19:43:09 +00:00
|
|
|
state.append("Delete(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeSingleDeletion:
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append("SingleDelete(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeRangeDeletion:
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append("DeleteRange(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(", ");
|
|
|
|
state.append(iter->value().ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2020-02-20 20:07:53 +00:00
|
|
|
case ROCKSDB_NAMESPACE::kTypeLogData:
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append("LogData(");
|
|
|
|
state.append(ikey.user_key.ToString());
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
|
|
|
break;
|
2015-01-31 19:43:09 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append("Err:Expected(");
|
|
|
|
state.append(std::to_string(ikey.type));
|
|
|
|
state.append(")");
|
|
|
|
count++;
|
2015-01-31 19:43:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
state.append("@");
|
2022-05-06 20:03:58 +00:00
|
|
|
state.append(std::to_string(ikey.sequence));
|
2015-01-31 19:43:09 +00:00
|
|
|
}
|
|
|
|
if (!s.ok()) {
|
|
|
|
state.append(s.ToString());
|
2020-02-20 20:07:53 +00:00
|
|
|
} else if (ROCKSDB_NAMESPACE::WriteBatchInternal::Count(b) != count) {
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append("Err:CountMismatch(expected=");
|
2020-02-20 20:07:53 +00:00
|
|
|
state.append(
|
|
|
|
std::to_string(ROCKSDB_NAMESPACE::WriteBatchInternal::Count(b)));
|
2018-03-02 23:33:08 +00:00
|
|
|
state.append(", actual=");
|
|
|
|
state.append(std::to_string(count));
|
|
|
|
state.append(")");
|
2015-01-31 19:43:09 +00:00
|
|
|
}
|
|
|
|
delete mem->Unref();
|
|
|
|
|
|
|
|
jbyteArray jstate = env->NewByteArray(static_cast<jsize>(state.size()));
|
2018-04-13 00:55:14 +00:00
|
|
|
if (jstate == nullptr) {
|
2017-02-28 00:26:12 +00:00
|
|
|
// exception thrown: OutOfMemoryError
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-04-13 00:55:14 +00:00
|
|
|
env->SetByteArrayRegion(
|
|
|
|
jstate, 0, static_cast<jsize>(state.size()),
|
|
|
|
const_cast<jbyte*>(reinterpret_cast<const jbyte*>(state.c_str())));
|
|
|
|
if (env->ExceptionCheck()) {
|
2017-02-28 00:26:12 +00:00
|
|
|
// exception thrown: ArrayIndexOutOfBoundsException
|
|
|
|
env->DeleteLocalRef(jstate);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-01-31 19:43:09 +00:00
|
|
|
|
|
|
|
return jstate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Class: org_rocksdb_WriteBatchTestInternalHelper
|
|
|
|
* Method: setSequence
|
2016-02-01 20:00:40 +00:00
|
|
|
* Signature: (JJ)V
|
2015-01-31 19:43:09 +00:00
|
|
|
*/
|
|
|
|
void Java_org_rocksdb_WriteBatchTestInternalHelper_setSequence(
|
2018-04-13 00:55:14 +00:00
|
|
|
JNIEnv* /*env*/, jclass /*jclazz*/, jlong jwb_handle, jlong jsn) {
|
2020-02-20 20:07:53 +00:00
|
|
|
auto* wb = reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatch*>(jwb_handle);
|
2015-01-31 19:43:09 +00:00
|
|
|
assert(wb != nullptr);
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::WriteBatchInternal::SetSequence(
|
|
|
|
wb, static_cast<ROCKSDB_NAMESPACE::SequenceNumber>(jsn));
|
2015-01-31 19:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Class: org_rocksdb_WriteBatchTestInternalHelper
|
|
|
|
* Method: sequence
|
2016-02-01 20:00:40 +00:00
|
|
|
* Signature: (J)J
|
2015-01-31 19:43:09 +00:00
|
|
|
*/
|
2018-04-13 00:55:14 +00:00
|
|
|
jlong Java_org_rocksdb_WriteBatchTestInternalHelper_sequence(JNIEnv* /*env*/,
|
|
|
|
jclass /*jclazz*/,
|
|
|
|
jlong jwb_handle) {
|
2020-02-20 20:07:53 +00:00
|
|
|
auto* wb = reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatch*>(jwb_handle);
|
2015-01-31 19:43:09 +00:00
|
|
|
assert(wb != nullptr);
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
return static_cast<jlong>(
|
|
|
|
ROCKSDB_NAMESPACE::WriteBatchInternal::Sequence(wb));
|
2015-01-31 19:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Class: org_rocksdb_WriteBatchTestInternalHelper
|
|
|
|
* Method: append
|
2016-02-01 20:00:40 +00:00
|
|
|
* Signature: (JJ)V
|
2015-01-31 19:43:09 +00:00
|
|
|
*/
|
2018-04-13 00:55:14 +00:00
|
|
|
void Java_org_rocksdb_WriteBatchTestInternalHelper_append(JNIEnv* /*env*/,
|
|
|
|
jclass /*jclazz*/,
|
|
|
|
jlong jwb_handle_1,
|
|
|
|
jlong jwb_handle_2) {
|
2020-02-20 20:07:53 +00:00
|
|
|
auto* wb1 = reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatch*>(jwb_handle_1);
|
2015-01-31 19:43:09 +00:00
|
|
|
assert(wb1 != nullptr);
|
2020-02-20 20:07:53 +00:00
|
|
|
auto* wb2 = reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatch*>(jwb_handle_2);
|
2015-01-31 19:43:09 +00:00
|
|
|
assert(wb2 != nullptr);
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::WriteBatchInternal::Append(wb1, wb2);
|
2015-01-31 19:43:09 +00:00
|
|
|
}
|