2015-11-22 02:16:19 +08:00
|
|
|
#pragma once
|
2015-07-04 17:51:33 +08:00
|
|
|
|
|
|
|
#include <atomic>
|
2017-04-28 00:55:15 +08:00
|
|
|
#include <limits>
|
2015-07-04 17:51:33 +08:00
|
|
|
#include <vector>
|
|
|
|
|
2016-08-10 16:39:02 +08:00
|
|
|
#include "threading/sync/lockable.hpp"
|
|
|
|
#include "threading/sync/spinlock.hpp"
|
|
|
|
#include "transactions/commit_log.hpp"
|
2016-07-05 11:01:22 +08:00
|
|
|
#include "transactions/transaction.hpp"
|
|
|
|
#include "transactions/transaction_store.hpp"
|
2017-04-28 16:33:46 +08:00
|
|
|
#include "utils/exceptions.hpp"
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-02-18 18:54:37 +08:00
|
|
|
namespace tx {
|
2015-10-04 15:47:15 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
/** Indicates an error in transaction handling (currently
|
|
|
|
* only command id overflow). */
|
2017-04-28 16:33:46 +08:00
|
|
|
class TransactionError : public utils::BasicException {
|
2017-02-18 18:54:37 +08:00
|
|
|
public:
|
2017-04-28 16:33:46 +08:00
|
|
|
using utils::BasicException::BasicException;
|
2015-10-04 15:47:15 +08:00
|
|
|
};
|
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
/** Database transaction egine.
|
|
|
|
*
|
|
|
|
* Used for managing transactions and the related information
|
|
|
|
* such as transaction snapshots and the commit log.
|
|
|
|
*/
|
2017-02-18 18:54:37 +08:00
|
|
|
class Engine : Lockable<SpinLock> {
|
2017-06-12 16:21:19 +08:00
|
|
|
// limit for the command id, used for checking if we're about
|
|
|
|
// to overflow. slightly unneccessary since command id should
|
|
|
|
// be a 64-bit int
|
|
|
|
static constexpr auto kMaxCommandId =
|
|
|
|
std::numeric_limits<decltype(std::declval<Transaction>().cid())>::max();
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2017-07-06 19:53:39 +08:00
|
|
|
template <class T>
|
|
|
|
class SimpleCounter {
|
|
|
|
public:
|
|
|
|
SimpleCounter(T initial) : counter(initial) {}
|
|
|
|
|
|
|
|
T next() { return ++counter; }
|
|
|
|
|
2017-09-27 22:53:11 +08:00
|
|
|
T count() const { return counter; }
|
2017-07-06 19:53:39 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
T counter;
|
|
|
|
};
|
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
public:
|
2017-06-12 16:21:19 +08:00
|
|
|
/** Begins a transaction and returns a pointer to
|
|
|
|
* it's object.
|
|
|
|
*
|
|
|
|
* The transaction object is owned by this engine.
|
|
|
|
* It will be released when the transaction gets
|
|
|
|
* committted or aborted.
|
|
|
|
*/
|
|
|
|
Transaction *Begin() {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
transaction_id_t id{counter_.next()};
|
|
|
|
auto t = new Transaction(id, active_, *this);
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
active_.insert(id);
|
|
|
|
store_.put(id, t);
|
2016-08-25 22:29:45 +08:00
|
|
|
|
2017-03-06 21:17:55 +08:00
|
|
|
return t;
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
/** Advances the command on the transaction with the
|
|
|
|
* given id.
|
|
|
|
*
|
|
|
|
* @param id - Transation id. That transaction must
|
|
|
|
* be currently active.
|
|
|
|
* @return Pointer to the transaction object for id.
|
|
|
|
*/
|
|
|
|
Transaction &Advance(transaction_id_t id) {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2015-10-04 15:47:15 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
auto *t = store_.get(id);
|
|
|
|
debug_assert(t != nullptr,
|
|
|
|
"Transaction::advance on non-existing transaction");
|
2015-10-04 15:47:15 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
if (t->cid_ == kMaxCommandId)
|
2017-03-06 21:17:55 +08:00
|
|
|
throw TransactionError(
|
|
|
|
"Reached maximum number of commands in this transaction.");
|
2017-04-28 00:55:15 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
t->cid_++;
|
2017-02-18 18:54:37 +08:00
|
|
|
return *t;
|
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
/** Returns the snapshot relevant to garbage collection of database records.
|
2017-06-12 16:21:19 +08:00
|
|
|
*
|
2017-07-14 19:58:25 +08:00
|
|
|
* If there are no active transactions that means a snapshot containing only
|
|
|
|
* the next transaction ID. If there are active transactions, that means the
|
|
|
|
* oldest active transaction's snapshot, with that transaction's ID appened as
|
|
|
|
* last.
|
2017-06-12 16:21:19 +08:00
|
|
|
*
|
2017-07-14 19:58:25 +08:00
|
|
|
* The idea is that data records can only be deleted if they were expired (and
|
|
|
|
* that was committed) by a transaction older then the older currently active.
|
|
|
|
* We need the full snapshot to prevent overlaps (see general GC
|
|
|
|
* documentation).
|
2017-06-12 16:21:19 +08:00
|
|
|
*/
|
|
|
|
Snapshot GcSnapshot() {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2016-08-25 22:29:45 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// No active transactions.
|
2017-06-12 16:21:19 +08:00
|
|
|
if (active_.size() == 0) {
|
|
|
|
auto snapshot_copy = active_;
|
|
|
|
snapshot_copy.insert(counter_.count() + 1);
|
|
|
|
return snapshot_copy;
|
|
|
|
}
|
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// There are active transactions.
|
2017-06-12 16:21:19 +08:00
|
|
|
auto snapshot_copy = store_.get(active_.front())->snapshot();
|
|
|
|
snapshot_copy.insert(active_.front());
|
|
|
|
return snapshot_copy;
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2016-08-25 22:29:45 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
/** Comits the given transaction. Deletes the transaction object, it's not
|
|
|
|
* valid after this function executes. */
|
2017-06-12 16:21:19 +08:00
|
|
|
void Commit(const Transaction &t) {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2017-06-12 16:21:19 +08:00
|
|
|
clog_.set_committed(t.id_);
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
Finalize(t);
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
/** Aborts the given transaction. Deletes the transaction object, it's not
|
|
|
|
* valid after this function executes. */
|
2017-06-12 16:21:19 +08:00
|
|
|
void Abort(const Transaction &t) {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2017-06-12 16:21:19 +08:00
|
|
|
clog_.set_aborted(t.id_);
|
2015-11-23 04:35:40 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
Finalize(t);
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
/** The total number of transactions that have executed since the creation of
|
|
|
|
* this engine */
|
2017-09-27 22:53:11 +08:00
|
|
|
auto Count() const {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2017-06-12 16:21:19 +08:00
|
|
|
return counter_.count();
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-06-12 16:21:19 +08:00
|
|
|
/** The count of currently active transactions */
|
2017-09-27 22:53:11 +08:00
|
|
|
int64_t ActiveCount() const {
|
2017-02-18 18:54:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2017-06-12 16:21:19 +08:00
|
|
|
return active_.size();
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2015-07-04 17:51:33 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
/** Calls function f on each active transaction. */
|
|
|
|
void ForEachActiveTransaction(std::function<void(Transaction &)> f) {
|
2017-07-17 20:15:37 +08:00
|
|
|
auto guard = this->acquire_unique();
|
2017-07-14 19:58:25 +08:00
|
|
|
for (auto transaction : active_) {
|
|
|
|
f(*store_.get(transaction));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-27 20:02:24 +08:00
|
|
|
const auto &clog() const { return clog_; }
|
|
|
|
|
|
|
|
auto &lock_graph() { return lock_graph_; }
|
|
|
|
const auto &lock_graph() const { return lock_graph_; }
|
2016-03-12 19:26:56 +08:00
|
|
|
|
2017-02-18 18:54:37 +08:00
|
|
|
private:
|
2017-07-14 19:58:25 +08:00
|
|
|
// Commit log of this engine.
|
2017-06-13 15:45:32 +08:00
|
|
|
CommitLog clog_;
|
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// Performs cleanup common to ending the transaction with either commit or
|
|
|
|
// abort.
|
2017-06-12 16:21:19 +08:00
|
|
|
void Finalize(const Transaction &t) {
|
|
|
|
active_.remove(t.id_);
|
|
|
|
store_.del(t.id_);
|
2017-02-18 18:54:37 +08:00
|
|
|
}
|
2016-08-25 22:29:45 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// Transaction counter. contains the number of transactions ever created till
|
|
|
|
// now.
|
2017-06-12 16:21:19 +08:00
|
|
|
SimpleCounter<transaction_id_t> counter_{0};
|
2015-10-04 15:47:15 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// A snapshot of currently active transactions.
|
2017-06-12 16:21:19 +08:00
|
|
|
Snapshot active_;
|
2017-02-18 18:54:37 +08:00
|
|
|
|
2017-07-14 19:58:25 +08:00
|
|
|
// Storage for the transactions.
|
2017-06-12 16:21:19 +08:00
|
|
|
TransactionStore<transaction_id_t> store_;
|
2017-09-27 20:02:24 +08:00
|
|
|
|
|
|
|
// For each active transaction we store a transaction that holds a lock that
|
|
|
|
// mentioned transaction is also trying to acquire. We can think of this
|
|
|
|
// data structure as a graph: key being a start node of directed edges and
|
|
|
|
// value being an end node of that edge. ConcurrentMap is used since it is
|
|
|
|
// garbage collected and we are sure that we will not be having problems with
|
|
|
|
// lifetimes of each object.
|
|
|
|
ConcurrentMap<transaction_id_t, transaction_id_t> lock_graph_;
|
2015-07-04 17:51:33 +08:00
|
|
|
};
|
2015-10-04 15:47:15 +08:00
|
|
|
}
|