2015-02-02 22:49:22 +00:00
|
|
|
// 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.
|
|
|
|
|
2015-04-10 04:05:09 +00:00
|
|
|
#ifdef XFUNC
|
2015-02-02 22:49:22 +00:00
|
|
|
#include <string>
|
2015-02-18 19:49:31 +00:00
|
|
|
#include "db/db_impl.h"
|
|
|
|
#include "db/managed_iterator.h"
|
2015-05-29 21:36:35 +00:00
|
|
|
#include "db/write_callback.h"
|
|
|
|
#include "rocksdb/db.h"
|
2015-02-02 22:49:22 +00:00
|
|
|
#include "rocksdb/options.h"
|
2015-05-29 21:36:35 +00:00
|
|
|
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
|
|
|
#include "rocksdb/write_batch.h"
|
2015-02-02 22:49:22 +00:00
|
|
|
#include "util/xfunc.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
|
|
|
|
std::string XFuncPoint::xfunc_test_;
|
|
|
|
bool XFuncPoint::initialized_ = false;
|
|
|
|
bool XFuncPoint::enabled_ = false;
|
2015-02-18 19:49:31 +00:00
|
|
|
int XFuncPoint::skip_policy_ = 0;
|
2015-02-02 22:49:22 +00:00
|
|
|
|
|
|
|
void GetXFTestOptions(Options* options, int skip_policy) {
|
|
|
|
if (XFuncPoint::Check("inplace_lock_test") &&
|
|
|
|
(!(skip_policy & kSkipNoSnapshot))) {
|
|
|
|
options->inplace_update_support = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-18 19:49:31 +00:00
|
|
|
void xf_manage_release(ManagedIterator* iter) {
|
|
|
|
if (!(XFuncPoint::GetSkip() & kSkipNoPrefix)) {
|
|
|
|
iter->ReleaseIter(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void xf_manage_options(ReadOptions* read_options) {
|
|
|
|
if (!XFuncPoint::Check("managed_xftest_dropold") &&
|
|
|
|
(!XFuncPoint::Check("managed_xftest_release"))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
read_options->managed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void xf_manage_new(DBImpl* db, ReadOptions* read_options,
|
|
|
|
bool is_snapshot_supported) {
|
|
|
|
if ((!XFuncPoint::Check("managed_xftest_dropold") &&
|
|
|
|
(!XFuncPoint::Check("managed_xftest_release"))) ||
|
|
|
|
(!read_options->managed)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((!read_options->tailing) && (read_options->snapshot == nullptr) &&
|
|
|
|
(!is_snapshot_supported)) {
|
|
|
|
read_options->managed = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (db->GetOptions().prefix_extractor != nullptr) {
|
|
|
|
if (strcmp(db->GetOptions().table_factory.get()->Name(), "PlainTable")) {
|
|
|
|
if (!(XFuncPoint::GetSkip() & kSkipNoPrefix)) {
|
|
|
|
read_options->total_order_seek = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
read_options->managed = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void xf_manage_create(ManagedIterator* iter) { iter->SetDropOld(false); }
|
|
|
|
|
Support saving history in memtable_list
Summary:
For transactions, we are using the memtables to validate that there are no write conflicts. But after flushing, we don't have any memtables, and transactions could fail to commit. So we want to someone keep around some extra history to use for conflict checking. In addition, we want to provide a way to increase the size of this history if too many transactions fail to commit.
After chatting with people, it seems like everyone prefers just using Memtables to store this history (instead of a separate history structure). It seems like the best place for this is abstracted inside the memtable_list. I decide to create a separate list in MemtableListVersion as using the same list complicated the flush/installalflushresults logic too much.
This diff adds a new parameter to control how much memtable history to keep around after flushing. However, it sounds like people aren't too fond of adding new parameters. So I am making the default size of flushed+not-flushed memtables be set to max_write_buffers. This should not change the maximum amount of memory used, but make it more likely we're using closer the the limit. (We are now postponing deleting flushed memtables until the max_write_buffer limit is reached). So while we might use more memory on average, we are still obeying the limit set (and you could argue it's better to go ahead and use up memory now instead of waiting for a write stall to happen to test this limit).
However, if people are opposed to this default behavior, we can easily set it to 0 and require this parameter be set in order to use transactions.
Test Plan: Added a xfunc test to play around with setting different values of this parameter in all tests. Added testing in memtablelist_test and planning on adding more testing here.
Reviewers: sdong, rven, igor
Reviewed By: igor
Subscribers: dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D37443
2015-05-28 23:34:24 +00:00
|
|
|
void xf_transaction_set_memtable_history(
|
|
|
|
int32_t* max_write_buffer_number_to_maintain) {
|
|
|
|
*max_write_buffer_number_to_maintain = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
void xf_transaction_clear_memtable_history(
|
|
|
|
int32_t* max_write_buffer_number_to_maintain) {
|
|
|
|
*max_write_buffer_number_to_maintain = 0;
|
|
|
|
}
|
|
|
|
|
2015-05-29 21:36:35 +00:00
|
|
|
class XFTransactionWriteHandler : public WriteBatch::Handler {
|
|
|
|
public:
|
|
|
|
OptimisticTransaction* txn_;
|
|
|
|
DBImpl* db_impl_;
|
|
|
|
|
|
|
|
XFTransactionWriteHandler(OptimisticTransaction* txn, DBImpl* db_impl)
|
|
|
|
: txn_(txn), db_impl_(db_impl) {}
|
|
|
|
|
|
|
|
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
|
|
|
const Slice& value) override {
|
|
|
|
InstrumentedMutexLock l(&db_impl_->mutex_);
|
|
|
|
|
|
|
|
ColumnFamilyHandle* cfh = db_impl_->GetColumnFamilyHandle(column_family_id);
|
|
|
|
if (cfh == nullptr) {
|
|
|
|
return Status::InvalidArgument(
|
|
|
|
"XFUNC test could not find column family "
|
|
|
|
"handle for id ",
|
|
|
|
ToString(column_family_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
txn_->Put(cfh, key, value);
|
|
|
|
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
|
|
|
const Slice& value) override {
|
|
|
|
InstrumentedMutexLock l(&db_impl_->mutex_);
|
|
|
|
|
|
|
|
ColumnFamilyHandle* cfh = db_impl_->GetColumnFamilyHandle(column_family_id);
|
|
|
|
if (cfh == nullptr) {
|
|
|
|
return Status::InvalidArgument(
|
|
|
|
"XFUNC test could not find column family "
|
|
|
|
"handle for id ",
|
|
|
|
ToString(column_family_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
txn_->Merge(cfh, key, value);
|
|
|
|
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual Status DeleteCF(uint32_t column_family_id,
|
|
|
|
const Slice& key) override {
|
|
|
|
InstrumentedMutexLock l(&db_impl_->mutex_);
|
|
|
|
|
|
|
|
ColumnFamilyHandle* cfh = db_impl_->GetColumnFamilyHandle(column_family_id);
|
|
|
|
if (cfh == nullptr) {
|
|
|
|
return Status::InvalidArgument(
|
|
|
|
"XFUNC test could not find column family "
|
|
|
|
"handle for id ",
|
|
|
|
ToString(column_family_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
txn_->Delete(cfh, key);
|
|
|
|
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void LogData(const Slice& blob) override { txn_->PutLogData(blob); }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Whenever DBImpl::Write is called, create a transaction and do the write via
|
|
|
|
// the transaction.
|
|
|
|
void xf_transaction_write(const WriteOptions& write_options,
|
|
|
|
const DBOptions& db_options, WriteBatch* my_batch,
|
|
|
|
WriteCallback* callback, DBImpl* db_impl, Status* s,
|
|
|
|
bool* write_attempted) {
|
|
|
|
if (callback != nullptr) {
|
|
|
|
// We may already be in a transaction, don't force a transaction
|
|
|
|
*write_attempted = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
OptimisticTransactionDB* txn_db = new OptimisticTransactionDB(db_impl);
|
|
|
|
OptimisticTransaction* txn =
|
|
|
|
OptimisticTransaction::BeginTransaction(txn_db, write_options);
|
|
|
|
|
|
|
|
XFTransactionWriteHandler handler(txn, db_impl);
|
|
|
|
*s = my_batch->Iterate(&handler);
|
|
|
|
|
|
|
|
if (!s->ok()) {
|
|
|
|
Log(InfoLogLevel::ERROR_LEVEL, db_options.info_log,
|
|
|
|
"XFUNC test could not iterate batch. status: $s\n",
|
|
|
|
s->ToString().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
*s = txn->Commit();
|
|
|
|
|
|
|
|
if (!s->ok()) {
|
|
|
|
Log(InfoLogLevel::ERROR_LEVEL, db_options.info_log,
|
|
|
|
"XFUNC test could not commit transaction. status: $s\n",
|
|
|
|
s->ToString().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
*write_attempted = true;
|
|
|
|
delete txn;
|
|
|
|
delete txn_db;
|
|
|
|
}
|
|
|
|
|
2015-02-02 22:49:22 +00:00
|
|
|
} // namespace rocksdb
|
|
|
|
|
|
|
|
#endif // XFUNC
|