ClickHouse/src/Interpreters/TransactionLog.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

213 lines
9.1 KiB
C++
Raw Normal View History

2021-03-31 17:55:04 +00:00
#pragma once
#include <Interpreters/MergeTreeTransaction.h>
2021-05-17 11:14:09 +00:00
#include <Interpreters/MergeTreeTransactionHolder.h>
2021-12-28 11:23:35 +00:00
#include <Common/ZooKeeper/ZooKeeper.h>
#include <Common/ThreadPool.h>
2021-03-31 17:55:04 +00:00
#include <boost/noncopyable.hpp>
#include <mutex>
2021-04-08 17:20:45 +00:00
#include <unordered_map>
2021-03-31 17:55:04 +00:00
namespace DB
{
2022-01-19 18:29:31 +00:00
/// We want to create a TransactionLog object lazily and avoid creation if it's not needed.
/// But we also want to call shutdown() in a specific place to avoid race conditions.
/// We cannot simply use return-static-variable pattern,
/// because a call to shutdown() may construct unnecessary object in this case.
template <typename Derived>
class SingletonHelper : private boost::noncopyable
{
public:
static Derived & instance()
{
Derived * ptr = instance_raw_ptr.load();
if (likely(ptr))
return *ptr;
2022-03-30 15:42:47 +00:00
return createInstanceOrThrow();
2022-01-19 18:29:31 +00:00
}
static void shutdownIfAny()
{
std::lock_guard lock{instance_mutex};
2022-03-30 15:42:47 +00:00
if (instance_holder)
2022-01-19 18:29:31 +00:00
instance_holder->shutdown();
}
private:
2022-03-30 15:42:47 +00:00
static Derived & createInstanceOrThrow();
2022-01-19 18:29:31 +00:00
static inline std::atomic<Derived *> instance_raw_ptr;
2022-03-30 15:42:47 +00:00
/// It was supposed to be std::optional, but gcc fails to compile it for some reason
static inline std::shared_ptr<Derived> instance_holder;
2022-01-19 18:29:31 +00:00
static inline std::mutex instance_mutex;
};
2022-01-14 14:03:00 +00:00
class TransactionsInfoLog;
using TransactionsInfoLogPtr = std::shared_ptr<TransactionsInfoLog>;
2021-12-28 11:23:35 +00:00
using ZooKeeperPtr = std::shared_ptr<zkutil::ZooKeeper>;
2022-03-14 20:43:34 +00:00
/// This class maintains transaction log in ZooKeeper and a list of currently running transactions in memory.
///
/// Each transaction has unique ID (TID, see details below).
/// TransactionID is allocated when transaction begins.
///
/// We use TransactionID to associate changes (created/removed data parts) with transaction that has made/is going to make these changes.
/// To commit a transaction we create sequential node "/path_to_log/log/csn-" in ZK and write TID into this node.
/// Allocated sequential number is a commit timestamp or Commit Sequence Number (CSN). It indicates a (logical) point in time
/// when transaction is committed and all its changes became visible. So we have total order of all changes.
///
/// Also CSNs are used as snapshots: all changes that were made by a transaction that was committed with a CSN less or equal than some_csn
/// are visible in some_csn snapshot.
///
/// TransactionID consists of three parts: (start_csn, local_tid, host_id)
/// - start_csn is the newest CSN that existed when the transaction was started and also it's snapshot that is visible for this transaction
/// - local_tid is local sequential number of the transaction, each server allocates local_tids independently without requests to ZK
/// - host_id is persistent UUID of host that has started the transaction, it's kind of tie-breaker that makes ID unique across all servers
///
/// To check if some transaction is committed or not we fetch "csn-xxxxxx" nodes from ZK and construct TID -> CSN mapping,
/// so for committed transactions we know commit timestamps.
/// However, if we did not find a mapping for some TID, it means one of the following cases:
/// 1. Transaction is not committed (yet)
/// 2. Transaction is rolled back (quite similar to the first case, but it will never be committed)
/// 3. Transactions was committed a long time ago and we removed its entry from the log
/// To distinguish the third case we store a "tail pointer" in "/path_to_log/tail_ptr". It's a CSN such that it's safe to remove from log
/// entries with tid.start_csn < tail_ptr, because CSNs for those TIDs are already written into data parts
/// and we will not do a CSN lookup for those TIDs anymore.
///
/// (however, transactions involving multiple hosts and/or ReplicatedMergeTree tables are currently not supported)
2022-01-19 18:29:31 +00:00
class TransactionLog final : public SingletonHelper<TransactionLog>
2021-03-31 17:55:04 +00:00
{
public:
TransactionLog();
2021-12-28 11:23:35 +00:00
~TransactionLog();
2022-01-19 18:29:31 +00:00
void shutdown();
2022-03-14 20:43:34 +00:00
/// Returns the newest snapshot available for reading
CSN getLatestSnapshot() const;
/// Returns the oldest snapshot that is visible for some running transaction
CSN getOldestSnapshot() const;
2021-03-31 17:55:04 +00:00
2022-03-14 20:43:34 +00:00
/// Allocates TID, returns new transaction object
2021-03-31 17:55:04 +00:00
MergeTreeTransactionPtr beginTransaction();
2022-03-14 20:43:34 +00:00
/// Tries to commit transaction. Returns Commit Sequence Number.
/// Throw if transaction was concurrently killed or if some precommit check failed.
/// May throw if ZK connection is lost. Transaction status is unknown in this case.
2022-05-20 20:08:46 +00:00
/// Returns CommittingCSN if throw_on_unknown_status is false and connection was lost.
CSN commitTransaction(const MergeTreeTransactionPtr & txn, bool throw_on_unknown_status);
2021-03-31 17:55:04 +00:00
2022-03-14 20:43:34 +00:00
/// Releases locks that that were acquired by transaction, releases snapshot, removes transaction from the list of active transactions.
/// Normally it should not throw, but if it does for some reason (global memory limit exceeded, disk failure, etc)
/// then we should terminate server and reinitialize it to avoid corruption of data structures. That's why it's noexcept.
2021-04-09 12:53:51 +00:00
void rollbackTransaction(const MergeTreeTransactionPtr & txn) noexcept;
2021-03-31 17:55:04 +00:00
2022-03-14 20:43:34 +00:00
/// Returns CSN if transaction with specified ID was committed and UnknownCSN if it was not.
/// Returns PrehistoricCSN for PrehistoricTID without creating a TransactionLog instance as a special case.
static CSN getCSN(const TransactionID & tid);
static CSN getCSN(const TIDHash & tid);
2021-04-08 17:20:45 +00:00
2022-03-14 20:43:34 +00:00
/// Ensures that getCSN returned UnknownCSN because transaction is not committed and not because entry was removed from the log.
static void assertTIDIsNotOutdated(const TransactionID & tid);
/// Returns a pointer to transaction object if it's running or nullptr.
2021-04-08 17:20:45 +00:00
MergeTreeTransactionPtr tryGetRunningTransaction(const TIDHash & tid);
2022-03-10 21:29:58 +00:00
using TransactionsList = std::unordered_map<TIDHash, MergeTreeTransactionPtr>;
2022-03-14 20:43:34 +00:00
/// Returns copy of list of running transactions.
2022-03-10 21:29:58 +00:00
TransactionsList getTransactionsList() const;
2022-05-20 20:08:46 +00:00
/// Waits for provided CSN (and all previous ones) to be loaded from the log.
/// Returns false if waiting was interrupted (e.g. by shutdown)
bool waitForCSNLoaded(CSN csn) const;
bool isShuttingDown() const { return stop_flag.load(); }
2022-05-25 20:20:13 +00:00
void sync() const;
2021-03-31 17:55:04 +00:00
private:
2022-06-27 20:48:27 +00:00
void loadLogFromZooKeeper() TSA_REQUIRES(mutex);
2021-12-28 11:23:35 +00:00
void runUpdatingThread();
void loadEntries(Strings::const_iterator beg, Strings::const_iterator end);
void loadNewEntries();
2022-03-14 20:43:34 +00:00
void removeOldEntries();
2021-12-28 11:23:35 +00:00
2022-05-21 00:32:35 +00:00
CSN finalizeCommittedTransaction(MergeTreeTransaction * txn, CSN allocated_csn, scope_guard & state_guard) noexcept;
2022-05-20 15:35:29 +00:00
void tryFinalizeUnknownStateTransactions();
2022-03-16 19:16:26 +00:00
static UInt64 deserializeCSN(const String & csn_node_name);
static String serializeCSN(CSN csn);
static TransactionID deserializeTID(const String & csn_node_content);
static String serializeTID(const TransactionID & tid);
2021-12-28 11:23:35 +00:00
2022-01-20 18:15:23 +00:00
ZooKeeperPtr getZooKeeper() const;
2022-03-14 20:43:34 +00:00
CSN getCSNImpl(const TIDHash & tid_hash) const;
2022-06-27 20:48:27 +00:00
const ContextPtr global_context;
Poco::Logger * const log;
2021-05-18 17:07:29 +00:00
2022-03-14 20:43:34 +00:00
/// The newest snapshot available for reading
2021-04-08 17:20:45 +00:00
std::atomic<CSN> latest_snapshot;
2022-03-14 20:43:34 +00:00
/// Local part of TransactionID number. We reset this counter for each new snapshot.
2021-03-31 17:55:04 +00:00
std::atomic<LocalTID> local_tid_counter;
2021-04-08 17:20:45 +00:00
2022-01-20 18:15:23 +00:00
mutable std::mutex mutex;
2022-03-14 20:43:34 +00:00
/// Mapping from TransactionID to CSN for recently committed transactions.
/// Allows to check if some transactions is committed.
struct CSNEntry
{
CSN csn;
TransactionID tid;
};
using TIDMap = std::unordered_map<TIDHash, CSNEntry>;
2022-06-27 20:48:27 +00:00
TIDMap tid_to_csn TSA_GUARDED_BY(mutex);
2021-04-08 17:20:45 +00:00
mutable std::mutex running_list_mutex;
2022-03-14 20:43:34 +00:00
/// Transactions that are currently processed
2022-06-27 20:48:27 +00:00
TransactionsList running_list TSA_GUARDED_BY(running_list_mutex);
2022-05-20 15:35:29 +00:00
/// If we lost connection on attempt to create csn- node then we don't know transaction's state.
using UnknownStateList = std::vector<std::pair<MergeTreeTransaction *, scope_guard>>;
2022-06-27 20:48:27 +00:00
UnknownStateList unknown_state_list TSA_GUARDED_BY(running_list_mutex);
UnknownStateList unknown_state_list_loaded TSA_GUARDED_BY(running_list_mutex);
2022-03-14 20:43:34 +00:00
/// Ordered list of snapshots that are currently used by some transactions. Needed for background cleanup.
2022-06-27 20:48:27 +00:00
std::list<CSN> snapshots_in_use TSA_GUARDED_BY(running_list_mutex);
2021-12-28 11:23:35 +00:00
2022-06-27 20:48:27 +00:00
ZooKeeperPtr zookeeper TSA_GUARDED_BY(mutex);
const String zookeeper_path;
2022-03-14 20:43:34 +00:00
2022-06-27 20:48:27 +00:00
const String zookeeper_path_log;
2022-03-14 20:43:34 +00:00
/// Name of the newest entry that was loaded from log in ZK
2022-06-27 20:48:27 +00:00
String last_loaded_entry TSA_GUARDED_BY(mutex);
2022-03-14 20:43:34 +00:00
/// The oldest CSN such that we store in log entries with TransactionIDs containing this CSN.
std::atomic<CSN> tail_ptr = Tx::UnknownCSN;
2021-12-28 11:23:35 +00:00
zkutil::EventPtr log_updated_event = std::make_shared<Poco::Event>();
std::atomic_bool stop_flag = false;
ThreadFromGlobalPool updating_thread;
2022-05-20 20:08:46 +00:00
2022-06-27 20:48:27 +00:00
const Float64 fault_probability_before_commit = 0;
const Float64 fault_probability_after_commit = 0;
2021-03-31 17:55:04 +00:00
};
2022-03-30 15:42:47 +00:00
template <typename Derived>
Derived & SingletonHelper<Derived>::createInstanceOrThrow()
{
std::lock_guard lock{instance_mutex};
if (!instance_holder)
{
instance_holder = std::make_shared<Derived>();
instance_raw_ptr = instance_holder.get();
}
return *instance_holder;
}
2021-03-31 17:55:04 +00:00
}