memgraph/src/transactions/engine.hpp
florijan 9721ccf61c Cleanup per-transaction caches in distributed
Summary:
On the master cleanups are hooked directly into the transaction engine.
This is beneficial because the master might have bigger caches and we
want to clear them as soon as possible.

On the workers there is a periodic RPC call to the master about living
transactions, which takes care of releasing local caches. This is
suboptimal because long transactions will prevent cache GC (like with
data GC). It is however fairly simple.

Note that all cleanup is not done automatically and `RemotePull` has
been reduced accordingly. @msantl, please verify correctness and
consider if the code can be additionally simplified.

Reviewers: teon.banek, msantl

Reviewed By: msantl

Subscribers: pullbot, msantl

Differential Revision: https://phabricator.memgraph.io/D1202
2018-02-16 15:30:05 +01:00

111 lines
4.2 KiB
C++

#pragma once
#include <algorithm>
#include <mutex>
#include <vector>
#include "data_structures/concurrent/concurrent_map.hpp"
#include "threading/sync/spinlock.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/transaction.hpp"
#include "transactions/type.hpp"
namespace tx {
class TxEndListener;
/**
* Database transaction engine. Used for managing transactions and the related
* information such as transaction snapshots and the transaction state info.
*
* This is an abstract base class for implementing a single-node transactional
* engine (MasterEngine), an engine for the master in a distributed system (also
* MasterEngine), and for the worker in a distributed system (WorkerEngine).
*
* Methods in this class are often prefixed with "Global" or "Local", depending
* on the guarantees that they need to satisfy. These guarantee requirements are
* determined by the users of a particular method.
*/
class Engine {
friend class TxEndListener;
public:
virtual ~Engine() = default;
/// Begins a transaction and returns a pointer to it's object.
virtual Transaction *Begin() = 0;
/// Advances the command on the transaction with the given id.
virtual command_id_t Advance(transaction_id_t id) = 0;
/// Updates the command on the workers to the master's value.
virtual command_id_t UpdateCommand(transaction_id_t id) = 0;
/// Comits the given transaction. Deletes the transaction object, it's not
/// valid after this function executes.
virtual void Commit(const Transaction &t) = 0;
/// Aborts the given transaction. Deletes the transaction object, it's not
/// valid after this function executes.
virtual void Abort(const Transaction &t) = 0;
/** Returns the commit log Info about the given transaction. */
virtual CommitLog::Info Info(transaction_id_t tx) const = 0;
/** Returns the snapshot relevant to garbage collection of database records.
*
* 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.
*
* 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).
*
* The returned snapshot must be for the globally oldest active transaction.
* If we only looked at locally known transactions, it would be possible to
* delete something that and older active transaction can still see.
*/
virtual Snapshot GlobalGcSnapshot() = 0;
/** Returns active transactions. */
virtual Snapshot GlobalActiveTransactions() = 0;
/** Returns true if the transaction with the given ID is currently active. */
virtual bool GlobalIsActive(transaction_id_t tx) const = 0;
/** Returns the ID of last locally known transaction. */
virtual tx::transaction_id_t LocalLast() const = 0;
/** Calls function f on each locally active transaction. */
virtual void LocalForEachActiveTransaction(
std::function<void(Transaction &)> f) = 0;
/** Gets a transaction object for a running transaction. */
virtual tx::Transaction *RunningTransaction(transaction_id_t tx_id) = 0;
auto &local_lock_graph() { return local_lock_graph_; }
const auto &local_lock_graph() const { return local_lock_graph_; }
private:
// Map lock dependencies. Each entry maps (tx_that_wants_lock,
// tx_that_holds_lock). Used for local deadlock resolution.
// TODO consider global deadlock resolution.
ConcurrentMap<transaction_id_t, transaction_id_t> local_lock_graph_;
// Transaction end listeners and the lock for protecting that datastructure.
std::vector<TxEndListener *> end_listeners_;
mutable SpinLock end_listeners_lock_;
/** Register a transaction end listener with this engine. */
void Register(TxEndListener *listener);
/** Unregister a transaction end listener with this engine. */
void Unregister(TxEndListener *listener);
protected:
/** Notifies all registered listeners that a transaction has ended. */
void NotifyListeners(transaction_id_t tx_id) const;
};
} // namespace tx