commit
f5469b1a61
@ -0,0 +1,71 @@ |
|||||||
|
TMP_DIR="/tmp/rocksdb-sanity-test" |
||||||
|
|
||||||
|
if [ "$#" -lt 2 ]; then |
||||||
|
echo "usage: ./auto_sanity_test.sh [new_commit] [old_commit]" |
||||||
|
echo "Missing either [new_commit] or [old_commit], perform sanity check with the latest and 10th latest commits." |
||||||
|
recent_commits=`git log | grep -e "^commit [a-z0-9]\+$"| head -n10 | sed -e 's/commit //g'` |
||||||
|
commit_new=`echo "$recent_commits" | head -n1` |
||||||
|
commit_old=`echo "$recent_commits" | tail -n1` |
||||||
|
echo "the most recent commits are:" |
||||||
|
echo "$recent_commits" |
||||||
|
else |
||||||
|
commit_new=$1 |
||||||
|
commit_old=$2 |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -d $TMP_DIR ]; then |
||||||
|
mkdir $TMP_DIR |
||||||
|
fi |
||||||
|
dir_new="${TMP_DIR}/${commit_new}" |
||||||
|
dir_old="${TMP_DIR}/${commit_old}" |
||||||
|
|
||||||
|
function makestuff() { |
||||||
|
echo "make clean" |
||||||
|
make clean > /dev/null |
||||||
|
echo "make db_sanity_test -j32" |
||||||
|
make db_sanity_test -j32 > /dev/null |
||||||
|
if [ $? -ne 0 ]; then |
||||||
|
echo "[ERROR] Failed to perform 'make db_sanity_test'" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
rm -r -f $dir_new |
||||||
|
rm -r -f $dir_old |
||||||
|
|
||||||
|
echo "Running db sanity check with commits $commit_new and $commit_old." |
||||||
|
|
||||||
|
echo "=============================================================" |
||||||
|
echo "Making build $commit_new" |
||||||
|
makestuff |
||||||
|
mv db_sanity_test new_db_sanity_test |
||||||
|
echo "Creating db based on the new commit --- $commit_new" |
||||||
|
./new_db_sanity_test $dir_new create |
||||||
|
|
||||||
|
echo "=============================================================" |
||||||
|
echo "Making build $commit_old" |
||||||
|
makestuff |
||||||
|
mv db_sanity_test old_db_sanity_test |
||||||
|
echo "Creating db based on the old commit --- $commit_old" |
||||||
|
./old_db_sanity_test $dir_old create |
||||||
|
|
||||||
|
echo "=============================================================" |
||||||
|
echo "Verifying new db $dir_new using the old commit --- $commit_old" |
||||||
|
./old_db_sanity_test $dir_new verify |
||||||
|
if [ $? -ne 0 ]; then |
||||||
|
echo "[ERROR] Verification of $dir_new using commit $commit_old failed." |
||||||
|
exit 2 |
||||||
|
fi |
||||||
|
|
||||||
|
echo "=============================================================" |
||||||
|
echo "Verifying old db $dir_old using the new commit --- $commit_new" |
||||||
|
./new_db_sanity_test $dir_old verify |
||||||
|
if [ $? -ne 0 ]; then |
||||||
|
echo "[ERROR] Verification of $dir_old using commit $commit_new failed." |
||||||
|
exit 2 |
||||||
|
fi |
||||||
|
|
||||||
|
rm old_db_sanity_test |
||||||
|
rm new_db_sanity_test |
||||||
|
|
||||||
|
echo "Auto sanity test passed!" |
@ -0,0 +1,62 @@ |
|||||||
|
// 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 "util/sync_point.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
SyncPoint* SyncPoint::GetInstance() { |
||||||
|
static SyncPoint sync_point; |
||||||
|
return &sync_point; |
||||||
|
} |
||||||
|
|
||||||
|
void SyncPoint::LoadDependency(const std::vector<Dependency>& dependencies) { |
||||||
|
successors_.clear(); |
||||||
|
predecessors_.clear(); |
||||||
|
cleared_points_.clear(); |
||||||
|
for (const auto& dependency : dependencies) { |
||||||
|
successors_[dependency.predecessor].push_back(dependency.successor); |
||||||
|
predecessors_[dependency.successor].push_back(dependency.predecessor); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool SyncPoint::PredecessorsAllCleared(const std::string& point) { |
||||||
|
for (const auto& pred : predecessors_[point]) { |
||||||
|
if (cleared_points_.count(pred) == 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void SyncPoint::EnableProcessing() { |
||||||
|
std::unique_lock<std::mutex> lock(mutex_); |
||||||
|
enabled_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void SyncPoint::DisableProcessing() { |
||||||
|
std::unique_lock<std::mutex> lock(mutex_); |
||||||
|
enabled_ = false; |
||||||
|
} |
||||||
|
|
||||||
|
void SyncPoint::ClearTrace() { |
||||||
|
std::unique_lock<std::mutex> lock(mutex_); |
||||||
|
cleared_points_.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
void SyncPoint::Process(const std::string& point) { |
||||||
|
std::unique_lock<std::mutex> lock(mutex_); |
||||||
|
|
||||||
|
if (!enabled_) return; |
||||||
|
|
||||||
|
while (!PredecessorsAllCleared(point)) { |
||||||
|
cv_.wait(lock); |
||||||
|
} |
||||||
|
|
||||||
|
cleared_points_.insert(point); |
||||||
|
cv_.notify_all(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,79 @@ |
|||||||
|
// 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 <condition_variable> |
||||||
|
#include <mutex> |
||||||
|
#include <string> |
||||||
|
#include <unordered_set> |
||||||
|
#include <unordered_map> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
// This class provides facility to reproduce race conditions deterministically
|
||||||
|
// in unit tests.
|
||||||
|
// Developer could specify sync points in the codebase via TEST_SYNC_POINT.
|
||||||
|
// Each sync point represents a position in the execution stream of a thread.
|
||||||
|
// In the unit test, 'Happens After' relationship among sync points could be
|
||||||
|
// setup via SyncPoint::LoadDependency, to reproduce a desired interleave of
|
||||||
|
// threads execution.
|
||||||
|
// Refer to (DBTest,TransactionLogIteratorRace), for an exmaple use case.
|
||||||
|
|
||||||
|
class SyncPoint { |
||||||
|
public: |
||||||
|
static SyncPoint* GetInstance(); |
||||||
|
|
||||||
|
struct Dependency { |
||||||
|
std::string predecessor; |
||||||
|
std::string successor; |
||||||
|
}; |
||||||
|
// call once at the beginning of a test to setup the dependency between
|
||||||
|
// sync points
|
||||||
|
void LoadDependency(const std::vector<Dependency>& dependencies); |
||||||
|
|
||||||
|
// enable sync point processing (disabled on startup)
|
||||||
|
void EnableProcessing(); |
||||||
|
|
||||||
|
// disable sync point processing
|
||||||
|
void DisableProcessing(); |
||||||
|
|
||||||
|
// remove the execution trace of all sync points
|
||||||
|
void ClearTrace(); |
||||||
|
|
||||||
|
// triggered by TEST_SYNC_POINT, blocking execution until all predecessors
|
||||||
|
// are executed.
|
||||||
|
void Process(const std::string& point); |
||||||
|
|
||||||
|
// TODO: it might be useful to provide a function that blocks until all
|
||||||
|
// sync points are cleared.
|
||||||
|
|
||||||
|
private: |
||||||
|
bool PredecessorsAllCleared(const std::string& point); |
||||||
|
|
||||||
|
// successor/predecessor map loaded from LoadDependency
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> successors_; |
||||||
|
std::unordered_map<std::string, std::vector<std::string>> predecessors_; |
||||||
|
|
||||||
|
std::mutex mutex_; |
||||||
|
std::condition_variable cv_; |
||||||
|
// sync points that have been passed through
|
||||||
|
std::unordered_set<std::string> cleared_points_; |
||||||
|
bool enabled_ = false; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
// Use TEST_SYNC_POINT to specify sync points inside code base.
|
||||||
|
// Sync points can have happens-after depedency on other sync points,
|
||||||
|
// configured at runtime via SyncPoint::LoadDependency. This could be
|
||||||
|
// utilized to re-produce race conditions between threads.
|
||||||
|
// See TransactionLogIteratorRace in db_test.cc for an example use case.
|
||||||
|
// TEST_SYNC_POINT is no op in release build.
|
||||||
|
#ifdef NDEBUG |
||||||
|
#define TEST_SYNC_POINT(x) |
||||||
|
#else |
||||||
|
#define TEST_SYNC_POINT(x) rocksdb::SyncPoint::GetInstance()->Process(x) |
||||||
|
#endif |
Loading…
Reference in new issue