Cross functional test infrastructure for RocksDB.

Summary:
This Diff provides the implementation of the cross functional
test infrastructure. This provides the ability to test a single feature
with every existing regression test in order to identify issues with
interoperability between features.

Test Plan:
Reference implementation of inplace update support cross
functional test. Able to find interoperability issues with inplace
support and ran all of db_test. Will add separate diff for those changes.

Reviewers: igor, sdong

Reviewed By: sdong

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D32247
main
Venkatesh Radhakrishnan 10 years ago
parent 868bfa4033
commit 0b8dec7172
  1. 35
      db/db_test.cc
  2. 3
      include/rocksdb/options.h
  3. 27
      util/xfunc.cc
  4. 99
      util/xfunc.h

@ -53,6 +53,7 @@
#include "util/mock_env.h" #include "util/mock_env.h"
#include "util/string_util.h" #include "util/string_util.h"
#include "util/thread_status_util.h" #include "util/thread_status_util.h"
#include "util/xfunc.h"
namespace rocksdb { namespace rocksdb {
@ -115,6 +116,9 @@ class AtomicCounter {
struct OptionsOverride { struct OptionsOverride {
std::shared_ptr<const FilterPolicy> filter_policy = nullptr; std::shared_ptr<const FilterPolicy> filter_policy = nullptr;
// Used as a bit mask of individual enums in which to skip an XF test point
int skip_policy = 0;
}; };
} // namespace anon } // namespace anon
@ -564,6 +568,9 @@ class DBTest {
const anon::OptionsOverride& options_override = anon::OptionsOverride()) { const anon::OptionsOverride& options_override = anon::OptionsOverride()) {
// this redudant copy is to minimize code change w/o having lint error. // this redudant copy is to minimize code change w/o having lint error.
Options options = defaultOptions; Options options = defaultOptions;
XFUNC_TEST("", "dbtest_options", inplace_options1, GetXFTestOptions,
reinterpret_cast<Options*>(&options),
options_override.skip_policy);
BlockBasedTableOptions table_options; BlockBasedTableOptions table_options;
bool set_block_based_table_factory = true; bool set_block_based_table_factory = true;
switch (option_config_) { switch (option_config_) {
@ -1631,8 +1638,10 @@ TEST(DBTest, GetFromVersions) {
} }
TEST(DBTest, GetSnapshot) { TEST(DBTest, GetSnapshot) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions()); CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override));
// Try with both a short key and a long key // Try with both a short key and a long key
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
@ -2242,7 +2251,9 @@ TEST(DBTest, IterMulti) {
// Check that we can skip over a run of user keys // Check that we can skip over a run of user keys
// by using reseek rather than sequential scan // by using reseek rather than sequential scan
TEST(DBTest, IterReseek) { TEST(DBTest, IterReseek) {
Options options = CurrentOptions(); anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
Options options = CurrentOptions(options_override);
options.max_sequential_skip_in_iterations = 3; options.max_sequential_skip_in_iterations = 3;
options.create_if_missing = true; options.create_if_missing = true;
options.statistics = rocksdb::CreateDBStatistics(); options.statistics = rocksdb::CreateDBStatistics();
@ -5699,8 +5710,10 @@ TEST(DBTest, IteratorPinsRef) {
} }
TEST(DBTest, Snapshot) { TEST(DBTest, Snapshot) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions()); CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override));
Put(0, "foo", "0v1"); Put(0, "foo", "0v1");
Put(1, "foo", "1v1"); Put(1, "foo", "1v1");
@ -5760,8 +5773,10 @@ TEST(DBTest, Snapshot) {
} }
TEST(DBTest, HiddenValuesAreRemoved) { TEST(DBTest, HiddenValuesAreRemoved) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
Options options = CurrentOptions(); Options options = CurrentOptions(options_override);
options.max_background_flushes = 0; options.max_background_flushes = 0;
CreateAndReopenWithCF({"pikachu"}, options); CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301); Random rnd(301);
@ -5798,8 +5813,10 @@ TEST(DBTest, HiddenValuesAreRemoved) {
} }
TEST(DBTest, CompactBetweenSnapshots) { TEST(DBTest, CompactBetweenSnapshots) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
Options options = CurrentOptions(); Options options = CurrentOptions(options_override);
options.disable_auto_compactions = true; options.disable_auto_compactions = true;
CreateAndReopenWithCF({"pikachu"}, options); CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301); Random rnd(301);
@ -6908,8 +6925,10 @@ TEST(DBTest, SnapshotFiles) {
} }
TEST(DBTest, CompactOnFlush) { TEST(DBTest, CompactOnFlush) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
Options options = CurrentOptions(); Options options = CurrentOptions(options_override);
options.purge_redundant_kvs_while_flush = true; options.purge_redundant_kvs_while_flush = true;
options.disable_auto_compactions = true; options.disable_auto_compactions = true;
CreateAndReopenWithCF({"pikachu"}, options); CreateAndReopenWithCF({"pikachu"}, options);
@ -7641,12 +7660,14 @@ static void MTThreadBody(void* arg) {
} // namespace } // namespace
TEST(DBTest, MultiThreaded) { TEST(DBTest, MultiThreaded) {
anon::OptionsOverride options_override;
options_override.skip_policy = kSkipNoSnapshot;
do { do {
std::vector<std::string> cfs; std::vector<std::string> cfs;
for (int i = 1; i < kColumnFamilies; ++i) { for (int i = 1; i < kColumnFamilies; ++i) {
cfs.push_back(ToString(i)); cfs.push_back(ToString(i));
} }
CreateAndReopenWithCF(cfs, CurrentOptions()); CreateAndReopenWithCF(cfs, CurrentOptions(options_override));
// Initialize state // Initialize state
MTState mt; MTState mt;
mt.test = this; mt.test = this;

@ -501,7 +501,8 @@ struct ColumnFamilyOptions {
// Allows thread-safe inplace updates. If this is true, there is no way to // Allows thread-safe inplace updates. If this is true, there is no way to
// achieve point-in-time consistency using snapshot or iterator (assuming // achieve point-in-time consistency using snapshot or iterator (assuming
// concurrent updates). // concurrent updates). Hence iterator and multi-get will return results
// which are not consistent as of any point-in-time.
// If inplace_callback function is not set, // If inplace_callback function is not set,
// Put(key, new_value) will update inplace the existing_value iff // Put(key, new_value) will update inplace the existing_value iff
// * key exists in current memtable // * key exists in current memtable

@ -0,0 +1,27 @@
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
#include <string>
#include "rocksdb/options.h"
#include "util/xfunc.h"
#ifdef XFUNC
namespace rocksdb {
std::string XFuncPoint::xfunc_test_;
bool XFuncPoint::initialized_ = false;
bool XFuncPoint::enabled_ = false;
void GetXFTestOptions(Options* options, int skip_policy) {
if (XFuncPoint::Check("inplace_lock_test") &&
(!(skip_policy & kSkipNoSnapshot))) {
options->inplace_update_support = true;
}
}
} // namespace rocksdb
#endif // XFUNC

@ -0,0 +1,99 @@
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
#pragma once
#include <string>
namespace rocksdb {
/*
* If ROCKSDB_XFTEST_FORCE has a value of 1, XFUNC is forced to be defined.
* If ROCKSDB_XFTEST_FORCE has a value other than 1,
* XFUNC is forced to be undefined.
* If ROCKSDB_XFTEST_FORCE is undefined, XFUNC is defined based on NDEBUG,
* with XFUNC only being set for debug builds.
*/
#if defined(ROCKSDB_XFTEST_FORCE)
#if (ROCKSDB_XFTEST_FORCE == 1)
#define XFUNC
#endif
#elif NDEBUG
#else
#define XFUNC
#endif
#ifndef XFUNC
#define XFUNC_TEST(condition, location, lfname, fname, ...)
#else
class Options;
void GetXFTestOptions(Options* options, int skip_policy);
// This class provides the facility to run custom code to test a specific
// feature typically with all existing unit tests.
// A developer could specify cross functional test points in the codebase
// via XFUNC_TEST.
// Each xfunc test represents a position in the execution stream of a thread.
// Whenever that particular piece of code is called, the given cross-functional
// test point is executed.
// eg. on DBOpen, a particular option can be set.
// on Get, a particular option can be set, or a specific check can be invoked.
// XFUNC_TEST(TestName, location, lfname, FunctionName, Args)
// Turn on a specific cross functional test by setting the environment variable
// ROCKSDB_XFUNC_TEST
class XFuncPoint {
public:
// call once at the beginning of a test to get the test name
static void Init() {
char* s = getenv("ROCKSDB_XFUNC_TEST");
if (s == nullptr) {
xfunc_test_ = "";
enabled_ = false;
} else {
xfunc_test_ = s;
enabled_ = true;
}
initialized_ = true;
}
static bool Initialized() { return initialized_; }
static bool Check(std::string test) {
return (enabled_ &&
((test.compare("") == 0) || (test.compare(xfunc_test_) == 0)));
}
private:
static std::string xfunc_test_;
static bool initialized_;
static bool enabled_;
};
// Use XFUNC_TEST to specify cross functional test points inside the code base.
// By setting ROCKSDB_XFUNC_TEST, all XFUNC_TEST having that
// value in the condition field will be executed.
// The second argument specifies a string representing the calling location
// The third argument, lfname, is the name of the function which will be created
// and called.
// The fourth argument fname represents the function to be called
// The arguments following that are the arguments to fname
// See Options::Options in options.h for an example use case.
// XFUNC_TEST is no op in release build.
#define XFUNC_TEST(condition, location, lfname, fname, ...) \
{ \
if (!XFuncPoint::Initialized()) { \
XFuncPoint::Init(); \
} \
if (XFuncPoint::Check(condition)) { \
std::function<void()> lfname = std::bind(fname, __VA_ARGS__); \
lfname(); \
} \
}
#endif // XFUNC
enum SkipPolicy { kSkipNone = 0, kSkipNoSnapshot = 1, kSkipNoPrefix = 2 };
} // namespace rocksdb
Loading…
Cancel
Save