Fb 9718 verify checksums is ignored (#9767)
Summary: Fixes https://github.com/facebook/rocksdb/issues/9718 The verify_checksums flag of read_options should be passed to the read options used by the BlockFetcher in a couple of cases where it is not at present. It will now happen (but did not, previously) on iteration and on [multi]get, where a fetcher is created as part of the iterate/get call. This may result in much better performance in a few workloads where the client chooses to remove verification. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9767 Reviewed By: mrambacher Differential Revision: D35218986 Pulled By: jay-zhuang fbshipit-source-id: 329d29764bb70fbc7f2673440bc46c107a813bc8main
parent
a5e5130556
commit
b6ad0d958f
@ -0,0 +1,213 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
import java.text.MessageFormat; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import org.junit.ClassRule; |
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.TemporaryFolder; |
||||||
|
|
||||||
|
public class VerifyChecksumsTest { |
||||||
|
@ClassRule |
||||||
|
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE = |
||||||
|
new RocksNativeLibraryResource(); |
||||||
|
|
||||||
|
@Rule public TemporaryFolder dbFolder = new TemporaryFolder(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Class to factor out the specific DB operations within the test |
||||||
|
*/ |
||||||
|
abstract static class Operations { |
||||||
|
final int kv_count; |
||||||
|
final List<String> elements = new ArrayList<>(); |
||||||
|
final List<String> sortedElements = new ArrayList<>(); |
||||||
|
|
||||||
|
Operations(final int kv_count) { |
||||||
|
this.kv_count = kv_count; |
||||||
|
for (int i = 0; i < kv_count; i++) elements.add(MessageFormat.format("{0,number,#}", i)); |
||||||
|
sortedElements.addAll(elements); |
||||||
|
Collections.sort(sortedElements); |
||||||
|
} |
||||||
|
|
||||||
|
void fill(final RocksDB db) throws RocksDBException { |
||||||
|
for (int i = 0; i < kv_count; i++) { |
||||||
|
final String key = MessageFormat.format("key{0}", elements.get(i)); |
||||||
|
final String value = MessageFormat.format("value{0}", elements.get(i)); |
||||||
|
// noinspection ObjectAllocationInLoop
|
||||||
|
db.put(key.getBytes(), value.getBytes()); |
||||||
|
} |
||||||
|
db.flush(new FlushOptions()); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("ObjectAllocationInLoop") |
||||||
|
void get(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
try (final ReadOptions readOptions = new ReadOptions()) { |
||||||
|
readOptions.setReadaheadSize(32 * 1024); |
||||||
|
readOptions.setFillCache(false); |
||||||
|
readOptions.setVerifyChecksums(verifyFlag); |
||||||
|
|
||||||
|
for (int i = 0; i < kv_count / 10; i++) { |
||||||
|
@SuppressWarnings("UnsecureRandomNumberGeneration") |
||||||
|
final int index = Double.valueOf(Math.random() * kv_count).intValue(); |
||||||
|
final String key = MessageFormat.format("key{0}", sortedElements.get(index)); |
||||||
|
final String expectedValue = MessageFormat.format("value{0}", sortedElements.get(index)); |
||||||
|
|
||||||
|
final byte[] value = db.get(readOptions, key.getBytes()); |
||||||
|
assertThat(value).isEqualTo(expectedValue.getBytes()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("ObjectAllocationInLoop") |
||||||
|
void multiGet(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
try (final ReadOptions readOptions = new ReadOptions()) { |
||||||
|
readOptions.setReadaheadSize(32 * 1024); |
||||||
|
readOptions.setFillCache(false); |
||||||
|
readOptions.setVerifyChecksums(verifyFlag); |
||||||
|
|
||||||
|
final List<byte[]> keys = new ArrayList<>(); |
||||||
|
final List<String> expectedValues = new ArrayList<>(); |
||||||
|
|
||||||
|
for (int i = 0; i < kv_count / 10; i++) { |
||||||
|
@SuppressWarnings("UnsecureRandomNumberGeneration") |
||||||
|
final int index = Double.valueOf(Math.random() * kv_count).intValue(); |
||||||
|
keys.add(MessageFormat.format("key{0}", sortedElements.get(index)).getBytes()); |
||||||
|
|
||||||
|
expectedValues.add(MessageFormat.format("value{0}", sortedElements.get(index))); |
||||||
|
} |
||||||
|
|
||||||
|
final List<byte[]> values = db.multiGetAsList(readOptions, keys); |
||||||
|
for (int i = 0; i < keys.size(); i++) { |
||||||
|
assertThat(values.get(i)).isEqualTo(expectedValues.get(i).getBytes()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void iterate(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
final ReadOptions readOptions = new ReadOptions(); |
||||||
|
readOptions.setReadaheadSize(32 * 1024); |
||||||
|
readOptions.setFillCache(false); |
||||||
|
readOptions.setVerifyChecksums(verifyFlag); |
||||||
|
int i = 0; |
||||||
|
try (final RocksIterator rocksIterator = db.newIterator(readOptions)) { |
||||||
|
rocksIterator.seekToFirst(); |
||||||
|
rocksIterator.status(); |
||||||
|
while (rocksIterator.isValid()) { |
||||||
|
final byte[] key = rocksIterator.key(); |
||||||
|
final byte[] value = rocksIterator.value(); |
||||||
|
// noinspection ObjectAllocationInLoop
|
||||||
|
assertThat(key).isEqualTo( |
||||||
|
(MessageFormat.format("key{0}", sortedElements.get(i))).getBytes()); |
||||||
|
// noinspection ObjectAllocationInLoop
|
||||||
|
assertThat(value).isEqualTo( |
||||||
|
(MessageFormat.format("value{0}", sortedElements.get(i))).getBytes()); |
||||||
|
rocksIterator.next(); |
||||||
|
rocksIterator.status(); |
||||||
|
i++; |
||||||
|
} |
||||||
|
} |
||||||
|
assertThat(i).isEqualTo(kv_count); |
||||||
|
} |
||||||
|
|
||||||
|
abstract void performOperations(final RocksDB db, final boolean verifyFlag) |
||||||
|
throws RocksDBException; |
||||||
|
} |
||||||
|
|
||||||
|
private static final int KV_COUNT = 10000; |
||||||
|
|
||||||
|
/** |
||||||
|
* Run some operations and count the TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT before and after |
||||||
|
* It should GO UP when the read options have checksum verification turned on. |
||||||
|
* It shoulld REMAIN UNCHANGED when the read options have checksum verification turned off. |
||||||
|
* As the read options refer only to the read operations, there are still a few checksums |
||||||
|
* performed outside this (blocks are getting loaded for lots of reasons, not aways directly due |
||||||
|
* to reads) but this test provides a good enough proxy for whether the flag is being noticed. |
||||||
|
* |
||||||
|
* @param operations the DB reading operations to perform which affect the checksum stats |
||||||
|
* |
||||||
|
* @throws RocksDBException |
||||||
|
*/ |
||||||
|
private void verifyChecksums(final Operations operations) throws RocksDBException { |
||||||
|
final String dbPath = dbFolder.getRoot().getAbsolutePath(); |
||||||
|
|
||||||
|
// noinspection SingleStatementInBlock
|
||||||
|
try (final Statistics statistics = new Statistics(); |
||||||
|
final Options options = new Options().setCreateIfMissing(true).setStatistics(statistics)) { |
||||||
|
try (final RocksDB db = RocksDB.open(options, dbPath)) { |
||||||
|
// 0
|
||||||
|
System.out.println(MessageFormat.format( |
||||||
|
"newly open {0}", statistics.getTickerCount(TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT))); |
||||||
|
operations.fill(db); |
||||||
|
//
|
||||||
|
System.out.println(MessageFormat.format( |
||||||
|
"flushed {0}", statistics.getTickerCount(TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT))); |
||||||
|
} |
||||||
|
|
||||||
|
// 2
|
||||||
|
System.out.println(MessageFormat.format("closed-after-write {0}", |
||||||
|
statistics.getTickerCount(TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT))); |
||||||
|
|
||||||
|
for (final boolean verifyFlag : new boolean[] {false, true, false, true}) { |
||||||
|
try (final RocksDB db = RocksDB.open(options, dbPath)) { |
||||||
|
final long beforeOperationsCount = |
||||||
|
statistics.getTickerCount(TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT); |
||||||
|
System.out.println(MessageFormat.format("re-opened {0}", beforeOperationsCount)); |
||||||
|
operations.performOperations(db, verifyFlag); |
||||||
|
final long afterOperationsCount = |
||||||
|
statistics.getTickerCount(TickerType.BLOCK_CHECKSUM_COMPUTE_COUNT); |
||||||
|
if (verifyFlag) { |
||||||
|
// We don't need to be exact - we are checking that the checksums happen
|
||||||
|
// exactly how many depends on block size etc etc, so may not be entirely stable
|
||||||
|
System.out.println(MessageFormat.format("verify=true {0}", afterOperationsCount)); |
||||||
|
assertThat(afterOperationsCount).isGreaterThan(beforeOperationsCount + 20); |
||||||
|
} else { |
||||||
|
System.out.println(MessageFormat.format("verify=false {0}", afterOperationsCount)); |
||||||
|
assertThat(afterOperationsCount).isEqualTo(beforeOperationsCount); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void verifyChecksumsInIteration() throws RocksDBException { |
||||||
|
// noinspection AnonymousInnerClassMayBeStatic
|
||||||
|
verifyChecksums(new Operations(KV_COUNT) { |
||||||
|
@Override |
||||||
|
void performOperations(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
iterate(db, verifyFlag); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void verifyChecksumsGet() throws RocksDBException { |
||||||
|
// noinspection AnonymousInnerClassMayBeStatic
|
||||||
|
verifyChecksums(new Operations(KV_COUNT) { |
||||||
|
@Override |
||||||
|
void performOperations(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
get(db, verifyFlag); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void verifyChecksumsMultiGet() throws RocksDBException { |
||||||
|
// noinspection AnonymousInnerClassMayBeStatic
|
||||||
|
verifyChecksums(new Operations(KV_COUNT) { |
||||||
|
@Override |
||||||
|
void performOperations(final RocksDB db, final boolean verifyFlag) throws RocksDBException { |
||||||
|
multiGet(db, verifyFlag); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue