Compare commits

..
9 Commits
Author SHA1 Message Date
Peter Dillinger fcd07e0f78 Fix a nuisance compiler warning from clang (#12144)
Summary:
Example:

```
cache/clock_cache.cc:56:7: error: fallthrough annotation in unreachable code [-Werror,-Wimplicit-fallthrough]
      FALLTHROUGH_INTENDED;
      ^
./port/lang.h:10:30: note: expanded from macro 'FALLTHROUGH_INTENDED'
                             ^
```

In clang < 14, this is annoyingly generated from -Wimplicit-fallthrough, but was changed to -Wunreachable-code-fallthrough (implied by -Wunreachable-code) in clang 14. See https://reviews.llvm.org/D107933 for how this nuisance pattern generated false positives similar to ours in the Linux kernel.

Just to underscore the ridiculousness of this warning, here an error is reported on the annotation, not the call to do_something(), depending on the constexpr value (https://godbolt.org/z/EvxqdPTdr):

```
#include <atomic>
void do_something();
void test(int v) {
    switch (v) {
        case 1:
            if constexpr (std::atomic<long>::is_always_lock_free) {
                return;
            } else {
                do_something();
                [[fallthrough]];
            }
        case 2:
            return;
    }
}
```

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

Test Plan: Added the warning to our Makefile for USE_CLANG, which reproduced the warning-as-error as shown above, but is now fixed.

Reviewed By: jaykorean

Differential Revision: D52139615

Pulled By: pdillinger

fbshipit-source-id: ba967ae700c0916d1a478bc465cf917633e337d9
2023-12-14 08:23:00 -08:00
anand76 0efb576ca3 Update version.h and HISTORY to 8.9.2 2023-12-12 11:47:43 -08:00
anand76 53649ce36b Sanitize the secondary_cache option in TieredCacheOptions (#12137)
Summary:
Sanitize the `secondary_cache` field in the `cache_opts` option of `TieredCacheOptions` to `nullptr` if set by the user. The nvm secondary cache should be directly set in `TieredCacheOptions`.

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

Reviewed By: akankshamahajan15

Differential Revision: D52063817

Pulled By: anand1976

fbshipit-source-id: 255116c665a9b908c8f44109a2d331d4b73e7591
2023-12-12 11:42:46 -08:00
anand76 6b389b43ff Fix false negative in TieredSecondaryCache nvm cache lookup (#12134)
Summary:
There is a bug in the `TieredSecondaryCache` that can result in a false negative. This can happen when a MultiGet does a cache lookup that gets a hit in the `TieredSecondaryCache` local nvm cache tier, and the result is available before MultiGet calls `WaitAll` (i.e the nvm cache `SecondaryCacheResultHandle` `IsReady` returns true).

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

Test Plan: Add a new unit test in tiered_secondary_cache_test

Reviewed By: akankshamahajan15

Differential Revision: D52023309

Pulled By: anand1976

fbshipit-source-id: e5ae681226a0f12753fecb2f6acc7e5f254ae72b
2023-12-12 11:42:05 -08:00
Levi Tamasi 49ce8a1064 Update version.h and HISTORY.md for 8.9.1 2023-12-08 12:44:09 -08:00
Levi Tamasi b8d41c55e7 Turn the default Timer in PeriodicTaskScheduler into a leaky Meyers singleton (#12128)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12128

The patch turns the `Timer` Meyers singleton in `PeriodicTaskScheduler::Default()` into one of the leaky variety in order to prevent static destruction order issues.

Reviewed By: akankshamahajan15

Differential Revision: D51963950

fbshipit-source-id: 0fc34113ad03c51fdc83bdb8c2cfb6c9f6913948
2023-12-08 12:38:07 -08:00
akankshamahajan 3b1ce121c2 Update version.h to correct version 8.9
Summary:

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:
2023-11-30 11:19:11 -08:00
Jay Huh 17e3628cb5 Merge memtable only UDT bugfix 2023-11-21 16:35:45 -08:00
Jay Huh 8bcbed2e93 Update HISTORY.md and version.h for 8.9.fb release 2023-11-21 16:33:46 -08:00
217 changed files with 2658 additions and 6977 deletions

No files matched your search

+2 -2
View File
@@ -914,6 +914,8 @@ workflows:
- build-linux-mini-crashtest
jobs-windows:
jobs:
- build-windows-vs2022-avx2
- build-windows-vs2022
- build-windows-vs2019
- build-cmake-mingw
jobs-java:
@@ -962,5 +964,3 @@ workflows:
- build-linux-non-shm
- build-linux-clang-13-asan-ubsan-with-folly
- build-linux-valgrind
- build-windows-vs2022-avx2
- build-windows-vs2022
+5 -41
View File
@@ -1,50 +1,14 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 8.10.2 (02/16/2024)
* Update zlib to 1.3.1 for Java builds
## 8.10.1 (01/16/2024)
## 8.9.2 (12/12/2023)
### Bug Fixes
* Fix bug in auto_readahead_size that combined with IndexType::kBinarySearchWithFirstKey + fails or iterator lands at a wrong key
## 8.10.0 (12/15/2023)
### New Features
* Provide support for async_io to trim readahead_size by doing block cache lookup
* Added initial wide-column support in `WriteBatchWithIndex`. This includes the `PutEntity` API and support for wide columns in the existing read APIs (`GetFromBatch`, `GetFromBatchAndDB`, `MultiGetFromBatchAndDB`, and `BaseDeltaIterator`).
### Public API Changes
* Custom implementations of `TablePropertiesCollectorFactory` may now return a `nullptr` collector to decline processing a file, reducing callback overheads in such cases.
### Behavior Changes
* Make ReadOptions.auto_readahead_size default true which does prefetching optimizations for forward scans if iterate_upper_bound and block_cache is also specified.
* Compactions can be scheduled in parallel in an additional scenario: high compaction debt relative to the data size
* HyperClockCache now has built-in protection against excessive CPU consumption under the extreme stress condition of no (or very few) evictable cache entries, which can slightly increase memory usage such conditions. New option `HyperClockCacheOptions::eviction_effort_cap` controls the space-time trade-off of the response. The default should be generally well-balanced, with no measurable affect on normal operation.
### Bug Fixes
* Fix a corner case with auto_readahead_size where Prev Operation returns NOT SUPPORTED error when scans direction is changed from forward to backward.
* Avoid destroying the periodic task scheduler's default timer in order to prevent static destruction order issues.
* Fix double counting of BYTES_WRITTEN ticker when doing writes with transactions.
* Fix a WRITE_STALL counter that was reporting wrong value in few cases.
* A lookup by MultiGet in a TieredCache that goes to the local flash cache and finishes with very low latency, i.e before the subsequent call to WaitAll, is ignored, resulting in a false negative and a memory leak.
* Sanitize the cache_opts->secondary_cache field in TieredCacheOptions. It can be mistakenly set by the user, which would conflict with nvm_sec_cache in TieredCacheOptions if not sanitized to nullptr.
### Performance Improvements
* Java API extensions to improve consistency and completeness of APIs
1 Extended `RocksDB.get([ColumnFamilyHandle columnFamilyHandle,] ReadOptions opt, ByteBuffer key, ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
2 Extended `RocksDB.put( [ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
3 Added `RocksDB.merge([ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOptions, ByteBuffer key, ByteBuffer value)` methods with the same parameter options as `put(...)` - direct and indirect buffers are supported
4 Added `RocksIterator.key( byte[] key [, int offset, int len])` methods which retrieve the iterator key into the supplied buffer
5 Added `RocksIterator.value( byte[] value [, int offset, int len])` methods which retrieve the iterator value into the supplied buffer
6 Deprecated `get(final ColumnFamilyHandle columnFamilyHandle, final ReadOptions readOptions, byte[])` in favour of `get(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, byte[])` which has consistent parameter ordering with other methods in the same class
7 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value)` methods which retrieve the requested value into the supplied buffer
8 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which retrieve the requested value into the supplied buffer
9 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
10 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
11 Added `Transaction.getIterator()` method as a convenience which defaults the `ReadOptions` value supplied to existing `Transaction.iterator()` methods. This mirrors the existing `RocksDB.iterator()` method.
12 Added `Transaction.put([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written in a `ByteBuffer` parameter
13 Added `Transaction.merge([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
14 Added `Transaction.mergeUntracked([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
## 8.9.1 (12/8/2023)
### Bug Fixes
* Avoid destroying the periodic task scheduler's default timer in order to prevent static destruction order issues.
## 8.9.0 (11/17/2023)
### New Features
+4 -8
View File
@@ -2060,7 +2060,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
ifeq ($(PLATFORM), OS_SOLARIS)
ARCH := $(shell isainfo -b)
else ifeq ($(PLATFORM), OS_OPENBSD)
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
ARCH := 64
else
ARCH := 32
@@ -2081,7 +2081,7 @@ ifneq ($(origin JNI_LIBC), undefined)
endif
ifeq (,$(ROCKSDBJNILIB))
ifneq (,$(filter ppc% s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
else
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
@@ -2094,8 +2094,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
SHA256_CMD = sha256sum
ZLIB_VER ?= 1.3.1
ZLIB_SHA256 ?= 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
ZLIB_VER ?= 1.3
ZLIB_SHA256 ?= ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
@@ -2346,10 +2346,6 @@ rocksdbjavastaticdockers390x:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
rocksdbjavastaticdockerriscv64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
rocksdbjavastaticdockerx86musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
+4 -5
View File
@@ -647,10 +647,8 @@ if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
fi
COMMON_FLAGS="$COMMON_FLAGS"
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
RISC_ISA=$(cat /proc/cpuinfo | grep -E '^isa\s*:' | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
if [ -n "${RISCV_ISA}" ]; then
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
fi
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
elif [ "$TARGET_OS" == "IOS" ]; then
COMMON_FLAGS="$COMMON_FLAGS"
else
@@ -662,7 +660,8 @@ else
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
COMMON_FLAGS="$COMMON_FLAGS -march=rv64gc"
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
elif test "$USE_SSE"; then
# USE_SSE is DEPRECATED
# This is a rough approximation of the old USE_SSE behavior
-5
View File
@@ -48,10 +48,6 @@ DEFINE_uint64(cache_size, 1 * GiB,
"Number of bytes to use as a cache of uncompressed data.");
DEFINE_int32(num_shard_bits, -1,
"ShardedCacheOptions::shard_bits. Default = auto");
DEFINE_int32(
eviction_effort_cap,
ROCKSDB_NAMESPACE::HyperClockCacheOptions(1, 1).eviction_effort_cap,
"HyperClockCacheOptions::eviction_effort_cap");
DEFINE_double(resident_ratio, 0.25,
"Ratio of keys fitting in cache to keyspace.");
@@ -395,7 +391,6 @@ class CacheBench {
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
opts.memory_allocator = allocator;
opts.eviction_effort_cap = FLAGS_eviction_effort_cap;
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
FLAGS_cache_type == "hyper_clock_cache") {
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
+41 -94
View File
@@ -15,7 +15,6 @@
#include <cassert>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <functional>
#include <numeric>
@@ -95,8 +94,7 @@ inline void Unref(const ClockHandle& h, uint64_t count = 1) {
(void)old_meta;
}
inline bool ClockUpdate(ClockHandle& h, BaseClockTable::EvictionData* data,
bool* purgeable = nullptr) {
inline bool ClockUpdate(ClockHandle& h, bool* purgeable = nullptr) {
uint64_t meta;
if (purgeable) {
assert(*purgeable == false);
@@ -128,7 +126,6 @@ inline bool ClockUpdate(ClockHandle& h, BaseClockTable::EvictionData* data,
(meta >> ClockHandle::kReleaseCounterShift) & ClockHandle::kCounterMask;
if (acquire_count != release_count) {
// Only clock update entries with no outstanding refs
data->seen_pinned_count++;
return false;
}
if ((meta >> ClockHandle::kStateShift == ClockHandle::kStateVisible) &&
@@ -152,8 +149,6 @@ inline bool ClockUpdate(ClockHandle& h, BaseClockTable::EvictionData* data,
<< ClockHandle::kStateShift) |
(meta & ClockHandle::kHitBitMask))) {
// Took ownership.
data->freed_charge += h.GetTotalCharge();
data->freed_count += 1;
return true;
} else {
// Compare-exchange failing probably
@@ -361,18 +356,6 @@ void ConstApplyToEntriesRange(const Func& func, const HandleImpl* begin,
}
}
constexpr uint32_t kStrictCapacityLimitBit = 1u << 31;
uint32_t SanitizeEncodeEecAndScl(int eviction_effort_cap,
bool strict_capacit_limit) {
eviction_effort_cap = std::max(int{1}, eviction_effort_cap);
eviction_effort_cap =
std::min(static_cast<int>(~kStrictCapacityLimitBit), eviction_effort_cap);
uint32_t eec_and_scl = static_cast<uint32_t>(eviction_effort_cap);
eec_and_scl |= strict_capacit_limit ? kStrictCapacityLimitBit : 0;
return eec_and_scl;
}
} // namespace
void ClockHandleBasicData::FreeData(MemoryAllocator* allocator) const {
@@ -402,20 +385,17 @@ HandleImpl* BaseClockTable::StandaloneInsert(
template <class Table>
typename Table::HandleImpl* BaseClockTable::CreateStandalone(
ClockHandleBasicData& proto, size_t capacity, uint32_t eec_and_scl,
ClockHandleBasicData& proto, size_t capacity, bool strict_capacity_limit,
bool allow_uncharged) {
Table& derived = static_cast<Table&>(*this);
typename Table::InsertState state;
derived.StartInsert(state);
const size_t total_charge = proto.GetTotalCharge();
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl & kStrictCapacityLimitBit) {
if (strict_capacity_limit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge, capacity,
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
/*need_evict_for_occupancy=*/false, state);
if (!s.ok()) {
if (allow_uncharged) {
proto.total_charge = 0;
@@ -427,7 +407,7 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge, capacity,
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
/*need_evict_for_occupancy=*/false, state);
if (!success) {
// Force the issue
usage_.FetchAddRelaxed(total_charge);
@@ -440,7 +420,7 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
template <class Table>
Status BaseClockTable::ChargeUsageMaybeEvictStrict(
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
typename Table::InsertState& state) {
if (total_charge > capacity) {
return Status::MemoryLimit(
"Cache entry too large for a single cache shard: " +
@@ -465,8 +445,7 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
}
if (request_evict_charge > 0) {
EvictionData data;
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data,
eviction_effort_cap);
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data);
occupancy_.FetchSub(data.freed_count);
if (LIKELY(data.freed_charge > need_evict_charge)) {
assert(data.freed_count > 0);
@@ -496,7 +475,7 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
template <class Table>
inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
typename Table::InsertState& state) {
// For simplicity, we consider that either the cache can accept the insert
// with no evictions, or we must evict enough to make (at least) enough
// space. It could lead to unnecessary failures or excessive evictions in
@@ -532,8 +511,7 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
}
EvictionData data;
if (need_evict_charge > 0) {
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data,
eviction_effort_cap);
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data);
// Deal with potential occupancy deficit
if (UNLIKELY(need_evict_for_occupancy) && data.freed_count == 0) {
assert(data.freed_charge == 0);
@@ -552,7 +530,11 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
return true;
}
void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
void BaseClockTable::TrackAndReleaseEvictedEntry(
ClockHandle* h, BaseClockTable::EvictionData* data) {
data->freed_charge += h->GetTotalCharge();
data->freed_count += 1;
bool took_value_ownership = false;
if (eviction_callback_) {
// For key reconstructed from hash
@@ -569,20 +551,11 @@ void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
MarkEmpty(*h);
}
bool IsEvictionEffortExceeded(const BaseClockTable::EvictionData& data,
uint32_t eviction_effort_cap) {
// Basically checks whether the ratio of useful effort to wasted effort is
// too low, with a start-up allowance for wasted effort before any useful
// effort.
return (data.freed_count + 1U) * uint64_t{eviction_effort_cap} <=
data.seen_pinned_count;
}
template <class Table>
Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
typename Table::HandleImpl** handle,
Cache::Priority priority, size_t capacity,
uint32_t eec_and_scl) {
bool strict_capacity_limit) {
using HandleImpl = typename Table::HandleImpl;
Table& derived = static_cast<Table&>(*this);
@@ -600,12 +573,9 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
// strict_capacity_limit, but mostly pessimistic.
bool use_standalone_insert = false;
const size_t total_charge = proto.GetTotalCharge();
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl & kStrictCapacityLimitBit) {
if (strict_capacity_limit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
total_charge, capacity, need_evict_for_occupancy, state);
if (!s.ok()) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -614,7 +584,7 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
} else {
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
total_charge, capacity, need_evict_for_occupancy, state);
if (!success) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -718,7 +688,8 @@ void BaseClockTable::TEST_ReleaseNMinus1(ClockHandle* h, size_t n) {
#endif
FixedHyperClockTable::FixedHyperClockTable(
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, bool /*strict_capacity_limit*/,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
@@ -1113,8 +1084,7 @@ inline void FixedHyperClockTable::ReclaimEntryUsage(size_t total_charge) {
}
inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
EvictionData* data,
uint32_t eviction_effort_cap) {
EvictionData* data) {
// precondition
assert(requested_charge > 0);
@@ -1135,10 +1105,10 @@ inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
for (;;) {
for (size_t i = 0; i < step_size; i++) {
HandleImpl& h = array_[ModTableSize(Lower32of64(old_clock_pointer + i))];
bool evicting = ClockUpdate(h, data);
bool evicting = ClockUpdate(h);
if (evicting) {
Rollback(h.hashed_key, &h);
TrackAndReleaseEvictedEntry(&h);
TrackAndReleaseEvictedEntry(&h, data);
}
}
@@ -1149,10 +1119,6 @@ inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
if (old_clock_pointer >= max_clock_pointer) {
return;
}
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
// Advance clock pointer (concurrently)
old_clock_pointer = clock_pointer_.FetchAddRelaxed(step_size);
@@ -1167,11 +1133,10 @@ ClockCacheShard<Table>::ClockCacheShard(
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const typename Table::Opts& opts)
: CacheShardBase(metadata_charge_policy),
table_(capacity, metadata_charge_policy, allocator, eviction_callback,
hash_seed, opts),
table_(capacity, strict_capacity_limit, metadata_charge_policy, allocator,
eviction_callback, hash_seed, opts),
capacity_(capacity),
eec_and_scl_(SanitizeEncodeEecAndScl(opts.eviction_effort_cap,
strict_capacity_limit)) {
strict_capacity_limit_(strict_capacity_limit) {
// Initial charge metadata should not exceed capacity
assert(table_.GetUsage() <= capacity_.LoadRelaxed() ||
capacity_.LoadRelaxed() < sizeof(HandleImpl));
@@ -1247,11 +1212,7 @@ void ClockCacheShard<Table>::SetCapacity(size_t capacity) {
template <class Table>
void ClockCacheShard<Table>::SetStrictCapacityLimit(
bool strict_capacity_limit) {
if (strict_capacity_limit) {
eec_and_scl_.FetchOrRelaxed(kStrictCapacityLimitBit);
} else {
eec_and_scl_.FetchAndRelaxed(~kStrictCapacityLimitBit);
}
strict_capacity_limit_.StoreRelaxed(strict_capacity_limit);
// next Insert will take care of any necessary evictions
}
@@ -1273,7 +1234,7 @@ Status ClockCacheShard<Table>::Insert(const Slice& key,
proto.total_charge = charge;
return table_.template Insert<Table>(proto, handle, priority,
capacity_.LoadRelaxed(),
eec_and_scl_.LoadRelaxed());
strict_capacity_limit_.LoadRelaxed());
}
template <class Table>
@@ -1288,9 +1249,9 @@ typename Table::HandleImpl* ClockCacheShard<Table>::CreateStandalone(
proto.value = obj;
proto.helper = helper;
proto.total_charge = charge;
return table_.template CreateStandalone<Table>(proto, capacity_.LoadRelaxed(),
eec_and_scl_.LoadRelaxed(),
allow_uncharged);
return table_.template CreateStandalone<Table>(
proto, capacity_.LoadRelaxed(), strict_capacity_limit_.LoadRelaxed(),
allow_uncharged);
}
template <class Table>
@@ -1542,20 +1503,14 @@ void BaseHyperClockCache<Table>::ReportProblems(
const std::shared_ptr<Logger>& info_log) const {
if (info_log->GetInfoLogLevel() <= InfoLogLevel::DEBUG_LEVEL) {
LoadVarianceStats slot_stats;
uint64_t eviction_effort_exceeded_count = 0;
this->ForEachShard([&](const BaseHyperClockCache<Table>::Shard* shard) {
size_t count = shard->GetTableAddressCount();
for (size_t i = 0; i < count; ++i) {
slot_stats.Add(IsSlotOccupied(*shard->GetTable().HandlePtr(i)));
}
eviction_effort_exceeded_count +=
shard->GetTable().GetEvictionEffortExceededCount();
});
ROCKS_LOG_AT_LEVEL(info_log, InfoLogLevel::DEBUG_LEVEL,
"Slot occupancy stats: %s", slot_stats.Report().c_str());
ROCKS_LOG_AT_LEVEL(info_log, InfoLogLevel::DEBUG_LEVEL,
"Eviction effort exceeded: %" PRIu64,
eviction_effort_exceeded_count);
}
}
@@ -1953,7 +1908,8 @@ class AutoHyperClockTable::ChainRewriteLock {
};
AutoHyperClockTable::AutoHyperClockTable(
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, bool /*strict_capacity_limit*/,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
@@ -2634,8 +2590,7 @@ using ClockUpdateChainLockedOpData =
template <class OpData>
void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
ChainRewriteLock& rewrite_lock,
size_t home,
BaseClockTable::EvictionData* data) {
size_t home) {
constexpr bool kIsPurge = std::is_same_v<OpData, PurgeLockedOpData>;
constexpr bool kIsClockUpdateChain =
std::is_same_v<OpData, ClockUpdateChainLockedOpData>;
@@ -2677,7 +2632,7 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
assert(home == BottomNBits(h->hashed_key[1], home_shift));
if constexpr (kIsClockUpdateChain) {
// Clock update and/or check for purgeable (under (de)construction)
if (ClockUpdate(*h, data, &purgeable)) {
if (ClockUpdate(*h, &purgeable)) {
// Remember for finishing eviction
op_data->push_back(h);
// Entries for eviction become purgeable
@@ -2687,7 +2642,6 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
}
} else {
(void)op_data;
(void)data;
purgeable = ((h->meta.Load() >> ClockHandle::kStateShift) &
ClockHandle::kStateShareableBit) == 0;
}
@@ -2765,8 +2719,7 @@ using PurgeOpData = const UniqueId64x2;
using ClockUpdateChainOpData = ClockUpdateChainLockedOpData;
template <class OpData>
void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home,
BaseClockTable::EvictionData* data) {
void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home) {
// Early efforts to make AutoHCC fully wait-free ran into too many problems
// that needed obscure and potentially inefficient work-arounds to have a
// chance at working.
@@ -2847,9 +2800,9 @@ void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home,
if (!rewrite_lock.IsEnd()) {
if constexpr (kIsPurge) {
PurgeLockedOpData* locked_op_data{};
PurgeImplLocked(locked_op_data, rewrite_lock, home, data);
PurgeImplLocked(locked_op_data, rewrite_lock, home);
} else {
PurgeImplLocked(op_data, rewrite_lock, home, data);
PurgeImplLocked(op_data, rewrite_lock, home);
}
}
}
@@ -3452,8 +3405,7 @@ void AutoHyperClockTable::EraseUnRefEntries() {
}
void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
EvictionData* data,
uint32_t eviction_effort_cap) {
EvictionData* data) {
// precondition
assert(requested_charge > 0);
@@ -3511,12 +3463,12 @@ void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
if (home >= used_length) {
break;
}
PurgeImpl(&to_finish_eviction, home, data);
PurgeImpl(&to_finish_eviction, home);
}
}
for (HandleImpl* h : to_finish_eviction) {
TrackAndReleaseEvictedEntry(h);
TrackAndReleaseEvictedEntry(h, data);
// NOTE: setting likely_empty_slot here can cause us to reduce the
// portion of "at home" entries, probably because an evicted entry
// is more likely to come back than a random new entry and would be
@@ -3544,11 +3496,6 @@ void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
if (old_clock_pointer + step_size >= max_clock_pointer) {
return;
}
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
}
}
+19 -49
View File
@@ -11,7 +11,6 @@
#include <array>
#include <atomic>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -375,14 +374,6 @@ struct ClockHandle : public ClockHandleBasicData {
class BaseClockTable {
public:
struct BaseOpts {
explicit BaseOpts(int _eviction_effort_cap)
: eviction_effort_cap(_eviction_effort_cap) {}
explicit BaseOpts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {}
int eviction_effort_cap;
};
BaseClockTable(CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -395,13 +386,13 @@ class BaseClockTable {
template <class Table>
typename Table::HandleImpl* CreateStandalone(ClockHandleBasicData& proto,
size_t capacity,
uint32_t eec_and_scl,
bool strict_capacity_limit,
bool allow_uncharged);
template <class Table>
Status Insert(const ClockHandleBasicData& proto,
typename Table::HandleImpl** handle, Cache::Priority priority,
size_t capacity, uint32_t eec_and_scl);
size_t capacity, bool strict_capacity_limit);
void Ref(ClockHandle& handle);
@@ -415,17 +406,12 @@ class BaseClockTable {
uint64_t GetYieldCount() const { return yield_count_.LoadRelaxed(); }
uint64_t GetEvictionEffortExceededCount() const {
return eviction_effort_exceeded_count_.LoadRelaxed();
}
struct EvictionData {
size_t freed_charge = 0;
size_t freed_count = 0;
size_t seen_pinned_count = 0;
};
void TrackAndReleaseEvictedEntry(ClockHandle* h);
void TrackAndReleaseEvictedEntry(ClockHandle* h, EvictionData* data);
#ifndef NDEBUG
// Acquire N references
@@ -450,7 +436,6 @@ class BaseClockTable {
template <class Table>
Status ChargeUsageMaybeEvictStrict(size_t total_charge, size_t capacity,
bool need_evict_for_occupancy,
uint32_t eviction_effort_cap,
typename Table::InsertState& state);
// Helper for updating `usage_` for new entry with given `total_charge`
@@ -464,7 +449,6 @@ class BaseClockTable {
template <class Table>
bool ChargeUsageMaybeEvictNonStrict(size_t total_charge, size_t capacity,
bool need_evict_for_occupancy,
uint32_t eviction_effort_cap,
typename Table::InsertState& state);
protected: // data
@@ -477,15 +461,9 @@ class BaseClockTable {
RelaxedAtomic<uint64_t> clock_pointer_{};
// Counter for number of times we yield to wait on another thread.
// It is normal for this to occur rarely in normal operation.
// (Relaxed: a simple stat counter.)
RelaxedAtomic<uint64_t> yield_count_{};
// Counter for number of times eviction effort cap is exceeded.
// It is normal for this to occur rarely in normal operation.
// (Relaxed: a simple stat counter.)
RelaxedAtomic<uint64_t> eviction_effort_exceeded_count_{};
// TODO: is this separation needed if we don't do background evictions?
ALIGN_AS(CACHE_LINE_SIZE)
// Number of elements in the table.
@@ -539,19 +517,17 @@ class FixedHyperClockTable : public BaseClockTable {
inline void SetStandalone() { standalone = true; }
}; // struct HandleImpl
struct Opts : public BaseOpts {
explicit Opts(size_t _estimated_value_size, int _eviction_effort_cap)
: BaseOpts(_eviction_effort_cap),
estimated_value_size(_estimated_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {
struct Opts {
explicit Opts(size_t _estimated_value_size)
: estimated_value_size(_estimated_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts) {
assert(opts.estimated_entry_charge > 0);
estimated_value_size = opts.estimated_entry_charge;
}
size_t estimated_value_size;
};
FixedHyperClockTable(size_t capacity,
FixedHyperClockTable(size_t capacity, bool strict_capacity_limit,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -573,8 +549,7 @@ class FixedHyperClockTable : public BaseClockTable {
// Runs the clock eviction algorithm trying to reclaim at least
// requested_charge. Returns how much is evicted, which could be less
// if it appears impossible to evict the requested amount without blocking.
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -828,20 +803,18 @@ class AutoHyperClockTable : public BaseClockTable {
}
}; // struct HandleImpl
struct Opts : public BaseOpts {
explicit Opts(size_t _min_avg_value_size, int _eviction_effort_cap)
: BaseOpts(_eviction_effort_cap),
min_avg_value_size(_min_avg_value_size) {}
struct Opts {
explicit Opts(size_t _min_avg_value_size)
: min_avg_value_size(_min_avg_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {
explicit Opts(const HyperClockCacheOptions& opts) {
assert(opts.estimated_entry_charge == 0);
min_avg_value_size = opts.min_avg_entry_charge;
}
size_t min_avg_value_size;
};
AutoHyperClockTable(size_t capacity,
AutoHyperClockTable(size_t capacity, bool strict_capacity_limit,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -868,8 +841,7 @@ class AutoHyperClockTable : public BaseClockTable {
// Runs the clock eviction algorithm trying to reclaim at least
// requested_charge. Returns how much is evicted, which could be less
// if it appears impossible to evict the requested amount without blocking.
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -934,8 +906,7 @@ class AutoHyperClockTable : public BaseClockTable {
// with proper handling to ensure all existing data is seen even in the
// presence of concurrent insertions, etc. (See implementation.)
template <class OpData>
void PurgeImpl(OpData* op_data, size_t home = SIZE_MAX,
EvictionData* data = nullptr);
void PurgeImpl(OpData* op_data, size_t home = SIZE_MAX);
// An RAII wrapper for locking a chain of entries for removals. See
// implementation.
@@ -945,7 +916,7 @@ class AutoHyperClockTable : public BaseClockTable {
// implementation.
template <class OpData>
void PurgeImplLocked(OpData* op_data, ChainRewriteLock& rewrite_lock,
size_t home, EvictionData* data);
size_t home);
// Update length_info_ as much as possible without waiting, given a known
// usable (ready for inserts and lookups) grow_home. (Previous grow_homes
@@ -1107,10 +1078,9 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<size_t> capacity_;
// Encodes eviction_effort_cap (bottom 31 bits) and strict_capacity_limit
// (top bit). See HyperClockCacheOptions::eviction_effort_cap etc.
// Whether to reject insertion if cache reaches its full capacity.
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<uint32_t> eec_and_scl_;
RelaxedAtomic<bool> strict_capacity_limit_;
}; // class ClockCacheShard
template <class Table>
+1 -3
View File
@@ -31,7 +31,7 @@ CompressedSecondaryCache::~CompressedSecondaryCache() {}
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) {
bool& kept_in_sec_cache) {
assert(helper);
// This is a minor optimization. Its ok to skip it in TSAN in order to
// avoid a false positive.
@@ -51,7 +51,6 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
void* handle_value = cache_->Value(lru_handle);
if (handle_value == nullptr) {
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_DUMMY_HITS);
return nullptr;
}
@@ -138,7 +137,6 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
}
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_HITS);
return handle;
}
+1 -1
View File
@@ -86,7 +86,7 @@ class CompressedSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) override;
bool& kept_in_sec_cache) override;
bool SupportForceErase() const override { return true; }
+13 -15
View File
@@ -44,7 +44,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
// Lookup an non-existent key.
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
sec_cache->Lookup(key0, GetHelper(), this, true, /*advise_erase=*/true,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle0, nullptr);
Random rnd(301);
@@ -59,7 +59,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle1_1, nullptr);
// Insert and Lookup the item k1 for the second time and advise erasing it.
@@ -68,7 +68,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_NE(handle1_2, nullptr);
ASSERT_FALSE(kept_in_sec_cache);
if (sec_cache_is_compressed) {
@@ -89,7 +89,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
// Lookup the item k1 again.
std::unique_ptr<SecondaryCacheResultHandle> handle1_3 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle1_3, nullptr);
// Insert and Lookup the item k2.
@@ -99,7 +99,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle2_1, nullptr);
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
@@ -115,7 +115,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
}
std::unique_ptr<SecondaryCacheResultHandle> handle2_2 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_NE(handle2_2, nullptr);
std::unique_ptr<TestItem> val2 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2_2->Value()));
@@ -196,14 +196,14 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
bool kept_in_sec_cache{false};
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle1, nullptr);
// Insert k2 and k1 is evicted.
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_NE(handle2, nullptr);
std::unique_ptr<TestItem> val2 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
@@ -215,14 +215,14 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle1_1, nullptr);
// Create Fails.
SetFailCreate(true);
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/true,
/*stats=*/nullptr, kept_in_sec_cache);
kept_in_sec_cache);
ASSERT_EQ(handle2_1, nullptr);
// Save Fails.
@@ -912,9 +912,9 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1U);
bool kept_in_sec_cache{true};
std::unique_ptr<SecondaryCacheResultHandle> handle = sec_cache->Lookup(
ith_key, GetHelper(role), this, true,
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
std::unique_ptr<SecondaryCacheResultHandle> handle =
sec_cache->Lookup(ith_key, GetHelper(role), this, true,
/*advise_erase=*/true, kept_in_sec_cache);
ASSERT_NE(handle, nullptr);
// Lookup returns the right data
@@ -992,8 +992,6 @@ class CompressedSecCacheTestWithTiered
/*_capacity=*/0,
/*_estimated_entry_charge=*/256 << 10,
/*_num_shard_bits=*/0);
// eviction_effort_cap setting simply to avoid churn in existing test
hcc_opts.eviction_effort_cap = 100;
TieredCacheOptions opts;
lru_opts.capacity = 0;
lru_opts.num_shard_bits = 0;
+3 -60
View File
@@ -389,13 +389,12 @@ class ClockCacheTest : public testing::Test {
}
}
void NewShard(size_t capacity, bool strict_capacity_limit = true,
int eviction_effort_cap = 30) {
void NewShard(size_t capacity, bool strict_capacity_limit = true) {
DeleteShard();
shard_ =
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
TableOpts opts{1 /*value_size*/, eviction_effort_cap};
TableOpts opts{1 /*value_size*/};
new (shard_)
Shard(capacity, strict_capacity_limit, kDontChargeCacheMetadata,
/*allocator*/ nullptr, &eviction_callback_, &hash_seed_, opts);
@@ -446,20 +445,12 @@ class ClockCacheTest : public testing::Test {
return Slice(reinterpret_cast<const char*>(&hashed_key), 16U);
}
// A bad hash function for testing / stressing collision handling
static inline UniqueId64x2 TestHashedKey(char key) {
// For testing hash near-collision behavior, put the variance in
// hashed_key in bits that are unlikely to be used as hash bits.
return {(static_cast<uint64_t>(key) << 56) + 1234U, 5678U};
}
// A reasonable hash function, for testing "typical behavior" etc.
template <typename T>
static inline UniqueId64x2 CheapHash(T i) {
return {static_cast<uint64_t>(i) * uint64_t{0x85EBCA77C2B2AE63},
static_cast<uint64_t>(i) * uint64_t{0xC2B2AE3D27D4EB4F}};
}
Shard* shard_ = nullptr;
private:
@@ -692,53 +683,6 @@ TYPED_TEST(ClockCacheTest, ClockEvictionTest) {
}
}
TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
for (bool strict_capacity_limit : {true, false}) {
SCOPED_TRACE("strict_capacity_limit = " +
std::to_string(strict_capacity_limit));
for (int eec : {-42, 0, 1, 10, 100, 1000}) {
SCOPED_TRACE("eviction_effort_cap = " + std::to_string(eec));
constexpr size_t kCapacity = 1000;
// Start with much larger capacity to ensure that we can go way over
// capacity without reaching table occupancy limit.
this->NewShard(3 * kCapacity, strict_capacity_limit, eec);
auto& shard = *this->shard_;
shard.SetCapacity(kCapacity);
// Nearly fill the cache with pinned entries, then add a bunch of
// non-pinned entries. eviction_effort_cap should affect how many
// evictable entries are present beyond the cache capacity, despite
// being evictable.
constexpr size_t kCount = kCapacity - 1;
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [kCount] {} };
for (size_t i = 0; i < 2 * kCount; ++i) {
UniqueId64x2 hkey = this->CheapHash(i);
ASSERT_OK(shard.Insert(
this->TestKey(hkey), hkey, nullptr /*value*/, &kNoopCacheItemHelper,
1 /*charge*/, i < kCount ? &ha[i] : nullptr, Cache::Priority::LOW));
}
if (strict_capacity_limit) {
// If strict_capacity_limit is enabled, the cache will never exceed its
// capacity
EXPECT_EQ(shard.GetOccupancyCount(), kCapacity);
} else {
// Rough inverse relationship between cap and possible memory
// explosion, which shows up as increased table occupancy count.
int effective_eec = std::max(int{1}, eec) + 1;
EXPECT_NEAR(shard.GetOccupancyCount() * 1.0,
kCount * (1 + 1.4 / effective_eec),
kCount * (0.6 / effective_eec) + 1.0);
}
for (size_t i = 0; i < kCount; ++i) {
shard.Release(ha[i]);
}
}
}
}
namespace {
struct DeleteCounter {
int deleted = 0;
@@ -1091,8 +1035,7 @@ class TestSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/,
bool /*advise_erase*/, Statistics* /*stats*/,
bool& kept_in_sec_cache) override {
bool /*advise_erase*/, bool& kept_in_sec_cache) override {
std::string key_str = key.ToString();
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
+5 -6
View File
@@ -294,8 +294,7 @@ Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
bool kept_in_sec_cache = false;
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
secondary_cache_->Lookup(key, helper, create_context, /*wait*/ true,
found_dummy_entry, stats,
/*out*/ kept_in_sec_cache);
found_dummy_entry, /*out*/ kept_in_sec_cache);
if (secondary_handle) {
result = Promote(std::move(secondary_handle), key, helper, priority,
stats, found_dummy_entry, kept_in_sec_cache);
@@ -349,10 +348,10 @@ void CacheWithSecondaryAdapter::StartAsyncLookupOnMySecondary(
assert(async_handle.result_handle == nullptr);
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
secondary_cache_->Lookup(
async_handle.key, async_handle.helper, async_handle.create_context,
/*wait*/ false, async_handle.found_dummy_entry, async_handle.stats,
/*out*/ async_handle.kept_in_sec_cache);
secondary_cache_->Lookup(async_handle.key, async_handle.helper,
async_handle.create_context, /*wait*/ false,
async_handle.found_dummy_entry,
/*out*/ async_handle.kept_in_sec_cache);
if (secondary_handle) {
// TODO with stacked secondaries: Check & process if already ready?
async_handle.pending_handle = secondary_handle.release();
+5 -13
View File
@@ -5,8 +5,6 @@
#include "cache/tiered_secondary_cache.h"
#include "monitoring/statistics_impl.h"
namespace ROCKSDB_NAMESPACE {
// Creation callback for use in the lookup path. It calls the upper layer
@@ -31,9 +29,6 @@ Status TieredSecondaryCache::MaybeInsertAndCreate(
// TODO: Don't hardcode the source
context->comp_sec_cache->InsertSaved(*context->key, data, type, source)
.PermitUncheckedError();
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTIONS);
} else {
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS);
}
// Primary cache will accept the object, so call its helper to create
// the object
@@ -48,10 +43,10 @@ Status TieredSecondaryCache::MaybeInsertAndCreate(
std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) {
bool& kept_in_sec_cache) {
bool dummy = false;
std::unique_ptr<SecondaryCacheResultHandle> result =
target()->Lookup(key, helper, create_context, wait, advise_erase, stats,
target()->Lookup(key, helper, create_context, wait, advise_erase,
/*kept_in_sec_cache=*/dummy);
// We never want the item to spill back into the secondary cache
kept_in_sec_cache = true;
@@ -71,10 +66,9 @@ std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
ctx.helper = helper;
ctx.inner_ctx = create_context;
ctx.comp_sec_cache = target();
ctx.stats = stats;
return nvm_sec_cache_->Lookup(key, outer_helper, &ctx, wait, advise_erase,
stats, kept_in_sec_cache);
kept_in_sec_cache);
}
// If wait is false, i.e its an async lookup, we have to allocate a result
@@ -86,10 +80,8 @@ std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
handle->ctx()->helper = helper;
handle->ctx()->inner_ctx = create_context;
handle->ctx()->comp_sec_cache = target();
handle->ctx()->stats = stats;
handle->SetInnerHandle(
nvm_sec_cache_->Lookup(key, outer_helper, handle->ctx(), wait,
advise_erase, stats, kept_in_sec_cache));
handle->SetInnerHandle(nvm_sec_cache_->Lookup(
key, outer_helper, handle->ctx(), wait, advise_erase, kept_in_sec_cache));
if (!handle->inner_handle()) {
handle.reset();
} else {
+1 -2
View File
@@ -59,7 +59,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) override;
bool& kept_in_sec_cache) override;
virtual void WaitAll(
std::vector<SecondaryCacheResultHandle*> handles) override;
@@ -72,7 +72,6 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
Cache::CreateContext* inner_ctx;
std::shared_ptr<SecondaryCacheResultHandle> inner_handle;
SecondaryCache* comp_sec_cache;
Statistics* stats;
};
class ResultHandle : public SecondaryCacheResultHandle {
+1 -1
View File
@@ -62,7 +62,7 @@ class TestSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool /*advise_erase*/,
Statistics* /*stats*/, bool& kept_in_sec_cache) override {
bool& kept_in_sec_cache) override {
std::string key_str = key.ToString();
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
+2 -2
View File
@@ -1220,7 +1220,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
auto sec_handle0 = secondary_cache->Lookup(
key0, BlobSource::SharedCacheInterface::GetFullHelper(),
/*context*/ nullptr, true,
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
/*advise_erase=*/true, kept_in_sec_cache);
ASSERT_FALSE(kept_in_sec_cache);
ASSERT_NE(sec_handle0, nullptr);
ASSERT_TRUE(sec_handle0->IsReady());
@@ -1248,7 +1248,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
auto sec_handle1 = secondary_cache->Lookup(
key1, BlobSource::SharedCacheInterface::GetFullHelper(),
/*context*/ nullptr, true,
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
/*advise_erase=*/true, kept_in_sec_cache);
ASSERT_FALSE(kept_in_sec_cache);
ASSERT_EQ(sec_handle1, nullptr);
+15 -45
View File
@@ -833,8 +833,8 @@ std::unique_ptr<WriteControllerToken> SetupDelay(
return write_controller->GetDelayToken(write_rate);
}
int GetL0FileCountForCompactionSpeedup(int level0_file_num_compaction_trigger,
int level0_slowdown_writes_trigger) {
int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
int level0_slowdown_writes_trigger) {
// SanitizeOptions() ensures it.
assert(level0_file_num_compaction_trigger <= level0_slowdown_writes_trigger);
@@ -864,36 +864,6 @@ int GetL0FileCountForCompactionSpeedup(int level0_file_num_compaction_trigger,
return static_cast<int>(res);
}
}
uint64_t GetPendingCompactionBytesForCompactionSpeedup(
const MutableCFOptions& mutable_cf_options,
const VersionStorageInfo* vstorage) {
// Compaction debt relatively large compared to the stable (bottommost) data
// size indicates compaction fell behind.
const uint64_t kBottommostSizeMultiplier = 8;
// Meaningful progress toward the slowdown trigger is another good indication.
const uint64_t kSlowdownTriggerDivisor = 4;
uint64_t bottommost_files_size = 0;
for (const auto& level_and_file : vstorage->BottommostFiles()) {
bottommost_files_size += level_and_file.second->fd.GetFileSize();
}
// Slowdown trigger might be zero but that means compaction speedup should
// always happen (undocumented/historical), so no special treatment is needed.
uint64_t slowdown_threshold =
mutable_cf_options.soft_pending_compaction_bytes_limit /
kSlowdownTriggerDivisor;
// Size of zero, however, should not be used to decide to speedup compaction.
if (bottommost_files_size == 0) {
return slowdown_threshold;
}
uint64_t size_threshold =
MultiplyCheckOverflow(bottommost_files_size, kBottommostSizeMultiplier);
return std::min(size_threshold, slowdown_threshold);
}
} // anonymous namespace
std::pair<WriteStallCondition, WriteStallCause>
@@ -1049,7 +1019,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
} else {
assert(write_stall_condition == WriteStallCondition::kNormal);
if (vstorage->l0_delay_trigger_count() >=
GetL0FileCountForCompactionSpeedup(
GetL0ThresholdSpeedupCompaction(
mutable_cf_options.level0_file_num_compaction_trigger,
mutable_cf_options.level0_slowdown_writes_trigger)) {
write_controller_token_ =
@@ -1059,22 +1029,22 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
"[%s] Increasing compaction threads because we have %d level-0 "
"files ",
name_.c_str(), vstorage->l0_delay_trigger_count());
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit == 0) {
} else if (vstorage->estimated_compaction_needed_bytes() >=
mutable_cf_options.soft_pending_compaction_bytes_limit / 4) {
// Increase compaction threads if bytes needed for compaction exceeds
// 1/4 of threshold for slowing down.
// If soft pending compaction byte limit is not set, always speed up
// compaction.
write_controller_token_ =
write_controller->GetCompactionPressureToken();
} else if (vstorage->estimated_compaction_needed_bytes() >=
GetPendingCompactionBytesForCompactionSpeedup(
mutable_cf_options, vstorage)) {
write_controller_token_ =
write_controller->GetCompactionPressureToken();
ROCKS_LOG_INFO(
ioptions_.logger,
"[%s] Increasing compaction threads because of estimated pending "
"compaction "
"bytes %" PRIu64,
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
ROCKS_LOG_INFO(
ioptions_.logger,
"[%s] Increasing compaction threads because of estimated pending "
"compaction "
"bytes %" PRIu64,
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
}
} else {
write_controller_token_.reset();
}
-37
View File
@@ -2971,43 +2971,6 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
db_options_.max_background_compactions = 6;
Open();
ColumnFamilyData* cfd =
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
MutableCFOptions mutable_cf_options(column_family_options_);
mutable_cf_options.soft_pending_compaction_bytes_limit =
std::numeric_limits<uint64_t>::max();
{
// No bottommost data, so debt ratio cannot trigger speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
// Add a tiny amount of bottommost data.
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
ASSERT_OK(db_->Flush(FlushOptions()));
{
// 1MB debt is way bigger than bottommost data so definitely triggers
// speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
// Eight bytes is way smaller than bottommost data so definitely does not
// trigger speedup.
vstorage->TEST_set_estimated_compaction_needed_bytes(8);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
}
TEST_P(ColumnFamilyTest, CreateAndDestroyOptions) {
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
ColumnFamilyHandle* cfh;
+18 -18
View File
@@ -3301,7 +3301,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FlushFinish:0");
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
// There is no need to find obsolete files if the flush job is rescheduled
// There is no need to do these clean up if the flush job is rescheduled
// to retain user-defined timestamps because the job doesn't get to the
// stage of actually flushing the MemTables.
if (!flush_rescheduled_to_retain_udt) {
@@ -3309,25 +3309,25 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
// have created. Thus, we force full scan in FindObsoleteFiles()
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
!s.IsColumnFamilyDropped());
}
// delete unnecessary files if any, this is done outside the mutex
if (job_context.HaveSomethingToClean() ||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
mutex_.Unlock();
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
// Have to flush the info logs before bg_flush_scheduled_--
// because if bg_flush_scheduled_ becomes 0 and the lock is
// released, the deconstructor of DB can kick in and destroy all the
// states of DB so info_log might not be available after that point.
// It also applies to access other states that DB owns.
log_buffer.FlushBufferToLog();
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
// delete unnecessary files if any, this is done outside the mutex
if (job_context.HaveSomethingToClean() ||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
mutex_.Unlock();
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
// Have to flush the info logs before bg_flush_scheduled_--
// because if bg_flush_scheduled_ becomes 0 and the lock is
// released, the deconstructor of DB can kick in and destroy all the
// states of DB so info_log might not be available after that point.
// It also applies to access other states that DB owns.
log_buffer.FlushBufferToLog();
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
mutex_.Lock();
}
job_context.Clean();
mutex_.Lock();
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:ContextCleanedUp");
}
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:ContextCleanedUp");
assert(num_running_flushes_ > 0);
num_running_flushes_--;
+5 -6
View File
@@ -437,10 +437,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
valid_batches += writer->batch_cnt;
if (writer->ShouldWriteToMemtable()) {
total_count += WriteBatchInternal::Count(writer->batch);
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
parallel = parallel && !writer->batch->HasMerge();
}
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
if (writer->pre_release_callback) {
pre_release_callback_cnt++;
}
@@ -720,11 +720,11 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
if (writer->ShouldWriteToMemtable()) {
writer->sequence = next_sequence;
size_t count = WriteBatchInternal::Count(writer->batch);
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
next_sequence += count;
total_count += count;
}
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
}
}
if (w.disable_wal) {
@@ -1839,12 +1839,11 @@ Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
delay = 0;
}
TEST_SYNC_POINT("DBImpl::DelayWrite:Start");
start_time = immutable_db_options_.clock->NowMicros();
if (delay > 0) {
if (write_options.no_slowdown) {
return Status::Incomplete("Write stall");
}
start_time = immutable_db_options_.clock->NowMicros();
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
// Notify write_thread about the stall so it can setup a barrier and
+32 -16
View File
@@ -83,7 +83,8 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
cfd_(cfd),
timestamp_ub_(read_options.timestamp),
timestamp_lb_(read_options.iter_start_ts),
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0),
auto_readahead_size_(read_options.auto_readahead_size) {
RecordTick(statistics_, NO_ITERATOR_CREATED);
if (pin_thru_lifetime_) {
pinned_iters_mgr_.StartPinning();
@@ -394,7 +395,9 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
case kTypeValue:
case kTypeBlobIndex:
case kTypeWideColumnEntity:
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
return false;
}
if (timestamp_lb_) {
@@ -425,7 +428,9 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
return true;
break;
case kTypeMerge:
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
return false;
}
saved_key_.SetUserKey(
@@ -570,7 +575,8 @@ bool DBIter::MergeValuesNewToOld() {
iter_.Next();
break;
}
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
valid_ = false;
return false;
}
@@ -741,15 +747,22 @@ bool DBIter::ReverseToBackward() {
// When current_entry_is_merged_ is true, iter_ may be positioned on the next
// key, which may not exist or may have prefix different from current.
// If that's the case, seek to saved_key_.
if (current_entry_is_merged_ &&
(!expect_total_order_inner_iter() || !iter_.Valid())) {
//
// In case of auto_readahead_size enabled, index_iter moves forward during
// forward scan for block cache lookup and points to different block. If Prev
// op is called, it needs to call SeekForPrev to point to right index_iter_ in
// BlockBasedTableIterator. This only happens when direction is changed from
// forward to backward.
if ((current_entry_is_merged_ &&
(!expect_total_order_inner_iter() || !iter_.Valid())) ||
auto_readahead_size_) {
IterKey last_key;
// Using kMaxSequenceNumber and kValueTypeForSeek
// (not kValueTypeForSeekForPrev) to seek to a key strictly smaller
// than saved_key_.
last_key.SetInternalKey(ParsedInternalKey(
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
if (!expect_total_order_inner_iter()) {
if (!expect_total_order_inner_iter() || auto_readahead_size_) {
iter_.SeekForPrev(last_key.GetInternalKey());
} else {
// Some iterators may not support SeekForPrev(), so we avoid using it
@@ -896,7 +909,8 @@ bool DBIter::FindValueForCurrentKey() {
return FindValueForCurrentKeyUsingSeek();
}
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
valid_ = false;
return false;
}
@@ -1135,7 +1149,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
}
return true;
}
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
valid_ = false;
return false;
}
if (timestamp_size_ > 0) {
@@ -1200,7 +1215,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
ikey.type == kTypeDeletionWithTimestamp) {
break;
}
if (!PrepareValue()) {
if (!iter_.PrepareValue()) {
valid_ = false;
return false;
}
@@ -1277,8 +1293,8 @@ bool DBIter::MergeWithNoBaseValue(const Slice& user_key) {
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
@@ -1290,8 +1306,8 @@ bool DBIter::MergeWithPlainBaseValue(const Slice& value,
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kPlainBaseValue, value,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
@@ -1303,8 +1319,8 @@ bool DBIter::MergeWithWideColumnBaseValue(const Slice& entity,
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kWideBaseValue, entity,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
+1 -16
View File
@@ -328,22 +328,6 @@ class DBIter final : public Iterator {
bool MergeWithPlainBaseValue(const Slice& value, const Slice& user_key);
bool MergeWithWideColumnBaseValue(const Slice& entity, const Slice& user_key);
bool PrepareValue() {
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
return false;
}
// ikey_ could change as BlockBasedTableIterator does Block cache
// lookup and index_iter_ could point to different block resulting
// in ikey_ pointing to wrong key. So ikey_ needs to be updated in
// case of Seek/Next calls to point to right key again.
if (!ParseKey(&ikey_)) {
return false;
}
return true;
}
const SliceTransform* prefix_extractor_;
Env* const env_;
SystemClock* clock_;
@@ -418,6 +402,7 @@ class DBIter final : public Iterator {
const Slice* const timestamp_lb_;
const size_t timestamp_size_;
std::string saved_timestamp_;
bool auto_readahead_size_;
};
// Return a new iterator that converts internal keys (yielded by
-74
View File
@@ -6,11 +6,9 @@
#include <string>
#include "db/db_test_util.h"
#include "db/write_batch_internal.h"
#include "monitoring/thread_status_util.h"
#include "port/stack_trace.h"
#include "rocksdb/statistics.h"
#include "rocksdb/utilities/transaction_db.h"
#include "util/random.h"
namespace ROCKSDB_NAMESPACE {
@@ -285,78 +283,6 @@ TEST_F(DBStatisticsTest, BlockChecksumStats) {
options.statistics->getTickerCount(BLOCK_CHECKSUM_MISMATCH_COUNT));
}
TEST_F(DBStatisticsTest, BytesWrittenStats) {
Options options = CurrentOptions();
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kExceptHistogramOrTimers);
Reopen(options);
EXPECT_EQ(0, options.statistics->getAndResetTickerCount(WAL_FILE_BYTES));
EXPECT_EQ(0, options.statistics->getAndResetTickerCount(BYTES_WRITTEN));
const int kNumKeysWritten = 100;
// Scenario 0: Not using transactions.
// This will write to WAL and memtable directly.
ASSERT_OK(options.statistics->Reset());
for (int i = 0; i < kNumKeysWritten; ++i) {
ASSERT_OK(Put(Key(i), "val"));
}
EXPECT_EQ(options.statistics->getAndResetTickerCount(WAL_FILE_BYTES),
options.statistics->getAndResetTickerCount(BYTES_WRITTEN));
// Scenario 1: Using transactions.
// This should not double count BYTES_WRITTEN (issue #12061).
for (bool enable_pipelined_write : {false, true}) {
ASSERT_OK(options.statistics->Reset());
// Destroy the DB to recreate as a TransactionDB.
Destroy(options, true);
// Create a TransactionDB.
TransactionDB* txn_db = nullptr;
TransactionDBOptions txn_db_opts;
txn_db_opts.write_policy = TxnDBWritePolicy::WRITE_COMMITTED;
options.enable_pipelined_write = enable_pipelined_write;
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, &txn_db));
ASSERT_NE(txn_db, nullptr);
db_ = txn_db->GetBaseDB();
WriteOptions wopts;
TransactionOptions txn_opts;
Transaction* txn = txn_db->BeginTransaction(wopts, txn_opts, nullptr);
ASSERT_NE(txn, nullptr);
ASSERT_OK(txn->SetName("txn1"));
for (int i = 0; i < kNumKeysWritten; ++i) {
ASSERT_OK(txn->Put(Key(i), "val"));
}
// Prepare() writes to WAL, but not to memtable. (WriteCommitted)
ASSERT_OK(txn->Prepare());
EXPECT_NE(0, options.statistics->getTickerCount(WAL_FILE_BYTES));
// BYTES_WRITTEN would have been non-zero previously (issue #12061).
EXPECT_EQ(0, options.statistics->getTickerCount(BYTES_WRITTEN));
// Commit() writes to memtable and also a commit marker to WAL.
ASSERT_OK(txn->Commit());
delete txn;
// The WAL has an extra header of size `kHeader` written to it,
// as we are writing twice to it (first during Prepare, second during
// Commit).
EXPECT_EQ(options.statistics->getAndResetTickerCount(WAL_FILE_BYTES),
options.statistics->getAndResetTickerCount(BYTES_WRITTEN) +
WriteBatchInternal::kHeader);
// Cleanup
db_ = nullptr;
delete txn_db;
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
-66
View File
@@ -22,7 +22,6 @@
#include "table/table_properties_internal.h"
#include "test_util/testharness.h"
#include "test_util/testutil.h"
#include "util/atomic.h"
#include "util/random.h"
@@ -418,71 +417,6 @@ TEST_F(DBTablePropertiesTest, GetDbIdentifiersProperty) {
}
}
TEST_F(DBTablePropertiesTest, FactoryReturnsNull) {
struct JunkTablePropertiesCollector : public TablePropertiesCollector {
const char* Name() const override { return "JunkTablePropertiesCollector"; }
Status Finish(UserCollectedProperties* properties) override {
properties->insert({"Junk", "Junk"});
return Status::OK();
}
UserCollectedProperties GetReadableProperties() const override {
return {};
}
};
// Alternates between putting a "Junk" property and using `nullptr` to
// opt out.
static RelaxedAtomic<int> count{0};
struct SometimesTablePropertiesCollectorFactory
: public TablePropertiesCollectorFactory {
const char* Name() const override {
return "SometimesTablePropertiesCollectorFactory";
}
TablePropertiesCollector* CreateTablePropertiesCollector(
TablePropertiesCollectorFactory::Context /*context*/) override {
if (count.FetchAddRelaxed(1) & 1) {
return nullptr;
} else {
return new JunkTablePropertiesCollector();
}
}
};
Options options = CurrentOptions();
options.table_properties_collector_factories.emplace_back(
std::make_shared<SometimesTablePropertiesCollectorFactory>());
// For plain table
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
for (std::shared_ptr<TableFactory> tf :
{options.table_factory,
std::shared_ptr<TableFactory>(NewPlainTableFactory({}))}) {
SCOPED_TRACE("Table factory = " + std::string(tf->Name()));
options.table_factory = tf;
DestroyAndReopen(options);
ASSERT_OK(Put("key0", "value1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("key0", "value2"));
ASSERT_OK(Flush());
TablePropertiesCollection props;
ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
int no_junk_count = 0;
int junk_count = 0;
for (const auto& item : props) {
if (item.second->user_collected_properties.find("Junk") !=
item.second->user_collected_properties.end()) {
junk_count++;
} else {
no_junk_count++;
}
}
EXPECT_EQ(1, no_junk_count);
EXPECT_EQ(1, junk_count);
}
}
class DBTableHostnamePropertyTest
: public DBTestBase,
public ::testing::WithParamInterface<std::tuple<int, std::string>> {
+39 -153
View File
@@ -1646,88 +1646,35 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
const Snapshot* snap_with_nothing = db_->GetSnapshot();
ASSERT_OK(db_->Put(write_opts, "foo", ts_early, "bar"));
ASSERT_OK(db_->Put(write_opts, "foo2", ts_early, "bar2"));
ASSERT_OK(db_->Put(write_opts, "foo3", ts_early, "bar3"));
const Snapshot* snap_with_foo = db_->GetSnapshot();
// Ensure file has sequence number greater than snapshot_with_foo
for (int i = 0; i < 10; i++) {
std::string numStr = std::to_string(i);
ASSERT_OK(db_->Put(write_opts, numStr, ts_later, numStr));
}
ASSERT_OK(Flush());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 0);
ReadOptions read_opts;
read_opts.timestamp = &ts_later_slice;
std::string read_value;
std::string read_ts;
Status s;
Status s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
ASSERT_EQ(read_ts, ts_early);
int expected_hit_count = 0;
int expected_miss_count = 0;
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
{
read_opts.timestamp = nullptr;
s = db_->Get(read_opts, "foo", &read_value);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsInvalidArgument());
}
// Mix use of Get
{
read_opts.timestamp = &ts_later_slice;
// Use Get without ts first, expect cache entry to store the correct ts
s = db_->Get(read_opts, "foo2", &read_value);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_value, "bar2");
s = db_->Get(read_opts, "foo2", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar2");
// Use Get with ts first, expect the Get without ts can get correct record
s = db_->Get(read_opts, "foo3", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar3");
s = db_->Get(read_opts, "foo3", &read_value);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_value, "bar3");
}
{
// Test with consecutive calls of Get with ts.
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
// Test repeated get on cache entry
for (int i = 0; i < 3; i++) {
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT),
++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
}
}
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
// Row cache is not storing the ts when record is inserted/updated.
// To be fixed after enabling ROW_CACHE with timestamp.
// ASSERT_EQ(read_ts, ts_early);
{
std::string ts_nothing = Timestamp(0, 0);
@@ -1735,43 +1682,41 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
read_opts.timestamp = &ts_nothing_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
read_opts.timestamp = &ts_later_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
}
{
read_opts.snapshot = snap_with_foo;
read_opts.timestamp = &ts_later_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 3);
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 3);
}
{
read_opts.snapshot = snap_with_nothing;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 4);
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 5);
}
db_->ReleaseSnapshot(snap_with_nothing);
@@ -1779,65 +1724,6 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
Close();
}
TEST_F(DBBasicTestWithTimestamp, GetWithRowCacheMultiSST) {
BlockBasedTableOptions table_options;
table_options.block_size = 1;
Options options = CurrentOptions();
options.env = env_;
options.create_if_missing = true;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
LRUCacheOptions cache_options;
cache_options.capacity = 8192;
options.row_cache = cache_options.MakeSharedRowCache();
const size_t kTimestampSize = Timestamp(0, 0).size();
TestComparator test_cmp(kTimestampSize);
options.comparator = &test_cmp;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
options.disable_auto_compactions = true;
DestroyAndReopen(options);
std::string ts_early = Timestamp(1, 0);
std::string ts_later = Timestamp(10, 0);
Slice ts_later_slice = ts_later;
ASSERT_OK(db_->Put(WriteOptions(), "foo", ts_early, "v1"));
ASSERT_OK(Flush());
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
ASSERT_OK(
db_->Merge(WriteOptions(), default_cf, "foo", Timestamp(2, 0), "v2"));
ASSERT_OK(
db_->Merge(WriteOptions(), default_cf, "foo", Timestamp(3, 0), "v3"));
ASSERT_OK(Flush());
ReadOptions read_opts;
read_opts.timestamp = &ts_later_slice;
std::string read_value;
std::string read_ts;
Status s;
{
// Since there are two SST files, will trigger the table lookup twice.
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
ASSERT_EQ(read_ts, Timestamp(3, 0));
ASSERT_EQ(read_value, "v1,v2,v3");
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
ASSERT_EQ(read_ts, Timestamp(3, 0));
ASSERT_EQ(read_value, "v1,v2,v3");
}
}
TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetPrefixFilter) {
Options options = CurrentOptions();
options.env = env_;
+2 -2
View File
@@ -326,7 +326,7 @@ TEST_F(ImportColumnFamilyTest, ImportSSTFileWriterFilesWithRangeTombstone) {
const SstFileMetaData* file_meta = nullptr;
for (const auto& level_meta : import_cf_meta.levels) {
if (!level_meta.files.empty()) {
file_meta = level_meta.files.data();
file_meta = &(level_meta.files[0]);
break;
}
}
@@ -389,7 +389,7 @@ TEST_F(ImportColumnFamilyTest, ImportExportedSSTFromAnotherCF) {
*metadata_ptr_, &import_cfh2_));
ASSERT_NE(import_cfh2_, nullptr);
delete metadata_ptr_;
metadata_ptr_ = nullptr;
metadata_ptr_ = NULL;
std::string value1, value2;
+4 -4
View File
@@ -132,8 +132,8 @@ class TestCompactionListener : public EventListener {
ASSERT_EQ(db->GetEnv()->GetThreadID(), ci.thread_id);
ASSERT_GT(ci.thread_id, 0U);
for (const auto& fl : {ci.input_files, ci.output_files}) {
for (const auto& fn : fl) {
for (auto fl : {ci.input_files, ci.output_files}) {
for (auto fn : fl) {
auto it = ci.table_properties.find(fn);
ASSERT_NE(it, ci.table_properties.end());
auto tp = it->second;
@@ -237,7 +237,7 @@ class TestFlushListener : public EventListener {
std::vector<ThreadStatus> thread_list;
ASSERT_OK(env_->GetThreadList(&thread_list));
bool found_match = false;
for (const auto& thread_status : thread_list) {
for (auto thread_status : thread_list) {
if (thread_status.operation_type == ThreadStatus::OP_FLUSH ||
thread_status.operation_type == ThreadStatus::OP_COMPACTION) {
if (thread_id == thread_status.thread_id) {
@@ -893,7 +893,7 @@ class MemTableSealedListener : public EventListener {
SequenceNumber latest_seq_number_;
public:
MemTableSealedListener() = default;
MemTableSealedListener() {}
void OnMemTableSealed(const MemTableInfo& info) override {
latest_seq_number_ = info.first_seqno;
}
+2 -2
View File
@@ -9,7 +9,7 @@
#include "db/log_reader.h"
#include <cstdio>
#include <stdio.h>
#include "file/sequence_file_reader.h"
#include "port/lang.h"
@@ -21,7 +21,7 @@
namespace ROCKSDB_NAMESPACE {
namespace log {
Reader::Reporter::~Reporter() = default;
Reader::Reporter::~Reporter() {}
Reader::Reader(std::shared_ptr<Logger> info_log,
std::unique_ptr<SequentialFileReader>&& _file,
+1 -1
View File
@@ -1167,7 +1167,7 @@ TEST_P(StreamingCompressionTest, Basic) {
}
allocator->Deallocate((void*)output_buffer);
} while (remaining > 0);
std::string uncompressed_buffer;
std::string uncompressed_buffer = "";
int ret_val = 0;
size_t output_pos;
char* uncompressed_output_buffer = (char*)allocator->Allocate(kBlockSize);
+1 -1
View File
@@ -9,7 +9,7 @@
#include "db/log_writer.h"
#include <cstdint>
#include <stdint.h>
#include "file/writable_file_writer.h"
#include "rocksdb/env.h"
+2 -1
View File
@@ -9,7 +9,8 @@
#include "db/malloc_stats.h"
#include <cstring>
#include <string.h>
#include <memory>
#include "port/jemalloc_helper.h"
+1 -1
View File
@@ -58,7 +58,7 @@ class ManualCompactionTest : public testing::Test {
class DestroyAllCompactionFilter : public CompactionFilter {
public:
DestroyAllCompactionFilter() = default;
DestroyAllCompactionFilter() {}
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& existing_value,
std::string* /*new_value*/,
+9 -9
View File
@@ -1061,8 +1061,8 @@ static bool SaveValue(void* arg, const char* entry) {
merge_operator, s->key->user_key(),
MergeHelper::kPlainBaseValue, v, merge_context->GetOperands(),
s->logger, s->statistics, s->clock,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
s->value, s->columns);
/* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
}
} else if (s->value) {
s->value->assign(v.data(), v.size());
@@ -1114,8 +1114,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kWideBaseValue,
v, merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
}
} else if (s->value) {
Slice value_of_default;
@@ -1152,8 +1152,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kNoBaseValue,
merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
} else {
// We have found a final value (a base deletion) and have newer
// merge operands that we do not intend to merge. Nothing remains
@@ -1192,8 +1192,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kNoBaseValue,
merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
}
*(s->found_final_value) = true;
@@ -1362,7 +1362,7 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
range_indexes[num_keys++] = iter.index();
}
}
bloom_filter_->MayContain(num_keys, bloom_keys.data(), may_match.data());
bloom_filter_->MayContain(num_keys, &bloom_keys[0], &may_match[0]);
for (int i = 0; i < num_keys; ++i) {
if (!may_match[i]) {
temp_range.SkipIndex(range_indexes[i]);
+156 -22
View File
@@ -12,6 +12,7 @@
#include "db/blob/prefetch_buffer_collection.h"
#include "db/compaction/compaction_iteration_stats.h"
#include "db/dbformat.h"
#include "db/wide/wide_column_serialization.h"
#include "db/wide/wide_columns_helper.h"
#include "logging/logging.h"
#include "monitoring/perf_context_imp.h"
@@ -110,9 +111,9 @@ Status MergeHelper::TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, std::string* result,
Slice* result_operand, ValueType* result_type) {
SystemClock* clock, bool update_num_ops_stats, std::string* result,
Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
assert(result);
assert(result_type);
@@ -172,9 +173,9 @@ Status MergeHelper::TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, std::string* result_value,
PinnableWideColumns* result_entity) {
SystemClock* clock, bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
assert(result_value || result_entity);
assert(!result_value || !result_entity);
@@ -244,6 +245,141 @@ Status MergeHelper::TimedFullMergeImpl(
op_failure_scope, std::move(visitor));
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result,
Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
Slice entity_copy(entity);
WideColumns existing_columns;
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
existing_value = std::move(existing_columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result, Slice* result_operand,
ValueType* result_type, MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
Slice entity_copy(entity);
WideColumns existing_columns;
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
existing_value = std::move(existing_columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
// PRE: iter points to the first merge type entry
// POST: iter points to the first entry beyond the merge process (or the end)
// keys_, operands_ are updated to reflect the merge result.
@@ -292,9 +428,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
Status s = ParseInternalKey(original_key, &orig_ikey, allow_data_in_errors);
assert(s.ok());
if (!s.ok()) {
return s;
}
if (!s.ok()) return s;
assert(kTypeMerge == orig_ikey.type);
@@ -383,14 +517,14 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_,
clock_, /* update_num_ops_stats */ false,
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
} else if (ikey.type == kTypeValue) {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kPlainBaseValue,
iter->value(), merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
} else if (ikey.type == kTypeBlobIndex) {
BlobIndex blob_index;
@@ -423,20 +557,20 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kPlainBaseValue,
blob_value, merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
} else if (ikey.type == kTypeWideColumnEntity) {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kWideBaseValue,
iter->value(), merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
} else {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_,
clock_, /* update_num_ops_stats */ false,
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
}
// We store the result in keys_.back() and operands_.back()
@@ -578,9 +712,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
MergeOperator::OpFailureScope op_failure_scope;
s = TimedFullMerge(user_merge_operator_, orig_ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_, clock_,
/* update_num_ops_stats */ false, &op_failure_scope,
&merge_result,
/* result_operand */ nullptr, &merge_result_type);
/* update_num_ops_stats */ false, &merge_result,
/* result_operand */ nullptr, &merge_result_type,
&op_failure_scope);
if (s.ok()) {
// The original key encountered
// We are certain that keys_ is not empty here (see assertions couple of
+46 -52
View File
@@ -12,7 +12,6 @@
#include "db/merge_context.h"
#include "db/range_del_aggregator.h"
#include "db/snapshot_checker.h"
#include "db/wide/wide_column_serialization.h"
#include "rocksdb/compaction_filter.h"
#include "rocksdb/env.h"
#include "rocksdb/merge_operator.h"
@@ -61,73 +60,74 @@ class MergeHelper {
struct WideBaseValueTag {};
static constexpr WideBaseValueTag kWideBaseValue{};
template <typename... ResultTs>
// Variants that expose the merge result directly (in serialized form for wide
// columns) as well as its value type. Used by iterator and compaction.
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope,
ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
std::string* result, Slice* result_operand,
ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
template <typename... ResultTs>
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
template <typename... ResultTs>
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
Slice entity_copy(entity);
WideColumns existing_columns;
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result, Slice* result_operand,
ValueType* result_type, MergeOperator::OpFailureScope* op_failure_scope);
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
// Variants that expose the merge result translated to the form requested by
// the client. (For example, if the result is a wide-column structure but the
// client requested the results in plain-value form, the value of the default
// column is returned.) Used by point lookups.
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
existing_value = std::move(existing_columns);
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
template <typename... ResultTs>
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, WideBaseValueTag,
const WideColumns& columns,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope,
ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
// During compaction, merge entries until we hit
// - a corrupted key
@@ -271,27 +271,21 @@ class MergeHelper {
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, Visitor&& visitor);
// Variant that exposes the merge result directly (in serialized form for wide
// columns) as well as its value type. Used by iterator and compaction.
static Status TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, std::string* result,
Slice* result_operand, ValueType* result_type);
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
// Variant that exposes the merge result translated into the form requested by
// the client. (For example, if the result is a wide-column structure but the
// client requested the results in plain-value form, the value of the default
// column is returned.) Used by point lookups.
static Status TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope,
std::string* result_value, PinnableWideColumns* result_entity);
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
};
// MergeOutputIterator can be used to iterate over the result of a merge.
+8 -13
View File
@@ -3,7 +3,8 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#include <cassert>
#include <assert.h>
#include <iostream>
#include <memory>
@@ -86,9 +87,7 @@ class EnvMergeTest : public EnvWrapper {
static std::unique_ptr<EnvMergeTest> singleton_;
static EnvMergeTest* GetInstance() {
if (nullptr == singleton_) {
singleton_.reset(new EnvMergeTest);
}
if (nullptr == singleton_) singleton_.reset(new EnvMergeTest);
return singleton_.get();
}
};
@@ -146,7 +145,7 @@ class Counters {
assert(db_);
}
virtual ~Counters() = default;
virtual ~Counters() {}
// public interface of Counters.
// All four functions return false
@@ -195,7 +194,7 @@ class Counters {
std::cerr << "value corruption\n";
return false;
}
*value = DecodeFixed64(str.data());
*value = DecodeFixed64(&str[0]);
return true;
} else {
std::cerr << s.ToString() << std::endl;
@@ -221,18 +220,14 @@ class Counters {
uint64_t value = default_;
int result = get(key, &value);
assert(result);
if (result == 0) {
exit(1); // Disable unused variable warning.
}
if (result == 0) exit(1); // Disable unused variable warning.
return value;
}
void assert_add(const std::string& key, uint64_t value) {
int result = add(key, value);
assert(result);
if (result == 0) {
exit(1); // Disable unused variable warning.
}
if (result == 0) exit(1); // Disable unused variable warning.
}
};
@@ -501,7 +496,7 @@ void testSingleBatchSuccessiveMerge(DB* db, size_t max_num_merges,
std::string get_value_str;
ASSERT_OK(db->Get(ReadOptions(), key, &get_value_str));
assert(get_value_str.size() == sizeof(uint64_t));
uint64_t get_value = DecodeFixed64(get_value_str.data());
uint64_t get_value = DecodeFixed64(&get_value_str[0]);
ASSERT_EQ(get_value, num_merges * merge_value);
ASSERT_EQ(num_merge_operator_calls,
static_cast<size_t>((num_merges % (max_num_merges + 1))));
+4 -2
View File
@@ -7,8 +7,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <stdlib.h>
#include <algorithm>
#include <cstdlib>
#include <map>
#include <string>
#include <vector>
@@ -66,7 +68,7 @@ class ObsoleteFilesTest : public DBTestBase {
int log_cnt = 0;
int sst_cnt = 0;
int manifest_cnt = 0;
for (const auto& file : filenames) {
for (auto file : filenames) {
uint64_t number;
FileType type;
if (ParseFileName(file, &number, &type)) {
+4 -4
View File
@@ -28,7 +28,7 @@ void UpdateOptionsFiles(DB* db,
uint64_t number;
FileType type;
*options_files_count = 0;
for (const auto& filename : filenames) {
for (auto filename : filenames) {
if (ParseFileName(filename, &number, &type) && type == kOptionsFile) {
filename_history->insert(filename);
(*options_files_count)++;
@@ -44,16 +44,16 @@ void VerifyOptionsFileName(
EXPECT_OK(db->GetEnv()->GetChildren(db->GetName(), &filenames));
uint64_t number;
FileType type;
for (const auto& filename : filenames) {
for (auto filename : filenames) {
if (ParseFileName(filename, &number, &type) && type == kOptionsFile) {
current_filenames.insert(filename);
}
}
for (const auto& past_filename : past_filenames) {
for (auto past_filename : past_filenames) {
if (current_filenames.find(past_filename) != current_filenames.end()) {
continue;
}
for (const auto& filename : current_filenames) {
for (auto filename : current_filenames) {
ASSERT_GT(filename, past_filename);
}
}
+2 -2
View File
@@ -1050,7 +1050,7 @@ TEST_F(PerfContextTest, MergeOperandCount) {
std::vector<Status> statuses(num_keys);
db->MultiGet(ReadOptions(), db->DefaultColumnFamily(), num_keys,
key_slices.data(), results.data(), statuses.data());
&key_slices[0], &results[0], &statuses[0]);
for (size_t i = 0; i < num_keys; ++i) {
ASSERT_OK(statuses[i]);
@@ -1068,7 +1068,7 @@ TEST_F(PerfContextTest, MergeOperandCount) {
std::vector<Status> statuses(num_keys);
db->MultiGetEntity(ReadOptions(), db->DefaultColumnFamily(), num_keys,
key_slices.data(), results.data(), statuses.data());
&key_slices[0], &results[0], &statuses[0]);
for (size_t i = 0; i < num_keys; ++i) {
ASSERT_OK(statuses[i]);
+1 -1
View File
@@ -292,7 +292,7 @@ class TestPlainTableReader : public PlainTableReader {
table_properties_ = std::move(props);
}
~TestPlainTableReader() override = default;
~TestPlainTableReader() override {}
private:
bool MatchBloom(uint32_t hash) const override {
+8 -21
View File
@@ -89,12 +89,8 @@ class TestKeyComparator : public Comparator {
const TestKey* key_a = &kkey_a;
const TestKey* key_b = &kkey_b;
if (key_a->prefix != key_b->prefix) {
if (key_a->prefix < key_b->prefix) {
return -1;
}
if (key_a->prefix > key_b->prefix) {
return 1;
}
if (key_a->prefix < key_b->prefix) return -1;
if (key_a->prefix > key_b->prefix) return 1;
} else {
EXPECT_TRUE(key_a->prefix == key_b->prefix);
// note, both a and b could be prefix only
@@ -103,12 +99,8 @@ class TestKeyComparator : public Comparator {
EXPECT_TRUE(
(a.size() == sizeof(uint64_t) && b.size() == sizeof(TestKey)) ||
(b.size() == sizeof(uint64_t) && a.size() == sizeof(TestKey)));
if (a.size() < b.size()) {
return -1;
}
if (a.size() > b.size()) {
return 1;
}
if (a.size() < b.size()) return -1;
if (a.size() > b.size()) return 1;
} else {
// both a and b are prefix
if (a.size() == sizeof(uint64_t)) {
@@ -117,15 +109,9 @@ class TestKeyComparator : public Comparator {
// both a and b are whole key
EXPECT_TRUE(a.size() == sizeof(TestKey) && b.size() == sizeof(TestKey));
if (key_a->sorted < key_b->sorted) {
return -1;
}
if (key_a->sorted > key_b->sorted) {
return 1;
}
if (key_a->sorted == key_b->sorted) {
return 0;
}
if (key_a->sorted < key_b->sorted) return -1;
if (key_a->sorted > key_b->sorted) return 1;
if (key_a->sorted == key_b->sorted) return 0;
}
}
return 0;
@@ -906,3 +892,4 @@ int main(int argc, char** argv) {
}
#endif // GFLAGS
+1
View File
@@ -8,6 +8,7 @@
#include "db/compaction/compaction_iteration_stats.h"
#include "db/dbformat.h"
#include "db/pinned_iterators_manager.h"
#include "db/range_del_aggregator.h"
#include "db/range_tombstone_fragmenter.h"
#include "db/version_edit.h"
#include "rocksdb/comparator.h"
+2 -1
View File
@@ -378,7 +378,8 @@ TEST_P(RepairTestWithTimestamp, UnflushedSst) {
ColumnFamilyOptions cf_options(options);
std::vector<ColumnFamilyDescriptor> column_families;
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
column_families.push_back(
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
ASSERT_OK(DB::Open(options, dbname_, column_families, &handles_, &db_));
+1 -1
View File
@@ -85,7 +85,7 @@ TableCache::TableCache(const ImmutableOptions& ioptions,
}
}
TableCache::~TableCache() = default;
TableCache::~TableCache() {}
Status TableCache::GetTableReader(
const ReadOptions& ro, const FileOptions& file_options,
+2 -7
View File
@@ -98,13 +98,8 @@ class UserKeyTablePropertiesCollectorFactory
TablePropertiesCollectorFactory::Context context;
context.column_family_id = column_family_id;
context.level_at_creation = level_at_creation;
TablePropertiesCollector* collector =
user_collector_factory_->CreateTablePropertiesCollector(context);
if (collector) {
return new UserKeyTablePropertiesCollector(collector);
} else {
return nullptr;
}
return new UserKeyTablePropertiesCollector(
user_collector_factory_->CreateTablePropertiesCollector(context));
}
virtual const char* Name() const override {
+2 -1
View File
@@ -180,6 +180,7 @@ class RegularKeysStartWithAInternal : public IntTblPropCollector {
uint64_t /* block_compressed_bytes_fast */,
uint64_t /* block_compressed_bytes_slow */) override {
// Nothing to do.
return;
}
UserCollectedProperties GetReadableProperties() const override {
@@ -226,7 +227,7 @@ class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
public:
explicit FlushBlockEveryThreePolicyFactory() = default;
explicit FlushBlockEveryThreePolicyFactory() {}
const char* Name() const override {
return "FlushBlockEveryThreePolicyFactory";
+1
View File
@@ -484,6 +484,7 @@ void VersionEdit::EncodeFileBoundaries(std::string* dst,
StripTimestampFromInternalKey(&largest_buf, meta.largest.Encode(), ts_sz);
PutLengthPrefixedSlice(dst, smallest_buf);
PutLengthPrefixedSlice(dst, largest_buf);
return;
};
Status VersionEdit::DecodeFrom(const Slice& src) {
+1 -3
View File
@@ -562,9 +562,7 @@ TEST_F(VersionEditTest, AddWalDebug) {
std::stringstream ss;
ss << "{\"LogNumber\": " << kLogNumbers[i] << ", "
<< "\"SyncedSizeInBytes\": " << kSizeInBytes[i] << "}";
if (i < n - 1) {
ss << ", ";
}
if (i < n - 1) ss << ", ";
expected_json += ss.str();
}
expected_json += "], \"ColumnFamily\": 0}";
+18 -23
View File
@@ -122,9 +122,7 @@ Status OverlapWithIterator(const Comparator* ucmp,
ParsedInternalKey seek_result;
Status s = ParseInternalKey(iter->key(), &seek_result,
false /* log_err_key */); // TODO
if (!s.ok()) {
return s;
}
if (!s.ok()) return s;
if (ucmp->CompareWithoutTimestamp(seek_result.user_key, largest_user_key) <=
0) {
@@ -837,9 +835,9 @@ Version::~Version() {
assert(cfd_ != nullptr);
uint32_t path_id = f->fd.GetPathId();
assert(path_id < cfd_->ioptions()->cf_paths.size());
vset_->obsolete_files_.emplace_back(
f, cfd_->ioptions()->cf_paths[path_id].path,
cfd_->GetFileMetadataCacheReservationManager());
vset_->obsolete_files_.push_back(
ObsoleteFileInfo(f, cfd_->ioptions()->cf_paths[path_id].path,
cfd_->GetFileMetadataCacheReservationManager()));
}
}
}
@@ -2536,8 +2534,8 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
*status = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
merge_context->GetOperands(), info_log_, db_statistics_, clock_,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
value ? value->GetSelf() : nullptr, columns);
/* update_num_ops_stats */ true, value ? value->GetSelf() : nullptr,
columns, /* op_failure_scope */ nullptr);
if (status->ok()) {
if (LIKELY(value != nullptr)) {
value->PinSelf();
@@ -2782,8 +2780,9 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
*status = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
iter->merge_context.GetOperands(), info_log_, db_statistics_, clock_,
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
iter->value ? iter->value->GetSelf() : nullptr, iter->columns);
/* update_num_ops_stats */ true,
iter->value ? iter->value->GetSelf() : nullptr, iter->columns,
/* op_failure_scope */ nullptr);
if (LIKELY(iter->value != nullptr)) {
iter->value->PinSelf();
range->AddValueSize(iter->value->size());
@@ -3067,7 +3066,9 @@ void VersionStorageInfo::PrepareForVersionAppend(
GenerateFileIndexer();
GenerateLevelFilesBrief();
GenerateLevel0NonOverlapping();
GenerateBottommostFiles();
if (!immutable_options.allow_ingest_behind) {
GenerateBottommostFiles();
}
GenerateFileLocationIndex();
}
@@ -3100,9 +3101,7 @@ bool Version::MaybeInitializeFileMetaData(const ReadOptions& read_options,
file_meta->fd.GetNumber(), s.ToString().c_str());
return false;
}
if (tp.get() == nullptr) {
return false;
}
if (tp.get() == nullptr) return false;
file_meta->num_entries = tp->num_entries;
file_meta->num_deletions = tp->num_deletions;
file_meta->raw_value_size = tp->raw_value_size;
@@ -4516,9 +4515,7 @@ const char* VersionStorageInfo::LevelSummary(
for (int i = 0; i < num_levels(); i++) {
int sz = sizeof(scratch->buffer) - len;
int ret = snprintf(scratch->buffer + len, sz, "%d ", int(files_[i].size()));
if (ret < 0 || ret >= sz) {
break;
}
if (ret < 0 || ret >= sz) break;
len += ret;
}
if (len > 0) {
@@ -4548,9 +4545,7 @@ const char* VersionStorageInfo::LevelFileSummary(FileSummaryStorage* scratch,
"#%" PRIu64 "(seq=%" PRIu64 ",sz=%s,%d) ",
f->fd.GetNumber(), f->fd.smallest_seqno, sztxt,
static_cast<int>(f->being_compacted));
if (ret < 0 || ret >= sz) {
break;
}
if (ret < 0 || ret >= sz) break;
len += ret;
}
// overwrite the last space (only if files_[level].size() is non-zero)
@@ -5389,9 +5384,9 @@ Status VersionSet::ProcessManifestWrites(
}
for (const auto* cfd : *column_family_set_) {
assert(curr_state.find(cfd->GetID()) == curr_state.end());
curr_state.emplace(
curr_state.emplace(std::make_pair(
cfd->GetID(),
MutableCFState(cfd->GetLogNumber(), cfd->GetFullHistoryTsLow()));
MutableCFState(cfd->GetLogNumber(), cfd->GetFullHistoryTsLow())));
}
for (const auto& wal : wals_.GetWals()) {
@@ -7282,7 +7277,7 @@ ReactiveVersionSet::ReactiveVersionSet(
/*db_session_id*/ "", /*daily_offpeak_time_utc*/ "",
/*error_handler=*/nullptr) {}
ReactiveVersionSet::~ReactiveVersionSet() = default;
ReactiveVersionSet::~ReactiveVersionSet() {}
Status ReactiveVersionSet::Recover(
const std::vector<ColumnFamilyDescriptor>& column_families,
-6
View File
@@ -490,12 +490,6 @@ class VersionStorageInfo {
files_marked_for_periodic_compaction_.emplace_back(level, f);
}
// REQUIRES: PrepareForVersionAppend has been called
const autovector<std::pair<int, FileMetaData*>>& BottommostFiles() const {
assert(finalized_);
return bottommost_files_;
}
// REQUIRES: ComputeCompactionScore has been called
// REQUIRES: DB mutex held during access
const autovector<std::pair<int, FileMetaData*>>&
+5 -54
View File
@@ -34,7 +34,7 @@ class GenerateLevelFilesBriefTest : public testing::Test {
LevelFilesBrief file_level_;
Arena arena_;
GenerateLevelFilesBriefTest() = default;
GenerateLevelFilesBriefTest() {}
~GenerateLevelFilesBriefTest() override {
for (size_t i = 0; i < files_.size(); i++) {
@@ -213,7 +213,7 @@ class VersionStorageInfoTest : public VersionStorageInfoTestBase {
public:
VersionStorageInfoTest() : VersionStorageInfoTestBase(BytewiseComparator()) {}
~VersionStorageInfoTest() override = default;
~VersionStorageInfoTest() override {}
};
TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) {
@@ -520,55 +520,6 @@ TEST_F(VersionStorageInfoTest, EstimateLiveDataSize2) {
ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize());
}
TEST_F(VersionStorageInfoTest, SingleLevelBottommostData) {
// In case of a single level, the oldest L0 file is bottommost. This could be
// improved in case the L0 files cover disjoint key-ranges.
Add(0 /* level */, 1U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 2U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 3U /* file_number */, "0" /* smallest */,
"9" /* largest */, 1U /* file_size */);
UpdateVersionStorageInfo();
ASSERT_EQ(1, vstorage_.BottommostFiles().size());
ASSERT_EQ(0, vstorage_.BottommostFiles()[0].first);
ASSERT_EQ(3U, vstorage_.BottommostFiles()[0].second->fd.GetNumber());
}
TEST_F(VersionStorageInfoTest, MultiLevelBottommostData) {
// In case of multiple levels, the oldest file for a key-range from each L1+
// level is bottommost. This could be improved in case an L0 file contains the
// oldest data for some range of keys.
Add(0 /* level */, 1U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 2U /* file_number */, "0" /* smallest */,
"9" /* largest */, 1U /* file_size */);
Add(1 /* level */, 3U /* file_number */, "A" /* smallest */,
"D" /* largest */, 1U /* file_size */);
Add(2 /* level */, 4U /* file_number */, "E" /* smallest */,
"H" /* largest */, 1U /* file_size */);
Add(2 /* level */, 5U /* file_number */, "I" /* smallest */,
"L" /* largest */, 1U /* file_size */);
UpdateVersionStorageInfo();
autovector<std::pair<int, FileMetaData*>> bottommost_files =
vstorage_.BottommostFiles();
std::sort(bottommost_files.begin(), bottommost_files.end(),
[](const std::pair<int, FileMetaData*>& lhs,
const std::pair<int, FileMetaData*>& rhs) {
assert(lhs.second);
assert(rhs.second);
return lhs.second->fd.GetNumber() < rhs.second->fd.GetNumber();
});
ASSERT_EQ(3, bottommost_files.size());
ASSERT_EQ(3U, bottommost_files[0].second->fd.GetNumber());
ASSERT_EQ(4U, bottommost_files[1].second->fd.GetNumber());
ASSERT_EQ(5U, bottommost_files[2].second->fd.GetNumber());
}
TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
// Two files that overlap at the range deletion tombstone sentinel.
Add(1, 1U, {"a", 0, kTypeValue},
@@ -977,7 +928,7 @@ class VersionStorageInfoTimestampTest : public VersionStorageInfoTestBase {
VersionStorageInfoTimestampTest()
: VersionStorageInfoTestBase(test::BytewiseComparatorWithU64TsWrapper()) {
}
~VersionStorageInfoTimestampTest() override = default;
~VersionStorageInfoTimestampTest() override {}
std::string Timestamp(uint64_t ts) const {
std::string ret;
PutFixed64(&ret, ts);
@@ -1031,7 +982,7 @@ class FindLevelFileTest : public testing::Test {
FindLevelFileTest() : disjoint_sorted_files_(true) {}
~FindLevelFileTest() override = default;
~FindLevelFileTest() override {}
void LevelFileInit(size_t num = 0) {
char* mem = arena_.AllocateAligned(num * sizeof(FdWithKeyRange));
@@ -3711,7 +3662,7 @@ class ChargeFileMetadataTestWithParam
: public ChargeFileMetadataTest,
public testing::WithParamInterface<CacheEntryRoleOptions::Decision> {
public:
ChargeFileMetadataTestWithParam() = default;
ChargeFileMetadataTestWithParam() {}
};
INSTANTIATE_TEST_CASE_P(
+11 -11
View File
@@ -94,7 +94,7 @@ TEST_F(DBWideBasicTest, PutEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
keys.data(), values.data(), statuses.data());
&keys[0], &values[0], &statuses[0]);
ASSERT_OK(statuses[0]);
ASSERT_EQ(values[0], first_value_of_default_column);
@@ -114,7 +114,7 @@ TEST_F(DBWideBasicTest, PutEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
keys.data(), results.data(), statuses.data());
&keys[0], &results[0], &statuses[0]);
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_columns);
@@ -398,8 +398,8 @@ TEST_F(DBWideBasicTest, MultiCFMultiGetEntity) {
std::array<PinnableWideColumns, num_keys> results;
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), num_keys, column_families.data(),
keys.data(), results.data(), statuses.data());
db_->MultiGetEntity(ReadOptions(), num_keys, &column_families[0], &keys[0],
&results[0], &statuses[0]);
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_columns);
@@ -642,7 +642,7 @@ TEST_F(DBWideBasicTest, MergePlainKeyValue) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
keys.data(), results.data(), statuses.data());
&keys[0], &results[0], &statuses[0]);
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), expected_first_columns);
@@ -822,7 +822,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
keys.data(), values.data(), statuses.data());
&keys[0], &values[0], &statuses[0]);
ASSERT_EQ(values[0], first_expected_default);
ASSERT_OK(statuses[0]);
@@ -839,7 +839,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
keys.data(), results.data(), statuses.data());
&keys[0], &results[0], &statuses[0]);
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_expected_columns);
@@ -900,7 +900,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
first_key, merge_operands.data(),
first_key, &merge_operands[0],
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -913,7 +913,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
second_key, merge_operands.data(),
second_key, &merge_operands[0],
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -933,7 +933,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
first_key, merge_operands.data(),
first_key, &merge_operands[0],
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -945,7 +945,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
second_key, merge_operands.data(),
second_key, &merge_operands[0],
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
+11 -11
View File
@@ -233,9 +233,9 @@ WriteBatch& WriteBatch::operator=(WriteBatch&& src) {
return *this;
}
WriteBatch::~WriteBatch() = default;
WriteBatch::~WriteBatch() {}
WriteBatch::Handler::~Handler() = default;
WriteBatch::Handler::~Handler() {}
void WriteBatch::Handler::LogData(const Slice& /*blob*/) {
// If the user has not specified something to do with blobs, then we ignore
@@ -741,7 +741,7 @@ SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) {
}
void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) {
EncodeFixed64(b->rep_.data(), seq);
EncodeFixed64(&b->rep_[0], seq);
}
size_t WriteBatchInternal::GetFirstOffset(WriteBatch* /*b*/) {
@@ -1856,9 +1856,7 @@ class MemTableInserter : public WriteBatch::Handler {
}
void DecrementProtectionInfoIdxForTryAgain() {
if (prot_info_ != nullptr) {
--prot_info_idx_;
}
if (prot_info_ != nullptr) --prot_info_idx_;
}
void ResetProtectionInfo() {
@@ -2545,8 +2543,9 @@ class MemTableInserter : public WriteBatch::Handler {
WideColumnsHelper::GetDefaultColumn(columns), {value},
moptions->info_log, moptions->statistics,
SystemClock::Default().get(),
/* update_num_ops_stats */ false, /* op_failure_scope */ nullptr,
&new_value, /* result_operand */ nullptr, &new_value_type);
/* update_num_ops_stats */ false, &new_value,
/* result_operand */ nullptr, &new_value_type,
/* op_failure_scope */ nullptr);
} else {
// `op_failure_scope` (an output parameter) is not provided (set to
// nullptr) since a failure must be propagated regardless of its
@@ -2555,8 +2554,9 @@ class MemTableInserter : public WriteBatch::Handler {
merge_operator, key, MergeHelper::kWideBaseValue, columns,
{value}, moptions->info_log, moptions->statistics,
SystemClock::Default().get(),
/* update_num_ops_stats */ false, /* op_failure_scope */ nullptr,
&new_value, /* result_operand */ nullptr, &new_value_type);
/* update_num_ops_stats */ false, &new_value,
/* result_operand */ nullptr, &new_value_type,
/* op_failure_scope */ nullptr);
}
if (!merge_status.ok()) {
@@ -3018,7 +3018,7 @@ class ProtectionInfoUpdater : public WriteBatch::Handler {
explicit ProtectionInfoUpdater(WriteBatch::ProtectionInfo* prot_info)
: prot_info_(prot_info) {}
~ProtectionInfoUpdater() override = default;
~ProtectionInfoUpdater() override {}
Status PutCF(uint32_t cf, const Slice& key, const Slice& val) override {
return UpdateProtInfo(cf, key, val, kTypeValue);
+4 -3
View File
@@ -64,7 +64,7 @@ class MockWriteCallback : public WriteCallback {
bool allow_batching_ = false;
std::atomic<bool> was_called_{false};
MockWriteCallback() = default;
MockWriteCallback() {}
MockWriteCallback(const MockWriteCallback& other) {
should_fail_ = other.should_fail_;
@@ -111,7 +111,7 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
WriteOP(bool should_fail = false) { callback_.should_fail_ = should_fail; }
void Put(const string& key, const string& val) {
kvs_.emplace_back(key, val);
kvs_.push_back(std::make_pair(key, val));
ASSERT_OK(write_batch_.Put(key, val));
}
@@ -176,7 +176,8 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
DBOptions db_options(options);
ColumnFamilyOptions cf_options(options);
std::vector<ColumnFamilyDescriptor> column_families;
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
column_families.push_back(
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
std::vector<ColumnFamilyHandle*> handles;
auto open_s = DBImpl::Open(db_options, dbname, column_families, &handles,
&db, seq_per_batch_, true /* batch_per_txn */);
+2 -2
View File
@@ -13,8 +13,8 @@
namespace ROCKSDB_NAMESPACE {
class BatchedOpsStressTest : public StressTest {
public:
BatchedOpsStressTest() = default;
virtual ~BatchedOpsStressTest() = default;
BatchedOpsStressTest() {}
virtual ~BatchedOpsStressTest() {}
bool IsStateTracked() const override { return false; }
+2 -2
View File
@@ -16,7 +16,7 @@ class CfConsistencyStressTest : public StressTest {
public:
CfConsistencyStressTest() : batch_id_(0) {}
~CfConsistencyStressTest() override = default;
~CfConsistencyStressTest() override {}
bool IsStateTracked() const override { return false; }
@@ -232,7 +232,7 @@ class CfConsistencyStressTest : public StressTest {
}
db_->MultiGet(readoptionscopy, cfh, num_keys, keys.data(), values.data(),
statuses.data());
for (const auto& s : statuses) {
for (auto s : statuses) {
if (s.ok()) {
// found case
thread->stats.AddGets(1, 1);
+3
View File
@@ -210,6 +210,9 @@ void CompressedCacheSetCapacityThread(void* v) {
(double)thread->rand.Uniform(
FLAGS_compressed_secondary_cache_ratio * 100) /
100;
if (new_comp_cache_ratio == 0.0) {
new_comp_cache_ratio = 0.05;
}
fprintf(stderr, "New comp cache ratio = %f\n", new_comp_cache_ratio);
s = UpdateTieredCache(block_cache, /*capacity*/ -1,
-1
View File
@@ -313,7 +313,6 @@ DECLARE_uint32(memtable_protection_bytes_per_key);
DECLARE_uint32(block_protection_bytes_per_key);
DECLARE_uint64(user_timestamp_size);
DECLARE_bool(persist_user_defined_timestamps);
DECLARE_string(secondary_cache_uri);
DECLARE_int32(secondary_cache_fault_one_in);
-4
View File
@@ -1025,10 +1025,6 @@ DEFINE_uint64(user_timestamp_size, 0,
"Number of bytes for a user-defined timestamp. Currently, only "
"8-byte is supported");
DEFINE_bool(persist_user_defined_timestamps, true,
"Flag to indicate whether user-defined timestamps will be persisted"
" during Flush");
DEFINE_int32(open_metadata_write_fault_one_in, 0,
"On non-zero, enables fault injection on file metadata write "
"during DB reopen.");
+1 -1
View File
@@ -67,7 +67,7 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
std::string id(24U, '\0');
Slice result;
for (;;) {
s = reader->Read(id.size(), opts, &result, id.data(), /*dbg*/ nullptr);
s = reader->Read(id.size(), opts, &result, &id[0], /*dbg*/ nullptr);
if (!s.ok()) {
fprintf(stderr, "Error reading unique id file: %s\n",
s.ToString().c_str());
+4 -25
View File
@@ -430,13 +430,6 @@ Status StressTest::AssertSame(DB* db, ColumnFamilyHandle* cf,
PinnableSlice v;
s = db->Get(ropt, cf, snap_state.key, &v);
if (!s.ok() && !s.IsNotFound()) {
// When `persist_user_defined_timestamps` is false, a repeated read with
// both a read timestamp and an explicitly taken snapshot cannot guarantee
// consistent result all the time. When it cannot return consistent result,
// it will return an `InvalidArgument` status.
if (s.IsInvalidArgument() && !FLAGS_persist_user_defined_timestamps) {
return Status::OK();
}
return s;
}
if (snap_state.status != s) {
@@ -1791,7 +1784,7 @@ Status StressTest::TestBackupRestore(
// `ListColumnFamilies` to get names because it won't necessarily give
// the same order as `column_family_names_`.
assert(FLAGS_clear_column_family_one_in == 0);
for (const auto& name : column_family_names_) {
for (auto name : column_family_names_) {
cf_descriptors.emplace_back(name, ColumnFamilyOptions(restore_options));
}
if (inplace_not_restore) {
@@ -2675,8 +2668,6 @@ void StressTest::PrintEnv() const {
static_cast<int>(FLAGS_fail_if_options_file_error));
fprintf(stdout, "User timestamp size bytes : %d\n",
static_cast<int>(FLAGS_user_timestamp_size));
fprintf(stdout, "Persist user defined timestamps : %d\n",
FLAGS_persist_user_defined_timestamps);
fprintf(stdout, "WAL compression : %s\n",
FLAGS_wal_compression.c_str());
fprintf(stdout, "Try verify sst unique id : %d\n",
@@ -2771,12 +2762,12 @@ void StressTest::Open(SharedState* shared, bool reopen) {
if (sorted_cfn != existing_column_families) {
fprintf(stderr, "Expected column families differ from the existing:\n");
fprintf(stderr, "Expected: {");
for (const auto& cf : sorted_cfn) {
for (auto cf : sorted_cfn) {
fprintf(stderr, "%s ", cf.c_str());
}
fprintf(stderr, "}\n");
fprintf(stderr, "Existing: {");
for (const auto& cf : existing_column_families) {
for (auto cf : existing_column_families) {
fprintf(stderr, "%s ", cf.c_str());
}
fprintf(stderr, "}\n");
@@ -2784,7 +2775,7 @@ void StressTest::Open(SharedState* shared, bool reopen) {
assert(sorted_cfn == existing_column_families);
}
std::vector<ColumnFamilyDescriptor> cf_descriptors;
for (const auto& name : column_family_names_) {
for (auto name : column_family_names_) {
if (name != kDefaultColumnFamilyName) {
new_column_family_name_ =
std::max(new_column_family_name_.load(), std::stoi(name) + 1);
@@ -3096,11 +3087,6 @@ bool StressTest::MaybeUseOlderTimestampForPointLookup(ThreadState* thread,
return false;
}
if (!FLAGS_persist_user_defined_timestamps) {
// Not read with older timestamps to avoid get InvalidArgument.
return false;
}
assert(thread);
if (!thread->rand.OneInOpt(3)) {
return false;
@@ -3130,11 +3116,6 @@ void StressTest::MaybeUseOlderTimestampForRangeScan(ThreadState* thread,
return;
}
if (!FLAGS_persist_user_defined_timestamps) {
// Not read with older timestamps to avoid get InvalidArgument.
return;
}
assert(thread);
if (!thread->rand.OneInOpt(3)) {
return;
@@ -3194,8 +3175,6 @@ void CheckAndSetOptionsForUserTimestamp(Options& options) {
exit(1);
}
options.comparator = cmp;
options.persist_user_defined_timestamps =
FLAGS_persist_user_defined_timestamps;
}
bool InitializeOptionsFromFile(Options& options) {
+1 -1
View File
@@ -185,7 +185,7 @@ ExpectedStateManager::ExpectedStateManager(size_t max_key,
num_column_families_(num_column_families),
latest_(nullptr) {}
ExpectedStateManager::~ExpectedStateManager() = default;
ExpectedStateManager::~ExpectedStateManager() {}
const std::string FileExpectedStateManager::kLatestBasename = "LATEST";
const std::string FileExpectedStateManager::kStateFilenameSuffix = ".state";
+3 -3
View File
@@ -150,7 +150,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodePrimaryKey(uint32_t a) {
PutFixed32(&ret, kPrimaryIndexId);
PutFixed32(&ret, a);
char* const buf = ret.data();
char* const buf = &ret[0];
std::reverse(buf, buf + sizeof(kPrimaryIndexId));
std::reverse(buf + sizeof(kPrimaryIndexId),
buf + sizeof(kPrimaryIndexId) + sizeof(a));
@@ -162,7 +162,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodeSecondaryKey(uint32_t c) {
PutFixed32(&ret, kSecondaryIndexId);
PutFixed32(&ret, c);
char* const buf = ret.data();
char* const buf = &ret[0];
std::reverse(buf, buf + sizeof(kSecondaryIndexId));
std::reverse(buf + sizeof(kSecondaryIndexId),
buf + sizeof(kSecondaryIndexId) + sizeof(c));
@@ -176,7 +176,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodeSecondaryKey(uint32_t c,
PutFixed32(&ret, c);
PutFixed32(&ret, a);
char* const buf = ret.data();
char* const buf = &ret[0];
std::reverse(buf, buf + sizeof(kSecondaryIndexId));
std::reverse(buf + sizeof(kSecondaryIndexId),
buf + sizeof(kSecondaryIndexId) + sizeof(c));
+4 -4
View File
@@ -17,9 +17,9 @@
namespace ROCKSDB_NAMESPACE {
class NonBatchedOpsStressTest : public StressTest {
public:
NonBatchedOpsStressTest() = default;
NonBatchedOpsStressTest() {}
virtual ~NonBatchedOpsStressTest() = default;
virtual ~NonBatchedOpsStressTest() {}
void VerifyDb(ThreadState* thread) const override {
// This `ReadOptions` is for validation purposes. Ignore
@@ -624,7 +624,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (!shared->AllowsOverwrite(rand_key) &&
shared->Exists(column_family, rand_key)) {
// Just do read your write checks for keys that allow overwrites.
ryw_expected_values.emplace_back(std::nullopt);
ryw_expected_values.push_back(std::nullopt);
continue;
}
// With a 1 in 10 probability, insert the just added key in the batch
@@ -667,7 +667,7 @@ class NonBatchedOpsStressTest : public StressTest {
thread->shared->SafeTerminate();
}
} else {
ryw_expected_values.emplace_back(std::nullopt);
ryw_expected_values.push_back(std::nullopt);
}
}
}
+1 -1
View File
@@ -504,7 +504,7 @@ EnvWrapper::EnvWrapper(const std::shared_ptr<Env>& t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::~EnvWrapper() = default;
EnvWrapper::~EnvWrapper() {}
Status EnvWrapper::PrepareOptions(const ConfigOptions& options) {
target_.Prepare();
Vendored
+9 -9
View File
@@ -359,7 +359,7 @@ class LegacyFileSystemWrapper : public FileSystem {
public:
// Initialize an EnvWrapper that delegates all calls to *t
explicit LegacyFileSystemWrapper(Env* t) : target_(t) {}
~LegacyFileSystemWrapper() override = default;
~LegacyFileSystemWrapper() override {}
static const char* kClassName() { return "LegacyFileSystem"; }
const char* Name() const override { return kClassName(); }
@@ -624,7 +624,7 @@ Env::Env(const std::shared_ptr<FileSystem>& fs,
const std::shared_ptr<SystemClock>& clock)
: thread_status_updater_(nullptr), file_system_(fs), system_clock_(clock) {}
Env::~Env() = default;
Env::~Env() {}
Status Env::NewLogger(const std::string& fname,
std::shared_ptr<Logger>* result) {
@@ -797,7 +797,7 @@ std::string Env::GenerateUniqueId() {
// Use 36 character format of RFC 4122
result.resize(36U);
char* buf = result.data();
char* buf = &result[0];
PutBaseChars<16>(&buf, 8, upper >> 32, /*!uppercase*/ false);
*(buf++) = '-';
PutBaseChars<16>(&buf, 4, upper >> 16, /*!uppercase*/ false);
@@ -817,15 +817,15 @@ std::string Env::GenerateUniqueId() {
return result;
}
SequentialFile::~SequentialFile() = default;
SequentialFile::~SequentialFile() {}
RandomAccessFile::~RandomAccessFile() = default;
RandomAccessFile::~RandomAccessFile() {}
WritableFile::~WritableFile() = default;
WritableFile::~WritableFile() {}
MemoryMappedFileBuffer::~MemoryMappedFileBuffer() = default;
MemoryMappedFileBuffer::~MemoryMappedFileBuffer() {}
Logger::~Logger() = default;
Logger::~Logger() {}
Status Logger::Close() {
if (!closed_) {
@@ -838,7 +838,7 @@ Status Logger::Close() {
Status Logger::CloseImpl() { return Status::NotSupported(); }
FileLock::~FileLock() = default;
FileLock::~FileLock() {}
void LogFlush(Logger* info_log) {
if (info_log) {
+1 -1
View File
@@ -342,7 +342,7 @@ TEST_P(EnvMoreTestWithParam, GetChildren) {
ASSERT_OK(env_->GetChildrenFileAttributes(test_dir_, &childAttr));
ASSERT_EQ(3U, children.size());
ASSERT_EQ(3U, childAttr.size());
for (const auto& each : children) {
for (auto each : children) {
env_->DeleteDir(test_dir_ + "/" + each).PermitUncheckedError();
} // necessary for default POSIX env
+2 -3
View File
@@ -7,11 +7,10 @@
#include "env/env_chroot.h"
#include <errno.h> // errno
#include <stdlib.h> // realpath, free
#include <unistd.h> // geteuid
#include <cerrno> // errno
#include <cstdlib> // realpath, free
#include "env/composite_env_wrapper.h"
#include "env/fs_remap.h"
#include "rocksdb/utilities/options_type.h"
+2 -2
View File
@@ -827,7 +827,7 @@ Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char* data,
AllocateScratch(scratch);
// Encrypt individual blocks.
while (true) {
while (1) {
char* block = data;
size_t n = std::min(dataSize, blockSize - blockOffset);
if (n != blockSize) {
@@ -871,7 +871,7 @@ Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char* data,
AllocateScratch(scratch);
// Decrypt individual blocks.
while (true) {
while (1) {
char* block = data;
size_t n = std::min(dataSize, blockSize - blockOffset);
if (n != blockSize) {
+7 -9
View File
@@ -14,21 +14,19 @@
#ifndef ROCKSDB_NO_DYNAMIC_EXTENSION
#include <dlfcn.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <cerrno>
#if defined(ROCKSDB_IOURING_PRESENT)
#include <liburing.h>
#endif
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID)
#include <sys/statfs.h>
#endif
@@ -38,10 +36,10 @@
#if defined(ROCKSDB_IOURING_PRESENT)
#include <sys/uio.h>
#endif
#include <time.h>
#include <unistd.h>
#include <algorithm>
#include <ctime>
// Get nano time includes
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_GNU_KFREEBSD)
#elif defined(__MACH__)
@@ -201,7 +199,7 @@ class PosixClock : public SystemClock {
std::string dummy;
dummy.reserve(maxsize);
dummy.resize(maxsize);
char* p = dummy.data();
char* p = &dummy[0];
port::LocalTimeR(&seconds, &t);
snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", t.tm_year + 1900,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+3 -4
View File
@@ -26,14 +26,13 @@
#ifdef OS_LINUX
#include <fcntl.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
#endif
#ifdef ROCKSDB_FALLOCATE_PRESENT
#include <cerrno>
#include <errno.h>
#endif
#include "db/db_impl/db_impl.h"
@@ -2956,7 +2955,7 @@ struct NoDuplicateMiniStressTest {
NoDuplicateMiniStressTest() { env = Env::Default(); }
virtual ~NoDuplicateMiniStressTest() = default;
virtual ~NoDuplicateMiniStressTest() {}
void Run() {
std::array<std::thread, kThreads> threads;
+2 -2
View File
@@ -22,9 +22,9 @@
namespace ROCKSDB_NAMESPACE {
FileSystem::FileSystem() = default;
FileSystem::FileSystem() {}
FileSystem::~FileSystem() = default;
FileSystem::~FileSystem() {}
static int RegisterBuiltinFileSystems(ObjectLibrary& library,
const std::string& /*arg*/) {
+7 -8
View File
@@ -13,17 +13,16 @@
#ifndef ROCKSDB_NO_DYNAMIC_EXTENSION
#include <dlfcn.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID)
#include <sys/statfs.h>
#include <sys/sysmacros.h>
@@ -31,9 +30,9 @@
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <algorithm>
#include <ctime>
// Get nano time includes
#if defined(OS_LINUX) || defined(OS_FREEBSD)
#elif defined(__MACH__)
@@ -144,7 +143,7 @@ class PosixFileSystem : public FileSystem {
const char* Name() const override { return kClassName(); }
const char* NickName() const override { return kDefaultName(); }
~PosixFileSystem() override = default;
~PosixFileSystem() override {}
bool IsInstanceOf(const std::string& name) const override {
if (name == "posix") {
return true;
+6 -9
View File
@@ -10,24 +10,23 @@
#ifdef ROCKSDB_LIB_IO_POSIX
#include "env/io_posix.h"
#include <errno.h>
#include <fcntl.h>
#include <algorithm>
#include <cerrno>
#if defined(OS_LINUX)
#include <linux/fs.h>
#ifndef FALLOC_FL_KEEP_SIZE
#include <linux/falloc.h>
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifdef OS_LINUX
#include <sys/statfs.h>
#include <sys/sysmacros.h>
@@ -438,7 +437,7 @@ void LogicalBlockSizeCache::UnrefAndTryRemoveCachedLogicalBlockSize(
size_t LogicalBlockSizeCache::GetLogicalBlockSize(const std::string& fname,
int fd) {
std::string dir = fname.substr(0, fname.find_last_of('/'));
std::string dir = fname.substr(0, fname.find_last_of("/"));
if (dir.empty()) {
dir = "/";
}
@@ -655,9 +654,7 @@ IOStatus PosixRandomAccessFile::MultiRead(FSReadRequest* reqs, size_t num_reqs,
size_t this_reqs = (num_reqs - reqs_off) + incomplete_rq_list.size();
// If requests exceed depth, split it into batches
if (this_reqs > kIoUringDepth) {
this_reqs = kIoUringDepth;
}
if (this_reqs > kIoUringDepth) this_reqs = kIoUringDepth;
assert(incomplete_rq_list.size() <= this_reqs);
for (size_t i = 0; i < this_reqs; i++) {
+1 -1
View File
@@ -483,7 +483,7 @@ class TestMemLogger : public Logger {
options_(options),
dbg_(dbg),
flush_pending_(false) {}
~TestMemLogger() override = default;
~TestMemLogger() override {}
void Flush() override {
if (flush_pending_) {
+6 -6
View File
@@ -37,28 +37,28 @@ TEST_F(MockEnvTest, Corrupt) {
Slice result;
std::unique_ptr<RandomAccessFile> rand_file;
ASSERT_OK(env_->NewRandomAccessFile(kFileName, &rand_file, soptions_));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, scratch.data()));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_EQ(result.compare(kGood), 0);
// Sync + corrupt => no change
ASSERT_OK(writable_file->Fsync());
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, scratch.data()));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_EQ(result.compare(kGood), 0);
// Add new data and corrupt it
ASSERT_OK(writable_file->Append(kCorrupted));
ASSERT_TRUE(writable_file->GetFileSize() == kGood.size() + kCorrupted.size());
result.clear();
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(), &result,
scratch.data()));
ASSERT_OK(
rand_file->Read(kGood.size(), kCorrupted.size(), &result, &(scratch[0])));
ASSERT_EQ(result.compare(kCorrupted), 0);
// Corrupted
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(), &result,
scratch.data()));
ASSERT_OK(
rand_file->Read(kGood.size(), kCorrupted.size(), &result, &(scratch[0])));
ASSERT_NE(result.compare(kCorrupted), 0);
}
+1 -1
View File
@@ -177,7 +177,7 @@ Status DeleteScheduler::CleanupDirectory(Env* env, SstFileManagerImpl* sfm,
Status DeleteScheduler::MarkAsTrash(const std::string& file_path,
std::string* trash_file) {
// Sanity check of the path
size_t idx = file_path.rfind('/');
size_t idx = file_path.rfind("/");
if (idx == std::string::npos || idx == file_path.size() - 1) {
return Status::InvalidArgument("file_path is corrupted");
}
+169 -233
View File
@@ -82,9 +82,9 @@ void FilePrefetchBuffer::CalculateOffsetAndLen(size_t alignment,
Status FilePrefetchBuffer::Read(const IOOptions& opts,
RandomAccessFileReader* reader,
uint64_t read_len, uint64_t chunk_len,
uint64_t start_offset, uint32_t index) {
uint64_t rounddown_start, uint32_t index) {
Slice result;
Status s = reader->Read(opts, start_offset + chunk_len, read_len, &result,
Status s = reader->Read(opts, rounddown_start + chunk_len, read_len, &result,
bufs_[index].buffer_.BufferStart() + chunk_len,
/*aligned_buf=*/nullptr);
#ifndef NDEBUG
@@ -102,15 +102,15 @@ Status FilePrefetchBuffer::Read(const IOOptions& opts,
RecordTick(stats_, PREFETCH_BYTES, read_len);
}
// Update the buffer offset and size.
bufs_[index].offset_ = start_offset;
bufs_[index].offset_ = rounddown_start;
bufs_[index].buffer_.Size(static_cast<size_t>(chunk_len) + result.size());
return s;
}
Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
RandomAccessFileReader* reader,
uint64_t read_len, uint64_t start_offset,
uint32_t index) {
uint64_t read_len,
uint64_t rounddown_start, uint32_t index) {
TEST_SYNC_POINT("FilePrefetchBuffer::ReadAsync");
// callback for async read request.
auto fp = std::bind(&FilePrefetchBuffer::PrefetchAsyncCallback, this,
@@ -118,7 +118,7 @@ Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
FSReadRequest req;
Slice result;
req.len = read_len;
req.offset = start_offset;
req.offset = rounddown_start;
req.result = result;
req.scratch = bufs_[index].buffer_.BufferStart();
bufs_[index].async_req_len_ = req.len;
@@ -129,7 +129,6 @@ Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
/*aligned_buf=*/nullptr);
req.status.PermitUncheckedError();
if (s.ok()) {
RecordTick(stats_, PREFETCH_BYTES, read_len);
bufs_[index].async_read_in_progress_ = true;
}
return s;
@@ -150,17 +149,19 @@ Status FilePrefetchBuffer::Prefetch(const IOOptions& opts,
}
size_t alignment = reader->file()->GetRequiredBufferAlignment();
uint64_t rounddown_offset = offset, roundup_end = 0, chunk_len = 0;
size_t read_len = 0;
size_t offset_ = static_cast<size_t>(offset);
uint64_t rounddown_offset = Rounddown(offset_, alignment);
uint64_t roundup_end = Roundup(offset_ + n, alignment);
uint64_t roundup_len = roundup_end - rounddown_offset;
assert(roundup_len >= alignment);
assert(roundup_len % alignment == 0);
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/true,
rounddown_offset, curr_, alignment, 0, n,
rounddown_offset, roundup_end, read_len, chunk_len);
uint64_t chunk_len = 0;
CalculateOffsetAndLen(alignment, offset, roundup_len, curr_,
true /*refit_tail*/, chunk_len);
size_t read_len = static_cast<size_t>(roundup_len - chunk_len);
Status s;
if (read_len > 0) {
s = Read(opts, reader, read_len, chunk_len, rounddown_offset, curr_);
}
Status s = Read(opts, reader, read_len, chunk_len, rounddown_offset, curr_);
if (usage_ == FilePrefetchBufferUsage::kTableOpenPrefetchTail && s.ok()) {
RecordInHistogram(stats_, TABLE_OPEN_PREFETCH_TAIL_READ_BYTES, read_len);
@@ -195,7 +196,7 @@ void FilePrefetchBuffer::CopyDataToBuffer(uint32_t src, uint64_t& offset,
// length > 0 indicates it has consumed all data from the src buffer and it
// still needs to read more other buffer.
if (length > 0) {
bufs_[src].ClearBuffer();
bufs_[src].buffer_.Clear();
}
}
@@ -265,35 +266,28 @@ void FilePrefetchBuffer::AbortAllIOs() {
// Clear the buffers if it contains outdated data. Outdated data can be
// because previous sequential reads were read from the cache instead of these
// buffer.
void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset, size_t length) {
void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset) {
uint32_t second = curr_ ^ 1;
if (IsBufferOutdated(offset, curr_)) {
bufs_[curr_].ClearBuffer();
bufs_[curr_].buffer_.Clear();
}
if (IsBufferOutdated(offset, second)) {
bufs_[second].ClearBuffer();
bufs_[second].buffer_.Clear();
}
{
// In case buffers do not align, reset second buffer if requested data needs
// to be read in second buffer.
// In case buffers do not align, reset second buffer. This can happen in
// case readahead_size is set.
if (!bufs_[second].async_read_in_progress_ &&
!bufs_[curr_].async_read_in_progress_) {
if (DoesBufferContainData(curr_)) {
if (bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize() !=
bufs_[second].offset_) {
if (DoesBufferContainData(second) &&
IsOffsetInBuffer(offset, curr_) &&
(offset + length >
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize())) {
bufs_[second].ClearBuffer();
}
bufs_[second].buffer_.Clear();
}
} else {
if (DoesBufferContainData(second) &&
!IsOffsetInBuffer(offset, second)) {
bufs_[second].ClearBuffer();
if (!IsOffsetInBuffer(offset, second)) {
bufs_[second].buffer_.Clear();
}
}
}
@@ -314,8 +308,7 @@ void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset, size_t length) {
}
}
void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset,
size_t length) {
void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset) {
if (bufs_[curr_].async_read_in_progress_ && fs_ != nullptr) {
if (bufs_[curr_].io_handle_ != nullptr) {
// Wait for prefetch data to complete.
@@ -331,87 +324,7 @@ void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset,
// completed.
DestroyAndClearIOHandle(curr_);
}
UpdateBuffersIfNeeded(offset, length);
}
// ReadAheadSizeTuning API calls readaheadsize_cb_
// (BlockBasedTableIterator::BlockCacheLookupForReadAheadSize) to lookup in the
// cache and tune the start and end offsets based on cache hits/misses.
//
// Arguments -
// read_curr_block : True if this call was due to miss in the cache and
// FilePrefetchBuffer wants to read that block
// synchronously.
// False if current call is to prefetch additional data in
// extra buffers through ReadAsync API.
// prev_buf_end_offset : End offset of the previous buffer. It's used in case
// of ReadAsync to make sure it doesn't read anything from
// previous buffer which is already prefetched.
void FilePrefetchBuffer::ReadAheadSizeTuning(
bool read_curr_block, bool refit_tail, uint64_t prev_buf_end_offset,
uint32_t index, size_t alignment, size_t length, size_t readahead_size,
uint64_t& start_offset, uint64_t& end_offset, size_t& read_len,
uint64_t& chunk_len) {
uint64_t updated_start_offset = Rounddown(start_offset, alignment);
uint64_t updated_end_offset =
Roundup(start_offset + length + readahead_size, alignment);
uint64_t initial_end_offset = updated_end_offset;
uint64_t initial_start_offset = updated_start_offset;
// Callback to tune the start and end offsets.
if (readaheadsize_cb_ != nullptr && readahead_size > 0) {
readaheadsize_cb_(read_curr_block, updated_start_offset,
updated_end_offset);
}
// read_len will be 0 and there is nothing to read/prefetch.
if (updated_start_offset == updated_end_offset) {
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(updated_end_offset - updated_start_offset));
return;
}
assert(updated_start_offset < updated_end_offset);
if (!read_curr_block) {
// Handle the case when callback added block handles which are already
// prefetched and nothing new needs to be prefetched. In that case end
// offset updated by callback will be less than prev_buf_end_offset which
// means data has been already prefetched.
if (updated_end_offset <= prev_buf_end_offset) {
start_offset = end_offset = prev_buf_end_offset;
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(end_offset - start_offset));
return;
}
}
// Realign if start and end offsets are not aligned after tuning.
start_offset = Rounddown(updated_start_offset, alignment);
end_offset = Roundup(updated_end_offset, alignment);
if (!read_curr_block && start_offset < prev_buf_end_offset) {
// Previous buffer already contains the data till prev_buf_end_offset
// because of alignment. Update the start offset after that to avoid
// prefetching it again.
start_offset = prev_buf_end_offset;
}
uint64_t roundup_len = end_offset - start_offset;
CalculateOffsetAndLen(alignment, start_offset, roundup_len, index, refit_tail,
chunk_len);
assert(roundup_len >= chunk_len);
// Update the buffer offset.
bufs_[index].offset_ = start_offset;
// Update the initial end offset of this buffer which will be the starting
// offset of next prefetch.
bufs_[index].initial_end_offset_ = initial_end_offset;
read_len = static_cast<size_t>(roundup_len - chunk_len);
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(end_offset - start_offset));
UpdateBuffersIfNeeded(offset);
}
Status FilePrefetchBuffer::HandleOverlappingData(
@@ -427,7 +340,7 @@ Status FilePrefetchBuffer::HandleOverlappingData(
// by Seek, but the next access is at another offset.
if (bufs_[curr_].async_read_in_progress_ &&
IsOffsetInBufferWithAsyncProgress(offset, curr_)) {
PollAndUpdateBuffersIfNeeded(offset, length);
PollAndUpdateBuffersIfNeeded(offset);
}
second = curr_ ^ 1;
@@ -441,7 +354,7 @@ Status FilePrefetchBuffer::HandleOverlappingData(
(bufs_[second].async_read_in_progress_ ||
DoesBufferContainData(second)))) {
// Allocate new buffer to third buffer;
bufs_[2].ClearBuffer();
bufs_[2].buffer_.Clear();
bufs_[2].buffer_.Alignment(alignment);
bufs_[2].buffer_.AllocateNewBuffer(length);
bufs_[2].offset_ = offset;
@@ -450,28 +363,31 @@ Status FilePrefetchBuffer::HandleOverlappingData(
CopyDataToBuffer(curr_, tmp_offset, tmp_length);
// Call async prefetching on curr_ since data has been consumed in curr_
// only if requested data lies within second buffer.
// only if data lies within second buffer.
size_t second_size = bufs_[second].async_read_in_progress_
? bufs_[second].async_req_len_
: bufs_[second].buffer_.CurrentSize();
uint64_t start_offset = bufs_[second].initial_end_offset_;
uint64_t rounddown_start = bufs_[second].offset_ + second_size;
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (tmp_offset + tmp_length <= bufs_[second].offset_ + second_size) {
size_t read_len = 0;
uint64_t end_offset = start_offset, chunk_len = 0;
if (tmp_offset + tmp_length <= bufs_[second].offset_ + second_size &&
!IsOffsetOutOfBound(rounddown_start)) {
uint64_t roundup_end =
Roundup(rounddown_start + readahead_size, alignment);
uint64_t roundup_len = roundup_end - rounddown_start;
uint64_t chunk_len = 0;
CalculateOffsetAndLen(alignment, rounddown_start, roundup_len, curr_,
false, chunk_len);
assert(chunk_len == 0);
assert(roundup_len >= chunk_len);
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
bufs_[second].offset_ + second_size, curr_, alignment,
/*length=*/0, readahead_size, start_offset,
end_offset, read_len, chunk_len);
if (read_len > 0) {
s = ReadAsync(opts, reader, read_len, start_offset, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].ClearBuffer();
return s;
}
bufs_[curr_].offset_ = rounddown_start;
uint64_t read_len = static_cast<size_t>(roundup_len - chunk_len);
s = ReadAsync(opts, reader, read_len, rounddown_start, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].buffer_.Clear();
return s;
}
}
curr_ = curr_ ^ 1;
@@ -514,14 +430,13 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
Status s;
uint64_t tmp_offset = offset;
size_t tmp_length = length;
size_t original_length = length;
// 1. Abort IO and swap buffers if needed to point curr_ to first buffer with
// data.
if (!explicit_prefetch_submitted_) {
AbortIOIfNeeded(offset);
}
UpdateBuffersIfNeeded(offset, length);
UpdateBuffersIfNeeded(offset);
// 2. Handle overlapping data over two buffers. If data is overlapping then
// during this call:
@@ -543,15 +458,14 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
if (!bufs_[curr_].async_read_in_progress_ && DoesBufferContainData(curr_) &&
IsDataBlockInBuffer(offset, length, curr_)) {
// Whole data is in curr_.
UpdateBuffersIfNeeded(offset, length);
UpdateBuffersIfNeeded(offset);
if (!IsSecondBuffEligibleForPrefetching()) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
} else {
// After poll request, curr_ might be empty because of IOError in
// callback while reading or may contain required data.
PollAndUpdateBuffersIfNeeded(offset, length);
PollAndUpdateBuffersIfNeeded(offset);
}
if (copy_to_third_buffer) {
@@ -574,7 +488,6 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
return s;
}
if (!IsSecondBuffEligibleForPrefetching()) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
}
@@ -596,7 +509,7 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
}
}
DestroyAndClearIOHandle(second);
bufs_[second].ClearBuffer();
bufs_[second].buffer_.Clear();
}
// 5. Data is overlapping i.e. some of the data has been copied to third
@@ -611,57 +524,73 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
// and sync prefetching and copy the remaining data to third buffer in the
// end.
if (length == 0) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
}
// 6. Go for ReadAsync and Read (if needed).
size_t prefetch_size = length + readahead_size;
size_t _offset = static_cast<size_t>(offset);
// offset and size alignment for curr_ buffer with synchronous prefetching
uint64_t rounddown_start1 = Rounddown(_offset, alignment);
uint64_t roundup_end1 = Roundup(_offset + prefetch_size, alignment);
uint64_t roundup_len1 = roundup_end1 - rounddown_start1;
assert(roundup_len1 >= alignment);
assert(roundup_len1 % alignment == 0);
uint64_t chunk_len1 = 0;
uint64_t read_len1 = 0;
assert(!bufs_[second].async_read_in_progress_ &&
!DoesBufferContainData(second));
// offset and size alignment for curr_ buffer with synchronous prefetching
uint64_t start_offset1 = offset, end_offset1 = 0, chunk_len1 = 0;
size_t read_len1 = 0;
// For length == 0, skip the synchronous prefetching. read_len1 will be 0.
if (length > 0) {
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/false,
start_offset1, curr_, alignment, length, readahead_size,
start_offset1, end_offset1, read_len1, chunk_len1);
UpdateStats(/*found_in_buffer=*/false,
/*length_found=*/original_length - length);
} else {
end_offset1 = bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
UpdateStats(/*found_in_buffer=*/true, original_length);
CalculateOffsetAndLen(alignment, offset, roundup_len1, curr_,
false /*refit_tail*/, chunk_len1);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1 - chunk_len1);
}
// Prefetch in second buffer only if readahead_size > 0.
if (readahead_size > 0) {
// Prefetch in second buffer only if readahead_size_ > 0.
if (readahead_size_ > 0) {
// offset and size alignment for second buffer for asynchronous
// prefetching.
uint64_t start_offset2 = bufs_[curr_].initial_end_offset_;
// prefetching
uint64_t rounddown_start2 = roundup_end1;
uint64_t roundup_end2 =
Roundup(rounddown_start2 + readahead_size, alignment);
// Find updated readahead size after tuning
size_t read_len2 = 0;
uint64_t end_offset2 = start_offset2, chunk_len2 = 0;
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
/*prev_buf_end_offset=*/end_offset1, second,
alignment,
/*length=*/0, readahead_size, start_offset2,
end_offset2, read_len2, chunk_len2);
if (read_len2 > 0) {
s = ReadAsync(opts, reader, read_len2, start_offset2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].ClearBuffer();
return s;
}
// For length == 0, do the asynchronous prefetching in second instead of
// synchronous prefetching in curr_.
if (length == 0) {
rounddown_start2 =
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
roundup_end2 = Roundup(rounddown_start2 + prefetch_size, alignment);
}
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (!IsOffsetOutOfBound(rounddown_start2)) {
uint64_t roundup_len2 = roundup_end2 - rounddown_start2;
uint64_t chunk_len2 = 0;
CalculateOffsetAndLen(alignment, rounddown_start2, roundup_len2, second,
false /*refit_tail*/, chunk_len2);
assert(chunk_len2 == 0);
// Update the buffer offset.
bufs_[second].offset_ = rounddown_start2;
assert(roundup_len2 >= chunk_len2);
uint64_t read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].buffer_.Clear();
return s;
}
}
}
if (read_len1 > 0) {
s = Read(opts, reader, read_len1, chunk_len1, start_offset1, curr_);
s = Read(opts, reader, read_len1, chunk_len1, rounddown_start1, curr_);
if (!s.ok()) {
if (bufs_[second].io_handle_ != nullptr) {
std::vector<void*> handles;
@@ -673,12 +602,11 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
}
}
DestroyAndClearIOHandle(second);
bufs_[second].ClearBuffer();
bufs_[curr_].ClearBuffer();
bufs_[second].buffer_.Clear();
bufs_[curr_].buffer_.Clear();
return s;
}
}
// Copy remaining requested bytes to third_buffer.
if (copy_to_third_buffer && length > 0) {
CopyDataToBuffer(curr_, offset, length);
@@ -740,7 +668,8 @@ bool FilePrefetchBuffer::TryReadFromCacheUntracked(
return false;
}
}
s = Prefetch(opts, reader, offset, n + readahead_size_);
size_t current_readahead_size = ReadAheadSizeTuning(offset, n);
s = Prefetch(opts, reader, offset, n + current_readahead_size);
}
if (!s.ok()) {
if (status) {
@@ -800,8 +729,8 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
// Random offset called. So abort the IOs.
if (prev_offset_ != offset) {
AbortAllIOs();
bufs_[curr_].ClearBuffer();
bufs_[curr_ ^ 1].ClearBuffer();
bufs_[curr_].buffer_.Clear();
bufs_[curr_ ^ 1].buffer_.Clear();
explicit_prefetch_submitted_ = false;
return false;
}
@@ -839,6 +768,8 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
}
}
UpdateReadAheadSizeForUpperBound(offset, n);
// Prefetch n + readahead_size_/2 synchronously as remaining
// readahead_size_/2 will be prefetched asynchronously.
s = PrefetchAsyncInternal(opts, reader, offset, n, readahead_size_ / 2,
@@ -857,8 +788,6 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
} else {
return false;
}
} else {
UpdateStats(/*found_in_buffer=*/true, n);
}
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
@@ -919,6 +848,7 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
explicit_prefetch_submitted_ = false;
bool is_eligible_for_prefetching = false;
UpdateReadAheadSizeForUpperBound(offset, n);
if (readahead_size_ > 0 &&
(!implicit_auto_readahead_ ||
num_file_reads_ >= num_file_reads_for_auto_readahead_)) {
@@ -930,19 +860,14 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
AbortAllIOs();
// 2. Clear outdated data.
UpdateBuffersIfNeeded(offset, n);
UpdateBuffersIfNeeded(offset);
uint32_t second = curr_ ^ 1;
// - Since PrefetchAsync can be called on non sequential reads. So offset can
// be less than curr_ buffers' offset. In that case it clears both
// buffers.
// - In case of tuning of readahead_size, on Reseek, we have to clear both
// buffers otherwise, we may end up with inconsistent BlockHandles in queue
// and data in buffer.
if (readaheadsize_cb_ != nullptr ||
(DoesBufferContainData(curr_) && !IsOffsetInBuffer(offset, curr_))) {
bufs_[curr_].ClearBuffer();
bufs_[second].ClearBuffer();
// Since PrefetchAsync can be called on non sequential reads. So offset can
// be less than curr_ buffers' offset. In that case also it clears both
// buffers.
if (DoesBufferContainData(curr_) && !IsOffsetInBuffer(offset, curr_)) {
bufs_[curr_].buffer_.Clear();
bufs_[second].buffer_.Clear();
}
UpdateReadPattern(offset, n, /*decrease_readaheadsize=*/false);
@@ -954,8 +879,6 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
uint64_t offset_in_buffer = offset - bufs_[curr_].offset_;
*result = Slice(bufs_[curr_].buffer_.BufferStart() + offset_in_buffer, n);
data_found = true;
UpdateStats(/*found_in_buffer=*/true, n);
// Update num_file_reads_ as TryReadFromCacheAsync won't be called for
// poll and update num_file_reads_ if data is found.
num_file_reads_++;
@@ -967,78 +890,91 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
}
} else {
// Partial data in curr_.
bufs_[curr_].ClearBuffer();
bufs_[curr_].buffer_.Clear();
}
bufs_[second].ClearBuffer();
std::string msg;
bufs_[second].buffer_.Clear();
Status s;
size_t alignment = reader->file()->GetRequiredBufferAlignment();
size_t readahead_size = is_eligible_for_prefetching ? readahead_size_ / 2 : 0;
size_t prefetch_size = is_eligible_for_prefetching ? readahead_size_ / 2 : 0;
size_t offset_to_read = static_cast<size_t>(offset);
uint64_t start_offset1 = offset, end_offset1 = 0, start_offset2 = 0,
chunk_len1 = 0;
size_t read_len1 = 0, read_len2 = 0;
uint64_t rounddown_start1 = 0;
uint64_t roundup_end1 = 0;
uint64_t rounddown_start2 = 0;
uint64_t roundup_end2 = 0;
uint64_t chunk_len1 = 0;
uint64_t chunk_len2 = 0;
size_t read_len1 = 0;
size_t read_len2 = 0;
// - If curr_ is empty.
// - Call async read for full data + readahead_size on curr_.
// - Call async read for readahead_size on second if eligible.
// - Call async read for full data + prefetch_size on curr_.
// - Call async read for prefetch_size on second if eligible.
// - If curr_ is filled.
// - readahead_size on second.
// - prefetch_size on second.
// Calculate length and offsets for reading.
if (!DoesBufferContainData(curr_)) {
uint64_t roundup_len1;
// Prefetch full data + readahead_size in curr_.
// Prefetch full data + prefetch_size in curr_.
if (is_eligible_for_prefetching || reader->use_direct_io()) {
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/false,
/*prev_buf_end_offset=*/start_offset1, curr_,
alignment, n, readahead_size, start_offset1,
end_offset1, read_len1, chunk_len1);
rounddown_start1 = Rounddown(offset_to_read, alignment);
roundup_end1 = Roundup(offset_to_read + n + prefetch_size, alignment);
roundup_len1 = roundup_end1 - rounddown_start1;
assert(roundup_len1 >= alignment);
assert(roundup_len1 % alignment == 0);
} else {
// No alignment or extra prefetching.
start_offset1 = offset_to_read;
end_offset1 = offset_to_read + n;
roundup_len1 = end_offset1 - start_offset1;
CalculateOffsetAndLen(alignment, start_offset1, roundup_len1, curr_,
false, chunk_len1);
assert(chunk_len1 == 0);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1);
bufs_[curr_].offset_ = start_offset1;
rounddown_start1 = offset_to_read;
roundup_end1 = offset_to_read + n;
roundup_len1 = roundup_end1 - rounddown_start1;
}
CalculateOffsetAndLen(alignment, rounddown_start1, roundup_len1, curr_,
false, chunk_len1);
assert(chunk_len1 == 0);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1);
bufs_[curr_].offset_ = rounddown_start1;
}
if (is_eligible_for_prefetching) {
start_offset2 = bufs_[curr_].initial_end_offset_;
if (DoesBufferContainData(curr_)) {
rounddown_start2 =
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
} else {
rounddown_start2 = roundup_end1;
}
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (!IsOffsetOutOfBound(rounddown_start2)) {
roundup_end2 = Roundup(rounddown_start2 + prefetch_size, alignment);
uint64_t roundup_len2 = roundup_end2 - rounddown_start2;
uint64_t end_offset2 = start_offset2, chunk_len2 = 0;
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
/*prev_buf_end_offset=*/end_offset1, second,
alignment,
/*length=*/0, readahead_size, start_offset2,
end_offset2, read_len2, chunk_len2);
CalculateOffsetAndLen(alignment, rounddown_start2, roundup_len2, second,
false, chunk_len2);
assert(chunk_len2 == 0);
assert(roundup_len2 >= chunk_len2);
read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
// Update the buffer offset.
bufs_[second].offset_ = rounddown_start2;
}
}
if (read_len1) {
s = ReadAsync(opts, reader, read_len1, start_offset1, curr_);
s = ReadAsync(opts, reader, read_len1, rounddown_start1, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].ClearBuffer();
bufs_[curr_].buffer_.Clear();
return s;
}
explicit_prefetch_submitted_ = true;
prev_len_ = 0;
}
if (read_len2) {
TEST_SYNC_POINT("FilePrefetchBuffer::PrefetchAsync:ExtraPrefetching");
s = ReadAsync(opts, reader, read_len2, start_offset2, second);
s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].ClearBuffer();
bufs_[second].buffer_.Clear();
return s;
}
readahead_size_ = std::min(max_readahead_size_, readahead_size_ * 2);
+48 -46
View File
@@ -32,11 +32,6 @@ struct IOOptions;
class RandomAccessFileReader;
struct BufferInfo {
void ClearBuffer() {
buffer_.Clear();
initial_end_offset_ = 0;
}
AlignedBuffer buffer_;
uint64_t offset_ = 0;
@@ -57,18 +52,6 @@ struct BufferInfo {
// pos represents the index of this buffer in vector of BufferInfo.
uint32_t pos_ = 0;
// initial_end_offset is used to keep track of the end offset of the buffer
// that was originally called. It's helpful in case of autotuning of readahead
// size when callback is made to BlockBasedTableIterator.
// initial end offset of this buffer which will be the starting
// offset of next prefetch.
//
// For example - if end offset of previous buffer was 100 and because of
// readahead_size optimization, end_offset was trimmed to 60. Then for next
// prefetch call, start_offset should be intialized to 100 i.e start_offset =
// buf->initial_end_offset_.
uint64_t initial_end_offset_ = 0;
};
enum class FilePrefetchBufferUsage {
@@ -105,9 +88,10 @@ class FilePrefetchBuffer {
size_t readahead_size = 0, size_t max_readahead_size = 0,
bool enable = true, bool track_min_offset = false,
bool implicit_auto_readahead = false, uint64_t num_file_reads = 0,
uint64_t num_file_reads_for_auto_readahead = 0, FileSystem* fs = nullptr,
uint64_t num_file_reads_for_auto_readahead = 0,
uint64_t upper_bound_offset = 0, FileSystem* fs = nullptr,
SystemClock* clock = nullptr, Statistics* stats = nullptr,
const std::function<void(bool, uint64_t&, uint64_t&)>& cb = nullptr,
const std::function<void(uint64_t, size_t, size_t&)>& cb = nullptr,
FilePrefetchBufferUsage usage = FilePrefetchBufferUsage::kUnknown)
: curr_(0),
readahead_size_(readahead_size),
@@ -126,6 +110,7 @@ class FilePrefetchBuffer {
clock_(clock),
stats_(stats),
usage_(usage),
upper_bound_offset_(upper_bound_offset),
readaheadsize_cb_(cb) {
assert((num_file_reads_ >= num_file_reads_for_auto_readahead_ + 1) ||
(num_file_reads_ == 0));
@@ -254,6 +239,9 @@ class FilePrefetchBuffer {
void UpdateReadPattern(const uint64_t& offset, const size_t& len,
bool decrease_readaheadsize) {
if (decrease_readaheadsize) {
// Since this block was eligible for prefetch but it was found in
// cache, so check and decrease the readahead_size by 8KB (default)
// if eligible.
DecreaseReadAheadIfEligible(offset, len);
}
prev_offset_ = offset;
@@ -294,10 +282,9 @@ class FilePrefetchBuffer {
// Callback function passed to underlying FS in case of asynchronous reads.
void PrefetchAsyncCallback(const FSReadRequest& req, void* cb_arg);
void TEST_GetBufferOffsetandSize(uint32_t index, uint64_t& offset,
size_t& len) {
offset = bufs_[index].offset_;
len = bufs_[index].buffer_.CurrentSize();
void ResetUpperBoundOffset(uint64_t upper_bound_offset) {
upper_bound_offset_ = upper_bound_offset;
readahead_size_ = initial_auto_readahead_size_;
}
private:
@@ -312,12 +299,12 @@ class FilePrefetchBuffer {
void AbortAllIOs();
void UpdateBuffersIfNeeded(uint64_t offset, size_t len);
void UpdateBuffersIfNeeded(uint64_t offset);
// It calls Poll API if any there is any pending asynchronous request. It then
// checks if data is in any buffer. It clears the outdated data and swaps the
// buffers if required.
void PollAndUpdateBuffersIfNeeded(uint64_t offset, size_t len);
void PollAndUpdateBuffersIfNeeded(uint64_t offset);
Status PrefetchAsyncInternal(const IOOptions& opts,
RandomAccessFileReader* reader, uint64_t offset,
@@ -325,11 +312,11 @@ class FilePrefetchBuffer {
bool& copy_to_third_buffer);
Status Read(const IOOptions& opts, RandomAccessFileReader* reader,
uint64_t read_len, uint64_t chunk_len, uint64_t start_offset,
uint64_t read_len, uint64_t chunk_len, uint64_t rounddown_start,
uint32_t index);
Status ReadAsync(const IOOptions& opts, RandomAccessFileReader* reader,
uint64_t read_len, uint64_t start_offset, uint32_t index);
uint64_t read_len, uint64_t rounddown_start, uint32_t index);
// Copy the data from src to third buffer.
void CopyDataToBuffer(uint32_t src, uint64_t& offset, size_t& length);
@@ -415,7 +402,7 @@ class FilePrefetchBuffer {
return false;
}
bufs_[second].ClearBuffer();
bufs_[second].buffer_.Clear();
return true;
}
@@ -445,27 +432,38 @@ class FilePrefetchBuffer {
uint64_t offset, size_t n, Slice* result,
Status* status);
void ReadAheadSizeTuning(bool read_curr_block, bool refit_tail,
uint64_t prev_buf_end_offset, uint32_t index,
size_t alignment, size_t length,
size_t readahead_size, uint64_t& offset,
uint64_t& end_offset, size_t& read_len,
uint64_t& chunk_len);
void UpdateStats(bool found_in_buffer, size_t length_found) {
if (found_in_buffer) {
RecordTick(stats_, PREFETCH_HITS);
}
if (length_found > 0) {
RecordTick(stats_, PREFETCH_BYTES_USEFUL, length_found);
void UpdateReadAheadSizeForUpperBound(uint64_t offset, size_t n) {
// Adjust readhahead_size till upper_bound if upper_bound_offset_ is
// set.
if (readahead_size_ > 0 && upper_bound_offset_ > 0 &&
upper_bound_offset_ > offset) {
if (upper_bound_offset_ < offset + n + readahead_size_) {
readahead_size_ = (upper_bound_offset_ - offset) - n;
RecordTick(stats_, READAHEAD_TRIMMED);
}
}
}
void UpdateReadAheadTrimmedStat(size_t initial_length,
size_t updated_length) {
if (initial_length != updated_length) {
RecordTick(stats_, READAHEAD_TRIMMED);
inline bool IsOffsetOutOfBound(uint64_t offset) {
if (upper_bound_offset_ > 0) {
return (offset >= upper_bound_offset_);
}
return false;
}
// Performs tuning to calculate readahead_size.
size_t ReadAheadSizeTuning(uint64_t offset, size_t n) {
UpdateReadAheadSizeForUpperBound(offset, n);
if (readaheadsize_cb_ != nullptr && readahead_size_ > 0) {
size_t updated_readahead_size = 0;
readaheadsize_cb_(offset, readahead_size_, updated_readahead_size);
if (readahead_size_ != updated_readahead_size) {
RecordTick(stats_, READAHEAD_TRIMMED);
}
return updated_readahead_size;
}
return readahead_size_;
}
std::vector<BufferInfo> bufs_;
@@ -510,6 +508,10 @@ class FilePrefetchBuffer {
FilePrefetchBufferUsage usage_;
std::function<void(bool, uint64_t&, uint64_t&)> readaheadsize_cb_;
// upper_bound_offset_ is set when ReadOptions.iterate_upper_bound and
// ReadOptions.auto_readahead_size are set to trim readahead_size upto
// upper_bound_offset_ during prefetching.
uint64_t upper_bound_offset_ = 0;
std::function<void(uint64_t, size_t, size_t&)> readaheadsize_cb_;
};
} // namespace ROCKSDB_NAMESPACE
+3 -2
View File
@@ -8,9 +8,10 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "file/filename.h"
#include <cctype>
#include <ctype.h>
#include <stdio.h>
#include <cinttypes>
#include <cstdio>
#include <vector>
#include "file/writable_file_writer.h"
+381 -9
View File
@@ -1325,10 +1325,6 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
ropts.readahead_size = cmp_ro.readahead_size = 32768;
}
if (std::get<1>(GetParam())) {
ropts.async_io = true;
}
// With and without tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
@@ -1355,6 +1351,10 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
cmp_iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_OK(cmp_iter->status());
ASSERT_OK(iter->status());
}
@@ -1381,6 +1381,10 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
cmp_iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_OK(cmp_iter->status());
ASSERT_OK(iter->status());
}
@@ -2349,6 +2353,314 @@ TEST_P(PrefetchTest1, SeekParallelizationTest) {
Close();
}
// This test checks if readahead_size is trimmed when upper_bound is reached.
// It tests with different combinations of async_io disabled/enabled,
// readahead_size (implicit and explicit), and num_file_reads_for_auto_readahead
// from 0 to 2.
TEST_P(PrefetchTest, IterReadAheadSizeWithUpperBound) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
// First param is if the mockFS support_prefetch or not
std::shared_ptr<MockFS> fs =
std::make_shared<MockFS>(FileSystem::Default(), false);
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
Options options;
SetGenericOptions(env.get(), /*use_direct_io=*/false, options);
options.statistics = CreateDBStatistics();
BlockBasedTableOptions table_options;
SetBlockBasedTableOptions(table_options);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Status s = TryReopen(options);
ASSERT_OK(s);
Random rnd(309);
WriteBatch batch;
for (int i = 0; i < 26; i++) {
std::string key = "my_key_";
for (int j = 0; j < 10; j++) {
key += char('a' + i);
ASSERT_OK(batch.Put(key, rnd.RandomString(1000)));
}
}
ASSERT_OK(db_->Write(WriteOptions(), &batch));
std::string start_key = "my_key_a";
std::string end_key = "my_key_";
for (int j = 0; j < 10; j++) {
end_key += char('a' + 25);
}
Slice least(start_key.data(), start_key.size());
Slice greatest(end_key.data(), end_key.size());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
int buff_prefetch_count = 0;
// Try with different num_file_reads_for_auto_readahead from 0 to 3.
for (size_t i = 0; i < 3; i++) {
table_options.num_file_reads_for_auto_readahead = i;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
s = TryReopen(options);
ASSERT_OK(s);
int buff_count_with_tuning = 0, buff_count_without_tuning = 0;
int keys_with_tuning = 0, keys_without_tuning = 0;
int reseek_keys_with_tuning = 0, reseek_keys_without_tuning = 0;
buff_prefetch_count = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::Prefetch:Start",
[&](void*) { buff_prefetch_count++; });
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::PrefetchAsyncInternal:Start",
[&](void*) { buff_prefetch_count++; });
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions ropts;
if (std::get<0>(GetParam())) {
ropts.readahead_size = 32768;
}
if (std::get<1>(GetParam())) {
ropts.async_io = true;
}
// With tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
Slice ub = Slice("my_key_uuu");
Slice* ub_ptr = &ub;
ropts.iterate_upper_bound = ub_ptr;
ropts.auto_readahead_size = true;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
// Seek.
{
Slice seek_key = Slice("my_key_aaa");
iter->Seek(seek_key);
while (iter->Valid()) {
keys_with_tuning++;
iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
buff_count_with_tuning = buff_prefetch_count;
}
// Reseek with new upper_bound_iterator.
{
ub = Slice("my_key_y");
Slice reseek_key = Slice("my_key_v");
iter->Seek(reseek_key);
while (iter->Valid()) {
iter->Next();
reseek_keys_with_tuning++;
}
ASSERT_OK(iter->status());
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_GT(reseek_keys_with_tuning, 0);
}
}
// Without tuning readahead_size
{
Slice ub = Slice("my_key_uuu");
Slice* ub_ptr = &ub;
ropts.iterate_upper_bound = ub_ptr;
buff_prefetch_count = 0;
ASSERT_OK(options.statistics->Reset());
ropts.auto_readahead_size = false;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
// Seek.
{
Slice seek_key = Slice("my_key_aaa");
iter->Seek(seek_key);
while (iter->Valid()) {
keys_without_tuning++;
iter->Next();
}
buff_count_without_tuning = buff_prefetch_count;
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_EQ(readahead_trimmed, 0);
}
// Reseek with new upper_bound_iterator.
{
ub = Slice("my_key_y");
Slice reseek_key = Slice("my_key_v");
iter->Seek(reseek_key);
while (iter->Valid()) {
iter->Next();
reseek_keys_without_tuning++;
}
ASSERT_OK(iter->status());
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_EQ(readahead_trimmed, 0);
ASSERT_GT(reseek_keys_without_tuning, 0);
}
}
{
// Verify results with and without tuning.
if (std::get<1>(GetParam())) {
// In case of async_io.
ASSERT_GE(buff_count_with_tuning, buff_count_without_tuning);
} else {
ASSERT_EQ(buff_count_without_tuning, buff_count_with_tuning);
}
// Prefetching should happen.
ASSERT_GT(buff_count_without_tuning, 0);
ASSERT_GT(buff_count_with_tuning, 0);
// No of keys should be equal.
ASSERT_EQ(keys_without_tuning, keys_with_tuning);
// No of keys after reseek with new upper bound should be equal.
ASSERT_EQ(reseek_keys_without_tuning, reseek_keys_with_tuning);
}
Close();
}
}
// This test checks if readahead_size is trimmed when upper_bound is reached
// during Seek in async_io and it goes for polling without any extra
// prefetching.
TEST_P(PrefetchTest, IterReadAheadSizeWithUpperBoundSeekOnly) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
// First param is if the mockFS support_prefetch or not
std::shared_ptr<MockFS> fs =
std::make_shared<MockFS>(FileSystem::Default(), false);
bool use_direct_io = false;
if (std::get<0>(GetParam())) {
use_direct_io = true;
}
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
Options options;
SetGenericOptions(env.get(), use_direct_io, options);
options.statistics = CreateDBStatistics();
BlockBasedTableOptions table_options;
SetBlockBasedTableOptions(table_options);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Status s = TryReopen(options);
if (use_direct_io && (s.IsNotSupported() || s.IsInvalidArgument())) {
// If direct IO is not supported, skip the test
return;
} else {
ASSERT_OK(s);
}
Random rnd(309);
WriteBatch batch;
for (int i = 0; i < 26; i++) {
std::string key = "my_key_";
for (int j = 0; j < 10; j++) {
key += char('a' + i);
ASSERT_OK(batch.Put(key, rnd.RandomString(1000)));
}
}
ASSERT_OK(db_->Write(WriteOptions(), &batch));
std::string start_key = "my_key_a";
std::string end_key = "my_key_";
for (int j = 0; j < 10; j++) {
end_key += char('a' + 25);
}
Slice least(start_key.data(), start_key.size());
Slice greatest(end_key.data(), end_key.size());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
s = TryReopen(options);
ASSERT_OK(s);
int buff_count_with_tuning = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::PrefetchAsyncInternal:Start",
[&](void*) { buff_count_with_tuning++; });
bool read_async_called = false;
SyncPoint::GetInstance()->SetCallBack(
"UpdateResults::io_uring_result",
[&](void* /*arg*/) { read_async_called = true; });
SyncPoint::GetInstance()->EnableProcessing();
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions ropts;
if (std::get<1>(GetParam())) {
ropts.readahead_size = 32768;
}
ropts.async_io = true;
Slice ub = Slice("my_key_aaa");
ropts.iterate_upper_bound = &ub;
Slice seek_key = Slice("my_key_aaa");
// With tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
ropts.auto_readahead_size = true;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
iter->Seek(seek_key);
ASSERT_OK(iter->status());
// Verify results.
uint64_t readhahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
// Readahead got trimmed.
if (read_async_called) {
ASSERT_GT(readhahead_trimmed, 0);
// Seek called PrefetchAsync to poll the data.
ASSERT_EQ(1, buff_count_with_tuning);
} else {
// async_io disabled.
ASSERT_GE(readhahead_trimmed, 0);
ASSERT_EQ(0, buff_count_with_tuning);
}
}
Close();
}
namespace {
#ifdef GFLAGS
const int kMaxArgCount = 100;
@@ -2927,7 +3239,7 @@ TEST_F(FilePrefetchBufferTest, SeekWithBlockCacheHit) {
std::unique_ptr<RandomAccessFileReader> r;
Read(fname, opts, &r);
FilePrefetchBuffer fpb(16384, 16384, true, false, false, 0, 0, fs());
FilePrefetchBuffer fpb(16384, 16384, true, false, false, 0, 0, 0, fs());
Slice result;
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
// it will do two reads of 4096+8192 and 8192
@@ -2976,7 +3288,8 @@ TEST_F(FilePrefetchBufferTest, SeekWithoutAlignment) {
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/true,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2,
/*upper_bound_offset=*/0, fs());
Slice result;
// Simulate a seek of half of alignment bytes at offset n. Due to the
@@ -3007,7 +3320,8 @@ TEST_F(FilePrefetchBufferTest, SeekWithoutAlignment) {
FilePrefetchBuffer fpb(
/*readahead_size=*/16384, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2,
/*upper_bound_offset=*/0, fs());
Slice result;
// Simulate a seek of half of alignment bytes at offset n.
@@ -3043,7 +3357,8 @@ TEST_F(FilePrefetchBufferTest, NoSyncWithAsyncIO) {
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0,
/*upper_bound_offset=*/0, fs());
int read_async_called = 0;
SyncPoint::GetInstance()->SetCallBack(
@@ -3081,6 +3396,63 @@ TEST_F(FilePrefetchBufferTest, NoSyncWithAsyncIO) {
ASSERT_EQ(result, async_result);
}
// This test checks if during seek in async_io, if first buffer already
// prefetched the data till upper_bound offset, second buffer shouldn't go for
// prefetching.
TEST_F(FilePrefetchBufferTest, IterateUpperBoundTest1) {
std::string fname = "iterate-upperbound-test1";
Random rand(0);
std::string content = rand.RandomString(32768);
Write(fname, content);
FileOptions opts;
std::unique_ptr<RandomAccessFileReader> r;
Read(fname, opts, &r);
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0,
/*upper_bound_offset=*/8000, fs());
int read_async_called = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::ReadAsync",
[&](void* /*arg*/) { read_async_called++; });
SyncPoint::GetInstance()->EnableProcessing();
Slice async_result;
// Simulate a seek of 4000 bytes at offset 3000. Due to the readahead
// settings, it will do 1 read of 4000+1000 (till 8000 - upper bound).
Status s = fpb.PrefetchAsync(IOOptions(), r.get(), 3000, 4000, &async_result);
// Platforms that don't have IO uring may not support async IO
if (s.IsNotSupported()) {
return;
}
ASSERT_TRUE(s.IsTryAgain());
IOOptions io_opts;
io_opts.rate_limiter_priority = Env::IOPriority::IO_LOW;
ASSERT_TRUE(fpb.TryReadFromCacheAsync(io_opts, r.get(), /*offset=*/3000,
/*length=*/4000, &async_result, &s));
// No sync call should be made.
HistogramData sst_read_micros;
stats()->histogramData(SST_READ_MICROS, &sst_read_micros);
ASSERT_EQ(sst_read_micros.count, 0);
// Number of async calls should be 1.
// No Prefetching should happen in second buffer as first buffer has already
// prefetched till offset.
ASSERT_EQ(read_async_called, 1);
// Length should be 4000.
ASSERT_EQ(async_result.size(), 4000);
// Data correctness.
Slice result(&content[3000], 4000);
ASSERT_EQ(result.size(), 4000);
ASSERT_EQ(result, async_result);
}
TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
std::string fname = "seek-with-block-cache-hit";
Random rand(0);
@@ -3092,7 +3464,7 @@ TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
Read(fname, opts, &r);
std::shared_ptr<Statistics> stats = CreateDBStatistics();
FilePrefetchBuffer fpb(8192, 8192, true, false, false, 0, 0, fs(), nullptr,
FilePrefetchBuffer fpb(8192, 8192, true, false, false, 0, 0, 0, fs(), nullptr,
stats.get());
Slice result;
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
+2 -3
View File
@@ -492,9 +492,8 @@ IOStatus RandomAccessFileReader::ReadAsync(
auto read_async_callback =
std::bind(&RandomAccessFileReader::ReadAsyncCallback, this,
std::placeholders::_1, std::placeholders::_2);
ReadAsyncInfo* read_async_info = new ReadAsyncInfo(
cb, cb_arg, (clock_ != nullptr ? clock_->NowMicros() : 0));
ReadAsyncInfo* read_async_info =
new ReadAsyncInfo(cb, cb_arg, clock_->NowMicros());
if (ShouldNotifyListeners()) {
read_async_info->fs_start_ts_ = FileOperationInfo::StartNow();
+3 -9
View File
@@ -425,9 +425,7 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) {
std::swap(dest, src);
}
if (reverse) std::swap(dest, src);
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
@@ -450,9 +448,7 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) {
std::swap(dest, src);
}
if (reverse) std::swap(dest, src);
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
@@ -475,9 +471,7 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) {
std::swap(dest, src);
}
if (reverse) std::swap(dest, src);
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
+3 -37
View File
@@ -380,6 +380,9 @@ inline std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
// to find the appropriate balance automatically.
// * Cache priorities are less aggressively enforced, which could cause
// cache dilution from long range scans (unless they use fill_cache=false).
// * Can be worse for small caches, because if almost all of a cache shard is
// pinned (more likely with non-partitioned filters), then CLOCK eviction
// becomes very CPU intensive.
//
// See internal cache/clock_cache.h for full description.
struct HyperClockCacheOptions : public ShardedCacheOptions {
@@ -438,43 +441,6 @@ struct HyperClockCacheOptions : public ShardedCacheOptions {
// load factor for efficient Lookup, Insert, etc.
size_t min_avg_entry_charge = 450;
// A tuning parameter to cap eviction CPU usage in a "thrashing" situation
// by allowing the memory capacity to be exceeded slightly as needed. The
// default setting should offer balanced protection against excessive CPU
// and memory usage under extreme stress conditions, with no effect on
// normal operation. Such stress conditions are proportionally more likely
// with small caches (10s of MB or less) vs. large caches (GB-scale).
// (NOTE: With the unusual setting of strict_capacity_limit=true, this
// parameter is ignored.)
//
// BACKGROUND: Without some kind of limiter, inserting into a CLOCK-based
// cache with no evictable entries (all "pinned") requires scanning the
// entire cache to determine that nothing can be evicted. (By contrast,
// LRU caches can determine no entries are evictable in O(1) time, but
// require more synchronization/coordination on that eviction metadata.)
// This aspect of a CLOCK cache can make a stressed situation worse by
// bogging down the CPU with repeated scans of the cache. And with
// strict_capacity_limit=false (normal setting), finding something evictable
// doesn't change the outcome of insertion: the entry is inserted anyway
// and the cache is allowed to exceed its target capacity if necessary.
//
// SOLUTION: Eviction is aborted upon seeing some number of pinned
// entries before evicting anything, or if the ratio of pinned to evicted
// is too high. This setting `eviction_effort_cap` essentially controls both
// that allowed initial number of pinned entries and the maximum allowed
// ratio. As the pinned size approaches the target cache capacity, roughly
// 1/eviction_effort_cap additional portion of the capacity might be kept
// in memory and evictable in order to keep CLOCK eviction reasonably
// performant. Under the default setting and high stress conditions, this
// memory overhead is around 3-5%. Under normal or even moderate stress
// conditions, the memory overhead is negligible to zero.
//
// A large value like 1000 offers some protection with essentially no
// memory overhead, while the minimum value of 1 could be useful for a
// small cache where roughly doubling in size under stress could be OK to
// keep operations very fast.
int eviction_effort_cap = 30;
HyperClockCacheOptions(
size_t _capacity, size_t _estimated_entry_charge,
int _num_shard_bits = -1, bool _strict_capacity_limit = false,
+10 -4
View File
@@ -1737,17 +1737,23 @@ struct ReadOptions {
// Default: empty (every table will be scanned)
std::function<bool(const TableProperties&)> table_filter;
// Experimental
//
// If auto_readahead_size is set to true, it will auto tune the readahead_size
// during scans internally.
// For this feature to enabled, iterate_upper_bound must also be specified.
//
// NOTE: - Recommended for forward Scans only.
// - In case of backward scans like Prev or SeekForPrev, the
// cost of these backward operations might increase and affect the
// performace. So this option should not be enabled if workload
// contains backward scans.
// - If there is a backward scans, this option will be
// disabled internally and won't be enabled again if the forward scan
// is issued again.
// disabled internally and won't be reset if forward scan is done
// again.
//
// Default: true
bool auto_readahead_size = true;
// Default: false
bool auto_readahead_size = false;
// *** END options only relevant to iterators or scans ***
+3 -3
View File
@@ -114,7 +114,7 @@ class SecondaryCache : public Customizable {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) = 0;
bool& kept_in_sec_cache) = 0;
// Indicate whether a handle can be erased in this secondary cache.
[[nodiscard]] virtual bool SupportForceErase() const = 0;
@@ -176,9 +176,9 @@ class SecondaryCacheWrapper : public SecondaryCache {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
Statistics* stats, bool& kept_in_sec_cache) override {
bool& kept_in_sec_cache) override {
return target()->Lookup(key, helper, create_context, wait, advise_erase,
stats, kept_in_sec_cache);
kept_in_sec_cache);
}
virtual bool SupportForceErase() const override {
-6
View File
@@ -531,12 +531,6 @@ enum Tickers : uint32_t {
// Number of FS reads avoided due to scan prefetching
PREFETCH_HITS,
// Compressed secondary cache related stats
COMPRESSED_SECONDARY_CACHE_DUMMY_HITS,
COMPRESSED_SECONDARY_CACHE_HITS,
COMPRESSED_SECONDARY_CACHE_PROMOTIONS,
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS,
TICKER_ENUM_MAX
};
+3 -7
View File
@@ -140,7 +140,8 @@ class TablePropertiesCollector {
virtual bool NeedCompact() const { return false; }
};
// Constructs TablePropertiesCollector instances for each table file creation.
// Constructs TablePropertiesCollector. Internals create a new
// TablePropertiesCollector for each new table
//
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
@@ -162,12 +163,7 @@ class TablePropertiesCollectorFactory : public Customizable {
const ConfigOptions& options, const std::string& value,
std::shared_ptr<TablePropertiesCollectorFactory>* result);
// To collect properties of a table with the given context, returns
// a new object inheriting from TablePropertiesCollector. The caller
// is responsible for deleting the object returned. Alternatively,
// nullptr may be returned to decline collecting properties for the
// file (and reduce callback overheads).
// MUST be thread-safe.
// has to be thread-safe
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
TablePropertiesCollectorFactory::Context context) = 0;
@@ -10,6 +10,7 @@
// inserted.
#pragma once
#include <memory>
#include <string>
#include <vector>
@@ -29,7 +30,6 @@ class DB;
class ReadCallback;
struct ReadOptions;
struct DBOptions;
class MergeContext;
enum WriteType {
kPutRecord,
@@ -39,12 +39,11 @@ enum WriteType {
kDeleteRangeRecord,
kLogDataRecord,
kXIDRecord,
kPutEntityRecord,
kUnknownRecord,
};
// An entry for Put, PutEntity, Merge, Delete, or SingleDelete for write
// batches. Used in WBWIIterator.
// an entry for Put, Merge, Delete, or SingleDelete entry for write batches.
// Used in WBWIIterator.
struct WriteEntry {
WriteType type = kUnknownRecord;
Slice key;
@@ -78,11 +77,12 @@ class WBWIIterator {
};
// A WriteBatchWithIndex with a binary searchable index built for all the keys
// inserted. In Put(), PutEntity(), Merge(), Delete(), or SingleDelete(), the
// corresponding function of the wrapped WriteBatch will be called. At the same
// time, indexes will be built. By calling GetWriteBatch(), a user will get the
// WriteBatch for the data they inserted, which can be used for DB::Write(). A
// user can call NewIterator() to create an iterator.
// inserted.
// In Put(), Merge() Delete(), or SingleDelete(), the same function of the
// wrapped will be called. At the same time, indexes will be built.
// By calling GetWriteBatch(), a user will get the WriteBatch for the data
// they inserted, which can be used for DB::Write().
// A user can call NewIterator() to create an iterator.
class WriteBatchWithIndex : public WriteBatchBase {
public:
// backup_index_comparator: the backup comparator used to compare keys
@@ -121,7 +121,14 @@ class WriteBatchWithIndex : public WriteBatchBase {
}
Status PutEntity(ColumnFamilyHandle* column_family, const Slice& /* key */,
const WideColumns& /* columns */) override;
const WideColumns& /* columns */) override {
if (!column_family) {
return Status::InvalidArgument(
"Cannot call this method without a column family handle");
}
return Status::NotSupported(
"PutEntity not supported by WriteBatchWithIndex");
}
Status PutEntity(const Slice& /* key */,
const AttributeGroups& attribute_groups) override {
@@ -229,8 +236,6 @@ class WriteBatchWithIndex : public WriteBatchBase {
return GetFromBatch(nullptr, options, key, value);
}
// TODO: implement GetEntityFromBatch
// Similar to DB::Get() but will also read writes from this batch.
//
// This function will query both this batch and the DB and then merge
@@ -257,24 +262,21 @@ class WriteBatchWithIndex : public WriteBatchBase {
ColumnFamilyHandle* column_family, const Slice& key,
PinnableSlice* value);
// TODO: implement GetEntityFromBatchAndDB
void MultiGetFromBatchAndDB(DB* db, const ReadOptions& read_options,
ColumnFamilyHandle* column_family,
const size_t num_keys, const Slice* keys,
PinnableSlice* values, Status* statuses,
bool sorted_input);
// TODO: implement MultiGetEntityFromBatchAndDB
// Records the state of the batch for future calls to RollbackToSavePoint().
// May be called multiple times to set multiple save points.
void SetSavePoint() override;
// Remove all entries in this batch (Put, PutEntity, Merge, Delete,
// SingleDelete, PutLogData) since the most recent call to SetSavePoint() and
// removes the most recent save point. If there is no previous call to
// SetSavePoint(), behaves the same as Clear().
// Remove all entries in this batch (Put, Merge, Delete, SingleDelete,
// PutLogData) since the most recent call to SetSavePoint() and removes the
// most recent save point.
// If there is no previous call to SetSavePoint(), behaves the same as
// Clear().
//
// Calling RollbackToSavePoint invalidates any open iterators on this batch.
//
@@ -303,11 +305,6 @@ class WriteBatchWithIndex : public WriteBatchBase {
// last sub-batch.
size_t SubBatchCnt();
void MergeAcrossBatchAndDB(ColumnFamilyHandle* column_family,
const Slice& key,
const PinnableWideColumns& existing,
const MergeContext& merge_context,
PinnableSlice* value, Status* status);
Status GetFromBatchAndDB(DB* db, const ReadOptions& read_options,
ColumnFamilyHandle* column_family, const Slice& key,
PinnableSlice* value, ReadCallback* callback);
+1 -1
View File
@@ -12,7 +12,7 @@
// NOTE: in 'main' development branch, this should be the *next*
// minor or major version number planned for release.
#define ROCKSDB_MAJOR 8
#define ROCKSDB_MINOR 10
#define ROCKSDB_MINOR 9
#define ROCKSDB_PATCH 2
// Do not use these. We made the mistake of declaring macros starting with
-2
View File
@@ -168,7 +168,6 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/FlushJobInfo.java
src/main/java/org/rocksdb/FlushReason.java
src/main/java/org/rocksdb/FlushOptions.java
src/main/java/org/rocksdb/GetStatus.java
src/main/java/org/rocksdb/HashLinkedListMemTableConfig.java
src/main/java/org/rocksdb/HashSkipListMemTableConfig.java
src/main/java/org/rocksdb/HistogramData.java
@@ -286,7 +285,6 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/WriteBufferManager.java
src/main/java/org/rocksdb/WriteStallCondition.java
src/main/java/org/rocksdb/WriteStallInfo.java
src/main/java/org/rocksdb/util/BufferUtil.java
src/main/java/org/rocksdb/util/ByteUtil.java
src/main/java/org/rocksdb/util/BytewiseComparator.java
src/main/java/org/rocksdb/util/Environment.java
@@ -8,16 +8,16 @@ Mac
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.10.0-osx.jar rocksdbjni-7.10.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.9.0-osx.jar rocksdbjni-7.9.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
```
Linux
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.10.0-linux64.jar rocksdbjni-7.10.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.9.0-linux64.jar rocksdbjni-7.9.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
```
Build jmh test package, on either platform
@@ -35,10 +35,31 @@ The long performance run (as big as we can make it on our Ubuntu box without fil
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
```
## Results (small runs, Mac)
These are run on a 10-core M1 with 64GB of memory and 2TB of SSD.
They probably reflect the absolute best case for this optimization, hitting in-memory buffers and completely eliminating a buffer copy.
### Before
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 43496.578 ± 5743.090 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 70765.578 ± 697.548 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 69883.554 ± 944.184 ops/s
### After fixing byte[] (.get and .preallocatedGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 149207.681 ± 2261.671 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 68920.489 ± 1574.664 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 177399.022 ± 2107.375 ops/s
### After fixing ByteBuffer (.preallocatedByteBufferGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 150389.259 ± 1371.473 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 179919.468 ± 1670.714 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 178261.938 ± 2630.571 ops/s
## Results (Ubuntu, big runs)
NB - we have removed some test results we initially observed on Mac which were not later reproducible.
These take 3-4 hours
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
@@ -46,13 +67,6 @@ java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,500
It's clear that all `get()` variants have noticeably improved performance, though not the spectacular gains of the M1.
### With fixes for all of the `get()` instances
The tests which use methods which have had performance improvements applied are:
```java
get()
preallocatedGet()
preallocatedByteBufferGet()
```
Benchmark (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get 1_column_family 1000 128 1024 thrpt 25 935648.793 ± 22879.910 ops/s
GetBenchmarks.get 1_column_family 1000 128 16384 thrpt 25 204366.301 ± 1326.570 ops/s
@@ -145,60 +159,3 @@ GetBenchmarks.preallocatedGet no_column_families 1000
The performance improvement is real.
# Put Performance Benchmarks
Results associated with [Java API consistency between RocksDB.put() , .merge() and Transaction.put() , .merge()](https://github.com/facebook/rocksdb/pull/11019)
This work was not designed specifically as a performance optimization, but we want to confirm that it has not regressed what it has changed, and to provide
a baseline for future possible performance work.
## Build/Run
Building is as above. Running is a different invocation of the same JMH jar.
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,32768 -p columnFamilyTestType="no_column_family" PutBenchmarks
```
## Before Changes
These results were generated in a private branch with the `PutBenchmarks` from the PR backported onto the current *main*.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 76670.200 ± 2555.248 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3913.692 ± 225.690 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74479.589 ± 988.361 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4070.800 ± 194.838 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 72150.853 ± 1744.216 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 3896.646 ± 188.629 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71753.287 ± 1053.904 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 3928.503 ± 264.443 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 72595.105 ± 1027.258 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 3890.100 ± 199.131 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 70878.133 ± 1181.601 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3863.181 ± 215.888 ops/s
## After Changes
These results were generated on the PR branch.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 75178.751 ± 2644.775 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3937.175 ± 257.039 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74375.519 ± 1776.654 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4013.413 ± 257.706 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 71418.303 ± 1610.977 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 4027.581 ± 227.900 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71229.107 ± 2720.083 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 4022.635 ± 212.540 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 71718.501 ± 787.537 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 4078.050 ± 176.331 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 72736.754 ± 828.971 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3987.232 ± 205.577 ops/s
## Discussion
The changes don't appear to have had a material effect on performance. We are happy with this.
* We would obviously advise running future changes before and after to confirm they have no adverse effects.
-4
View File
@@ -150,9 +150,7 @@ JAVA_TESTS = \
org.rocksdb.LRUCacheTest\
org.rocksdb.MemoryUtilTest\
org.rocksdb.MemTableTest\
org.rocksdb.MergeCFVariantsTest\
org.rocksdb.MergeTest\
org.rocksdb.MergeVariantsTest\
org.rocksdb.MultiColumnRegressionTest \
org.rocksdb.MultiGetManyKeysTest\
org.rocksdb.MultiGetTest\
@@ -169,8 +167,6 @@ JAVA_TESTS = \
org.rocksdb.OptionsTest\
org.rocksdb.PerfLevelTest \
org.rocksdb.PerfContextTest \
org.rocksdb.PutCFVariantsTest\
org.rocksdb.PutVariantsTest\
org.rocksdb.PlainTableConfigTest\
org.rocksdb.RateLimiterTest\
org.rocksdb.ReadOnlyTest\
@@ -6,20 +6,19 @@
*/
package org.rocksdb.jmh;
import static org.rocksdb.util.KVUtils.ba;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.openjdk.jmh.annotations.*;
import org.rocksdb.*;
import org.rocksdb.util.FileUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.rocksdb.util.KVUtils.ba;
@State(Scope.Benchmark)
public class PutBenchmarks {
@@ -31,24 +30,12 @@ public class PutBenchmarks {
})
String columnFamilyTestType;
@Param({"1000", "100000"}) int keyCount;
@Param({"12", "64", "128"}) int keySize;
@Param({"64", "1024", "65536"}) int valueSize;
@Param({"16"}) int bufferListSize;
Path dbDir;
DBOptions options;
int cfs = 0; // number of column families
private AtomicInteger cfHandlesIdx;
ColumnFamilyHandle[] cfHandles;
RocksDB db;
List<byte[]> keyBuffers = new ArrayList<>(bufferListSize);
List<byte[]> valueBuffers = new ArrayList<>(bufferListSize);
List<ByteBuffer> keyBuffersBB = new ArrayList<>(bufferListSize);
List<ByteBuffer> valueBuffersBB = new ArrayList<>(bufferListSize);
@Setup(Level.Trial)
public void setup() throws IOException, RocksDBException {
@@ -81,34 +68,6 @@ public class PutBenchmarks {
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size());
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList);
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]);
for (int i = 0; i < bufferListSize; i++) {
final byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBuffers.add(keyArr);
}
for (int i = 0; i < bufferListSize; i++) {
final byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBuffers.add(valueArr);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer keyBB = ByteBuffer.allocateDirect(keySize);
byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBB.put(keyArr);
keyBuffersBB.add(keyBB);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer valueBB = ByteBuffer.allocateDirect(valueSize);
byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBB.put(valueArr);
valueBuffersBB.add(valueBB);
}
}
@TearDown(Level.Trial)
@@ -145,79 +104,9 @@ public class PutBenchmarks {
}
}
private <T> T borrow(final List<T> buffers) {
synchronized (buffers) {
while (true) {
if (buffers.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
return null;
}
continue;
}
return buffers.remove(0);
}
}
}
private <T> void repay(final List<T> buffers, final T buffer) {
synchronized (buffers) {
buffers.add(buffer);
}
}
@Benchmark
public void put(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
public void put(final ComparatorBenchmarks.Counter counter) throws RocksDBException {
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteArrays(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteBuffers(final Counter counter) throws RocksDBException {
ByteBuffer keyBuf = borrow(keyBuffersBB);
keyBuf.clear();
ByteBuffer valueBuf = borrow(valueBuffersBB);
valueBuf.clear();
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
keyBuf.put(keyPrefix, 0, keyPrefix.length);
keyBuf.position(keySize);
keyBuf.flip();
valueBuf.put(valuePrefix, 0, valuePrefix.length);
valueBuf.position(valueSize);
valueBuf.flip();
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffersBB, keyBuf);
repay(valueBuffersBB, valueBuf);
db.put(getColumnFamily(), ba("key" + i), ba("value" + i));
}
}
-1
View File
@@ -21,7 +21,6 @@
<exclude name="FieldNamingConventions"/>
<exclude name="FormalParameterNamingConventions"/>
<exclude name="LinguisticNaming"/>
<exclude name="ConfusingTernary"/>
<!-- These could be fixed if we take the time to do it -->
<exclude name="CommentDefaultAccessModifier"/>
<exclude name="FieldDeclarationsShouldBeAtStartOfClass"/>
-284
View File
@@ -1,284 +0,0 @@
// 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).
//
// This file defines helper methods for Java API write methods
//
#pragma once
#include <jni.h>
#include <cstring>
#include <exception>
#include <functional>
#include <string>
#include "rocksdb/rocksdb_namespace.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "rocksjni/portal.h"
namespace ROCKSDB_NAMESPACE {
/**
* @brief Exception class used to make the flow of key/value (Put(), Get(),
* Merge(), ...) calls clearer.
*
* This class is used by Java API JNI methods in try { save/fetch } catch { ...
* } style.
*
*/
class KVException : public std::exception {
public:
// These values are expected on Java API calls to represent the result of a
// Get() which has failed; a negative length is returned to indicate an error.
static const int kNotFound = -1; // the key was not found in RocksDB
static const int kStatusError =
-2; // there was some other error fetching the value for the key
/**
* @brief Throw a KVException (and potentially a Java exception) if the
* RocksDB status is "bad"
*
* @param env JNI environment needed to create a Java exception
* @param status RocksDB status to examine
*/
static void ThrowOnError(JNIEnv* env, const Status& status) {
if (status.ok()) {
return;
}
if (status.IsNotFound()) {
// IsNotFound does not generate a Java Exception, any other bad status
// does..
throw KVException(kNotFound);
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException and a Java exception
*
* @param env JNI environment needed to create a Java exception
* @param message content of the exception we will throw
*/
static void ThrowNew(JNIEnv* env, const std::string& message) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, message);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException if there is already a Java exception in the JNI
* enviroment
*
* @param env
*/
static void ThrowOnError(JNIEnv* env) {
if (env->ExceptionCheck()) {
throw KVException(kStatusError);
}
}
KVException(jint code) : kCode_(code){};
virtual const char* what() const throw() {
return "Exception raised by JNI. There may be a Java exception in the "
"JNIEnv. Please check!";
}
jint Code() const { return kCode_; }
private:
jint kCode_;
};
/**
* @brief Construct a slice with the contents of a Java byte array
*
* The slice refers to an array into which the Java byte array's whole region is
* copied
*/
class JByteArraySlice {
public:
JByteArraySlice(JNIEnv* env, const jbyteArray& jarr, const jint jarr_off,
const jint jarr_len)
: arr_(new jbyte[jarr_len]),
slice_(reinterpret_cast<char*>(arr_), jarr_len) {
env->GetByteArrayRegion(jarr, jarr_off, jarr_len, arr_);
KVException::ThrowOnError(env);
};
~JByteArraySlice() {
slice_.clear();
delete[] arr_;
};
Slice& slice() { return slice_; }
private:
jbyte* arr_;
Slice slice_;
};
/**
* @brief Construct a slice with the contents of a direct Java ByterBuffer
*
* The slice refers directly to the contents of the buffer, no copy is made.
*
*/
class JDirectBufferSlice {
public:
JDirectBufferSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: slice_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off,
jbuffer_len) {
KVException::ThrowOnError(env);
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message = "Direct buffer offset " + std::to_string(jbuffer_off) +
" + length " + std::to_string(jbuffer_len) +
" exceeds capacity " + std::to_string(capacity);
KVException::ThrowNew(env, message);
slice_.clear();
}
}
~JDirectBufferSlice() { slice_.clear(); };
Slice& slice() { return slice_; }
private:
Slice slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Byte Array version sets the byte array's region from the slice
*/
class JByteArrayPinnableSlice {
public:
/**
* @brief Construct a new JByteArrayPinnableSlice object referring to an
* existing java byte buffer
*
* @param env
* @param jbuffer
* @param jbuffer_off
* @param jbuffer_len
*/
JByteArrayPinnableSlice(JNIEnv* env, const jbyteArray& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: env_(env),
jbuffer_(jbuffer),
jbuffer_off_(jbuffer_off),
jbuffer_len_(jbuffer_len){};
/**
* @brief Construct an empty new JByteArrayPinnableSlice object
*
*/
JByteArrayPinnableSlice(JNIEnv* env) : env_(env){};
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JByteArrayPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java ByteBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
env_->SetByteArrayRegion(
jbuffer_, jbuffer_off_, result_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(
env_); // exception thrown: ArrayIndexOutOfBoundsException
return pinnable_len;
};
/**
* @brief create a new Java buffer and copy the result into it
*
* @return jbyteArray the java buffer holding the result
*/
jbyteArray NewByteArray() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
jbyteArray jbuffer = env_->NewByteArray(static_cast<jsize>(pinnable_len));
KVException::ThrowOnError(env_); // OutOfMemoryError
env_->SetByteArrayRegion(
jbuffer, 0, pinnable_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(env_); // ArrayIndexOutOfBoundsException
return jbuffer;
}
private:
JNIEnv* env_;
jbyteArray jbuffer_;
jint jbuffer_off_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Direct Buffer version copies the memory of the buffer from the slice
*/
class JDirectBufferPinnableSlice {
public:
JDirectBufferPinnableSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: buffer_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off),
jbuffer_len_(jbuffer_len) {
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message =
"Invalid value argument. Capacity is less than requested region. "
"offset " +
std::to_string(jbuffer_off) + " + length " +
std::to_string(jbuffer_len) + " exceeds capacity " +
std::to_string(capacity);
KVException::ThrowNew(env, message);
}
}
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JDirectBufferPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java DirectBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
memcpy(buffer_, pinnable_slice_.data(), result_len);
return pinnable_len;
};
private:
char* buffer_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
} // namespace ROCKSDB_NAMESPACE
-20
View File
@@ -5175,15 +5175,6 @@ class TickerTypeJni {
return -0x41;
case ROCKSDB_NAMESPACE::Tickers::PREFETCH_HITS:
return -0x42;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_DUMMY_HITS:
return -0x43;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_HITS:
return -0x44;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_PROMOTIONS:
return -0x45;
case ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS:
return -0x46;
case ROCKSDB_NAMESPACE::Tickers::TICKER_ENUM_MAX:
// 0x5F was the max value in the initial copy of tickers to Java.
// Since these values are exposed directly to Java clients, we keep
@@ -5559,17 +5550,6 @@ class TickerTypeJni {
return ROCKSDB_NAMESPACE::Tickers::PREFETCH_BYTES_USEFUL;
case -0x42:
return ROCKSDB_NAMESPACE::Tickers::PREFETCH_HITS;
case -0x43:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_DUMMY_HITS;
case -0x44:
return ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_HITS;
case -0x45:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTIONS;
case -0x46:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS;
case 0x5F:
// 0x5F was the max value in the initial copy of tickers to Java.
// Since these values are exposed directly to Java clients, we keep
+168 -109
View File
@@ -26,7 +26,6 @@
#include "rocksdb/types.h"
#include "rocksdb/version.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#ifdef min
@@ -601,6 +600,55 @@ void Java_org_rocksdb_RocksDB_dropColumnFamilies(
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Put
/**
* @return true if the put succeeded, false if a Java Exception was thrown
*/
bool rocksdb_put_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Put(write_options, cf_handle, key_slice, value_slice);
} else {
// backwards compatibility
s = db->Put(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: put
@@ -614,14 +662,8 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_put_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
}
/*
@@ -640,21 +682,13 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BIIJ(JNIEnv* env, jobject,
ROCKSDB_NAMESPACE::WriteOptions();
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle == nullptr) {
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Put(default_write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@@ -672,15 +706,8 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_put_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
}
/*
@@ -697,19 +724,13 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BIIJ(
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle == nullptr) {
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@@ -1288,6 +1309,53 @@ jint Java_org_rocksdb_RocksDB_getDirect(JNIEnv* env, jobject /*jdb*/,
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Merge
/**
* @return true if the merge succeeded, false if a Java Exception was thrown
*/
bool rocksdb_merge_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Merge(write_options, cf_handle, key_slice, value_slice);
} else {
s = db->Merge(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
/*
* Class: org_rocksdb_RocksDB
* Method: merge
@@ -1301,14 +1369,8 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_merge_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
}
/*
@@ -1326,15 +1388,8 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, cf_handle, key.slice(),
value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_merge_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@@ -1354,14 +1409,8 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BII(
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_merge_helper(env, db, *write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
}
/*
@@ -1379,15 +1428,8 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Merge(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
rocksdb_merge_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@@ -1395,37 +1437,16 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: mergeDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_RocksDB_mergeDirect(
JNIEnv* env, jobject /*jdb*/, jlong jdb_handle, jlong jwrite_options_handle,
jobject jkey, jint jkey_off, jint jkey_len, jobject jval, jint jval_off,
jint jval_len, jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto merge = [&env, &db, &cf_handle, &write_options](
ROCKSDB_NAMESPACE::Slice& key,
ROCKSDB_NAMESPACE::Slice& value) {
ROCKSDB_NAMESPACE::Status s;
if (cf_handle == nullptr) {
s = db->Merge(*write_options, key, value);
} else {
s = db->Merge(*write_options, cf_handle, key, value);
}
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
};
ROCKSDB_NAMESPACE::JniUtil::kv_op_direct(merge, env, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
jlong rocksdb_iterator_helper(
ROCKSDB_NAMESPACE::DB* db, ROCKSDB_NAMESPACE::ReadOptions read_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle) {
ROCKSDB_NAMESPACE::Iterator* iterator = nullptr;
if (cf_handle != nullptr) {
iterator = db->NewIterator(read_options, cf_handle);
} else {
iterator = db->NewIterator(read_options);
}
return GET_CPLUSPLUS_POINTER(iterator);
}
/*
@@ -2623,17 +2644,55 @@ jobjectArray Java_org_rocksdb_RocksDB_keyMayExistFoundValue(
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (J)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__J(JNIEnv*, jobject, jlong db_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(), nullptr);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__JJ(JNIEnv*, jobject, jlong db_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return rocksdb_iterator_helper(db, read_options, nullptr);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iteratorCF
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_RocksDB_iteratorCF__JJ(JNIEnv*, jobject, jlong db_handle,
jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(),
cf_handle);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iteratorCF
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_RocksDB_iterator(JNIEnv*, jobject, jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
jlong Java_org_rocksdb_RocksDB_iteratorCF__JJJ(JNIEnv*, jobject,
jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return GET_CPLUSPLUS_POINTER(db->NewIterator(read_options, cf_handle));
return rocksdb_iterator_helper(db, read_options, cf_handle);
}
/*
+229 -353
View File
@@ -14,7 +14,6 @@
#include "include/org_rocksdb_Transaction.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#if defined(_MSC_VER)
@@ -159,113 +158,90 @@ typedef std::function<ROCKSDB_NAMESPACE::Status(
std::string*)>
FnGet;
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BIIJ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
jbyteArray txn_get_helper(JNIEnv* env, const FnGet& fn_get,
const jlong& jread_options_handle,
const jbyteArray& jkey, const jint& jkey_part_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BII)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, key.slice(), &value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
std::string value;
ROCKSDB_NAMESPACE::Status s = fn_get(*read_options, key_slice, &value);
// trigger java unref on key.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (s.IsNotFound()) {
return nullptr;
}
if (s.ok()) {
jbyteArray jret_value = env->NewByteArray(static_cast<jsize>(value.size()));
if (jret_value == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
env->SetByteArrayRegion(
jret_value, 0, static_cast<jsize>(value.size()),
const_cast<jbyte*>(reinterpret_cast<const jbyte*>(value.c_str())));
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
return nullptr;
}
return jret_value;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return nullptr;
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BII[BIIJ)I
* Signature: (JJ[BIJ)[B
*/
jint Java_org_rocksdb_Transaction_get__JJ_3BII_3BIIJ(
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_part_len, jlong jcolumn_family_handle) {
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
column_family_handle, std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)I
* Method: get
* Signature: (JJ[BI)[B
*/
jint Java_org_rocksdb_Transaction_getDirect(JNIEnv* env, jobject, jlong jhandle,
jlong jread_options_handle,
jobject jkey_bb, jint jkey_off,
jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_part_len,
jlong jcolumn_family_handle) {
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
}
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
@@ -452,91 +428,47 @@ jobjectArray Java_org_rocksdb_Transaction_multiGet__JJ_3_3B(
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BIIJZZ)[B
* Signature: (JJ[BIJZZ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIIJZZ(
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIJZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle, jboolean jexclusive, jboolean jdo_validate) {
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, column_family_handle, std::placeholders::_2,
std::placeholders::_3, jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BII[BIIJZZ)I
* Signature: (JJ[BIZZ)[B
*/
jint Java_org_rocksdb_Transaction_getForUpdate__JJ_3BII_3BIIJZZ(
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
jbyteArray jkey, jint jkey_part_len, jboolean jexclusive,
jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirectForUpdate
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZZ)I
*/
jint Java_org_rocksdb_Transaction_getDirectForUpdate(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jobject jkey_bb, jint jkey_off, jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
}
/*
@@ -587,15 +519,29 @@ jobjectArray Java_org_rocksdb_Transaction_multiGetForUpdate__JJ_3_3B(
jread_options_handle, jkey_parts);
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator__JJ(JNIEnv* /*env*/,
jobject /*jobj*/,
jlong jhandle,
jlong jread_options_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return GET_CPLUSPLUS_POINTER(txn->GetIterator(*read_options));
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator(JNIEnv* /*env*/,
jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle,
jlong jcolumn_family_handle) {
jlong Java_org_rocksdb_Transaction_getIterator__JJJ(
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
@@ -606,93 +552,84 @@ jlong Java_org_rocksdb_Transaction_getIterator(JNIEnv* /*env*/,
txn->GetIterator(*read_options, column_family_handle));
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BII[BIIJZ)V
*/
void Java_org_rocksdb_Transaction_put__J_3BII_3BIIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
typedef std::function<ROCKSDB_NAMESPACE::Status(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>
FnWriteKV;
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
void txn_write_kv_helper(JNIEnv* env, const FnWriteKV& fn_write_kv,
const jbyteArray& jkey, const jint& jkey_part_len,
const jbyteArray& jval, const jint& jval_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
return;
}
jbyte* value = env->GetByteArrayElements(jval, nullptr);
if (value == nullptr) {
// exception thrown: OutOfMemoryError
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
return;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s = fn_write_kv(key_slice, value_slice);
// trigger java unref on key.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jval, value, JNI_ABORT);
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BII[BII)V
* Signature: (J[BI[BIJZ)V
*/
void Java_org_rocksdb_Transaction_put__J_3BII_3BII(
void Java_org_rocksdb_Transaction_put__J_3BI_3BIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Put, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
* Method: put
* Signature: (J[BI[BI)V
*/
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
void Java_org_rocksdb_Transaction_put__J_3BI_3BI(JNIEnv* env, jobject /*jobj*/,
jlong jhandle, jbyteArray jkey,
jint jkey_part_len,
jbyteArray jval,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Put, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@@ -843,92 +780,41 @@ void Java_org_rocksdb_Transaction_put__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BII[BIIJZ)V
* Signature: (J[BI[BIJZ)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BII_3BIIJZ(
void Java_org_rocksdb_Transaction_merge__J_3BI_3BIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BII[BII)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BII_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Merge, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
* Method: merge
* Signature: (J[BI[BI)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
void Java_org_rocksdb_Transaction_merge__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Merge, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@@ -1179,15 +1065,14 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BIJ(
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->PutUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
@@ -1199,14 +1084,13 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->PutUntracked(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
@@ -1253,50 +1137,42 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BII[BIIJ)V
* Signature: (J[BI[BIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked(
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntrackedDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntrackedDirect(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jobject jkey, jint jkey_off,
jint jkey_part_len, jobject jval, jint jval_off, jint jval_len,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BI[BI)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
@@ -1,32 +0,0 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// 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;
/**
* The result for a fetch
* and the total size of the object fetched.
* If the target of the fetch is not big enough, this may be bigger than the contents of the target.
*/
public class GetStatus {
public final Status status;
public final int requiredSize;
/**
* Constructor used for success status, when the value is contained in the buffer
*
* @param status the status of the request to fetch into the buffer
* @param requiredSize the size of the data, which may be bigger than the buffer
*/
GetStatus(final Status status, final int requiredSize) {
this.status = status;
this.requiredSize = requiredSize;
}
static GetStatus fromStatusCode(final Status.Code code, final int requiredSize) {
return new GetStatus(new Status(code, Status.SubCode.getSubCode((byte) 0), null), requiredSize);
}
}
Loaded 100 of 217 files, more files were not shown because too many files have changed in this diff. Show more