ClickHouse/src/Interpreters/TransactionVersionMetadata.cpp

430 lines
15 KiB
C++
Raw Normal View History

2021-11-08 18:56:09 +00:00
#include <Interpreters/TransactionVersionMetadata.h>
2022-01-14 14:03:00 +00:00
#include <Interpreters/TransactionsInfoLog.h>
2021-03-31 17:55:04 +00:00
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeTuple.h>
#include <DataTypes/DataTypeUUID.h>
2021-04-08 17:20:45 +00:00
#include <Interpreters/TransactionLog.h>
2022-01-19 20:16:05 +00:00
#include <Interpreters/Context.h>
2021-12-30 13:15:28 +00:00
#include <IO/ReadHelpers.h>
#include <IO/WriteHelpers.h>
#include <IO/WriteBufferFromString.h>
2022-04-27 15:05:45 +00:00
#include <Common/logger_useful.h>
2021-11-08 18:56:09 +00:00
2021-03-31 17:55:04 +00:00
namespace DB
{
2021-04-08 17:20:45 +00:00
namespace ErrorCodes
{
2021-11-08 18:56:09 +00:00
extern const int SERIALIZATION_ERROR;
extern const int LOGICAL_ERROR;
2021-12-30 13:15:28 +00:00
extern const int CANNOT_PARSE_TEXT;
2021-04-08 17:20:45 +00:00
}
2022-03-14 20:43:34 +00:00
inline static CSN getCSNAndAssert(TIDHash tid_hash, std::atomic<CSN> & csn, const TransactionID * tid = nullptr)
{
CSN maybe_csn = TransactionLog::getCSN(tid_hash);
if (maybe_csn)
return maybe_csn;
2022-03-18 11:01:26 +00:00
/// Either transaction is not committed (yet) or it was committed and then the CSN entry was cleaned up from the log.
/// We should load CSN again to distinguish the second case.
/// If entry was cleaned up, then CSN is already stored in VersionMetadata and we will get it.
/// And for the first case we will get UnknownCSN again.
2022-03-14 20:43:34 +00:00
maybe_csn = csn.load();
if (maybe_csn)
return maybe_csn;
if (tid)
TransactionLog::assertTIDIsNotOutdated(*tid);
return Tx::UnknownCSN;
}
2022-01-14 14:03:00 +00:00
VersionMetadata::VersionMetadata()
{
/// It would be better to make it static, but static loggers do not work for some reason (initialization order?)
log = &Poco::Logger::get("VersionMetadata");
}
2021-06-04 09:26:47 +00:00
/// It can be used for introspection purposes only
TransactionID VersionMetadata::getRemovalTID() const
2021-04-08 17:20:45 +00:00
{
2022-03-14 20:43:34 +00:00
TIDHash removal_lock = removal_tid_lock.load();
if (removal_lock)
2021-04-08 17:20:45 +00:00
{
2022-03-14 20:43:34 +00:00
if (removal_lock == Tx::PrehistoricTID.getHash())
2021-06-04 09:26:47 +00:00
return Tx::PrehistoricTID;
2022-03-14 20:43:34 +00:00
if (auto txn = TransactionLog::instance().tryGetRunningTransaction(removal_lock))
2022-02-03 21:40:17 +00:00
return txn->tid;
2021-04-08 17:20:45 +00:00
}
if (removal_csn.load(std::memory_order_relaxed))
2021-04-08 17:20:45 +00:00
{
/// removal_tid cannot be changed since we have removal_csn, so it's readonly
return removal_tid;
2021-04-08 17:20:45 +00:00
}
return Tx::EmptyTID;
}
2022-03-14 20:43:34 +00:00
void VersionMetadata::lockRemovalTID(const TransactionID & tid, const TransactionInfoContext & context)
2021-04-08 17:20:45 +00:00
{
LOG_TEST(log, "Trying to lock removal_tid by {}, table: {}, part: {}", tid, context.table.getNameForLogs(), context.part_name);
2021-12-14 20:06:34 +00:00
TIDHash locked_by = 0;
2022-03-14 20:43:34 +00:00
if (tryLockRemovalTID(tid, context, &locked_by))
2021-12-14 20:06:34 +00:00
return;
2022-01-14 14:03:00 +00:00
String part_desc;
if (context.covering_part.empty())
part_desc = context.part_name;
else
part_desc = fmt::format("{} (covered by {})", context.part_name, context.covering_part);
2021-12-14 20:06:34 +00:00
throw Exception(ErrorCodes::SERIALIZATION_ERROR,
"Serialization error: "
2022-01-14 14:03:00 +00:00
"Transaction {} tried to remove data part {} from {}, "
"but it's locked by another transaction (TID: {}, TIDH: {}) which is currently removing this part.",
tid, part_desc, context.table.getNameForLogs(), getRemovalTID(), locked_by);
2021-12-14 20:06:34 +00:00
}
2022-03-14 20:43:34 +00:00
bool VersionMetadata::tryLockRemovalTID(const TransactionID & tid, const TransactionInfoContext & context, TIDHash * locked_by_id)
2021-12-14 20:06:34 +00:00
{
2022-05-20 10:41:44 +00:00
chassert(!tid.isEmpty());
chassert(!creation_tid.isEmpty());
2022-03-14 20:43:34 +00:00
TIDHash removal_lock_value = tid.getHash();
TIDHash expected_removal_lock_value = 0;
bool locked = removal_tid_lock.compare_exchange_strong(expected_removal_lock_value, removal_lock_value);
2021-04-08 17:20:45 +00:00
if (!locked)
{
2022-06-14 18:20:54 +00:00
if (expected_removal_lock_value == removal_lock_value)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Tried to lock part {} for removal second time by {}", context.part_name, tid);
2021-11-17 18:14:14 +00:00
2021-12-14 20:06:34 +00:00
if (locked_by_id)
2022-03-14 20:43:34 +00:00
*locked_by_id = expected_removal_lock_value;
2021-12-14 20:06:34 +00:00
return false;
2021-04-08 17:20:45 +00:00
}
removal_tid = tid;
2022-01-14 14:03:00 +00:00
tryWriteEventToSystemLog(log, TransactionsInfoLogElement::LOCK_PART, tid, context);
2021-12-14 20:06:34 +00:00
return true;
2021-04-08 17:20:45 +00:00
}
2022-03-14 20:43:34 +00:00
void VersionMetadata::unlockRemovalTID(const TransactionID & tid, const TransactionInfoContext & context)
2021-04-08 17:20:45 +00:00
{
LOG_TEST(log, "Unlocking removal_tid by {}, table: {}, part: {}", tid, context.table.getNameForLogs(), context.part_name);
2022-05-20 10:41:44 +00:00
chassert(!tid.isEmpty());
2022-03-14 20:43:34 +00:00
TIDHash removal_lock_value = tid.getHash();
TIDHash locked_by = removal_tid_lock.load();
2021-04-08 17:20:45 +00:00
auto throw_cannot_unlock = [&]()
{
auto locked_by_txn = TransactionLog::instance().tryGetRunningTransaction(locked_by);
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot unlock removal_tid, it's a bug. Current: {} {}, actual: {} {}",
2022-03-14 20:43:34 +00:00
removal_lock_value, tid, locked_by, locked_by_txn ? locked_by_txn->tid : Tx::EmptyTID);
2021-04-08 17:20:45 +00:00
};
2022-03-14 20:43:34 +00:00
if (locked_by != removal_lock_value)
2021-04-08 17:20:45 +00:00
throw_cannot_unlock();
removal_tid = Tx::EmptyTID;
bool unlocked = removal_tid_lock.compare_exchange_strong(locked_by, 0);
2021-04-08 17:20:45 +00:00
if (!unlocked)
throw_cannot_unlock();
2022-01-14 14:03:00 +00:00
tryWriteEventToSystemLog(log, TransactionsInfoLogElement::UNLOCK_PART, tid, context);
2021-04-08 17:20:45 +00:00
}
2022-02-17 21:26:37 +00:00
bool VersionMetadata::isRemovalTIDLocked() const
2021-06-04 09:26:47 +00:00
{
return removal_tid_lock.load() != 0;
2021-06-04 09:26:47 +00:00
}
2022-02-14 19:50:08 +00:00
void VersionMetadata::setCreationTID(const TransactionID & tid, TransactionInfoContext * context)
2021-04-08 17:20:45 +00:00
{
2022-05-09 19:13:02 +00:00
/// NOTE ReplicatedMergeTreeSink may add one part multiple times
2022-05-20 10:41:44 +00:00
chassert(creation_tid.isEmpty() || creation_tid == tid);
2022-02-14 19:47:17 +00:00
creation_tid = tid;
2022-02-14 19:50:08 +00:00
if (context)
tryWriteEventToSystemLog(log, TransactionsInfoLogElement::ADD_PART, tid, *context);
2021-04-08 17:20:45 +00:00
}
bool VersionMetadata::isVisible(const MergeTreeTransaction & txn)
{
2021-06-04 09:26:47 +00:00
return isVisible(txn.getSnapshot(), txn.tid);
}
2022-03-14 20:43:34 +00:00
bool VersionMetadata::isVisible(CSN snapshot_version, TransactionID current_tid)
2021-06-04 09:26:47 +00:00
{
2022-05-20 10:41:44 +00:00
chassert(!creation_tid.isEmpty());
2022-03-14 20:43:34 +00:00
CSN creation = creation_csn.load(std::memory_order_relaxed);
TIDHash removal_lock = removal_tid_lock.load(std::memory_order_relaxed);
CSN removal = removal_csn.load(std::memory_order_relaxed);
2022-03-14 20:43:34 +00:00
[[maybe_unused]] bool had_creation_csn = creation;
[[maybe_unused]] bool had_removal_tid = removal_lock;
[[maybe_unused]] bool had_removal_csn = removal;
2022-05-20 10:41:44 +00:00
chassert(!had_removal_csn || had_removal_tid);
chassert(!had_removal_csn || had_creation_csn);
chassert(creation == Tx::UnknownCSN || creation == Tx::PrehistoricCSN || Tx::MaxReservedCSN < creation);
chassert(removal == Tx::UnknownCSN || removal == Tx::PrehistoricCSN || Tx::MaxReservedCSN < removal);
2021-04-08 17:20:45 +00:00
2022-03-18 13:33:59 +00:00
/// Special snapshot for introspection purposes
if (unlikely(snapshot_version == Tx::EverythingVisibleCSN))
return true;
2021-04-08 17:20:45 +00:00
/// Fast path:
/// Part is definitely not visible if:
/// - creation was committed after we took the snapshot
/// - removal was committed before we took the snapshot
/// - current transaction is removing it
2022-03-14 20:43:34 +00:00
if (creation && snapshot_version < creation)
2021-04-08 17:20:45 +00:00
return false;
2022-03-14 20:43:34 +00:00
if (removal && removal <= snapshot_version)
2021-04-08 17:20:45 +00:00
return false;
2022-03-14 20:43:34 +00:00
if (!current_tid.isEmpty() && removal_lock && removal_lock == current_tid.getHash())
2021-04-08 17:20:45 +00:00
return false;
/// Otherwise, part is definitely visible if:
2021-05-18 17:07:29 +00:00
/// - creation was committed before we took the snapshot and nobody tried to remove the part
/// - creation was committed before and removal was committed after
2021-04-08 17:20:45 +00:00
/// - current transaction is creating it
2022-03-14 20:43:34 +00:00
if (creation && creation <= snapshot_version && !removal_lock)
2021-05-18 17:07:29 +00:00
return true;
2022-03-14 20:43:34 +00:00
if (creation && creation <= snapshot_version && removal && snapshot_version < removal)
2021-04-08 17:20:45 +00:00
return true;
if (!current_tid.isEmpty() && creation_tid == current_tid)
2021-04-08 17:20:45 +00:00
return true;
/// End of fast path.
/// Data part has creation_tid/removal_tid, but does not have creation_csn/removal_csn.
2021-04-08 17:20:45 +00:00
/// It means that some transaction is creating/removing the part right now or has done it recently
2021-11-08 18:56:09 +00:00
/// and we don't know if it was already committed or not.
2022-05-20 10:41:44 +00:00
chassert(!had_creation_csn || (had_removal_tid && !had_removal_csn));
chassert(current_tid.isEmpty() || (creation_tid != current_tid && removal_lock != current_tid.getHash()));
2021-04-08 17:20:45 +00:00
/// Before doing CSN lookup, let's check some extra conditions.
2021-11-08 18:56:09 +00:00
/// If snapshot_version <= some_tid.start_csn, then changes of the transaction with some_tid
/// are definitely not visible for us (because the transaction can be committed with greater CSN only),
/// so we don't need to check if it was committed.
if (snapshot_version <= creation_tid.start_csn)
2021-04-08 17:20:45 +00:00
return false;
/// Check if creation_tid/removal_tid transactions are committed and write CSNs
2022-03-14 20:43:34 +00:00
/// TODO Transactions: we probably need more optimizations here
2021-04-08 17:20:45 +00:00
/// to avoid some CSN lookups or make the lookups cheaper.
/// NOTE: Old enough committed parts always have written CSNs,
/// so we can determine their visibility through fast path.
/// But for long-running writing transactions we will always do
2022-03-18 11:01:26 +00:00
/// CNS lookup and get 0 (UnknownCSN) until the transaction is committed/rolled back.
2022-03-14 20:43:34 +00:00
creation = getCSNAndAssert(creation_tid.getHash(), creation_csn, &creation_tid);
if (!creation)
{
2021-04-08 17:20:45 +00:00
return false; /// Part creation is not committed yet
2022-03-14 20:43:34 +00:00
}
2021-04-08 17:20:45 +00:00
2021-11-08 18:56:09 +00:00
/// We don't need to check if CSNs are already written or not,
/// because once written CSN cannot be changed, so it's safe to overwrite it (with the same value).
2022-03-14 20:43:34 +00:00
creation_csn.store(creation, std::memory_order_relaxed);
2021-04-08 17:20:45 +00:00
2022-03-14 20:43:34 +00:00
if (removal_lock)
2021-04-08 17:20:45 +00:00
{
2022-03-14 20:43:34 +00:00
removal = getCSNAndAssert(removal_lock, removal_csn);
if (removal)
removal_csn.store(removal, std::memory_order_relaxed);
2021-04-08 17:20:45 +00:00
}
2022-03-14 20:43:34 +00:00
return creation <= snapshot_version && (!removal || snapshot_version < removal);
2021-04-08 17:20:45 +00:00
}
bool VersionMetadata::canBeRemoved()
{
if (creation_tid == Tx::PrehistoricTID)
{
/// Avoid access to Transaction log if transactions are not involved
2022-03-14 20:43:34 +00:00
TIDHash removal_lock = removal_tid_lock.load(std::memory_order_relaxed);
if (!removal_lock)
return false;
2022-03-14 20:43:34 +00:00
if (removal_lock == Tx::PrehistoricTID.getHash())
return true;
}
return canBeRemovedImpl(TransactionLog::instance().getOldestSnapshot());
}
2022-03-14 20:43:34 +00:00
bool VersionMetadata::canBeRemovedImpl(CSN oldest_snapshot_version)
2021-06-11 12:14:38 +00:00
{
2022-03-14 20:43:34 +00:00
CSN creation = creation_csn.load(std::memory_order_relaxed);
2022-01-14 14:03:00 +00:00
/// We can safely remove part if its creation was rolled back
2022-03-14 20:43:34 +00:00
if (creation == Tx::RolledBackCSN)
2021-06-11 12:14:38 +00:00
return true;
2022-03-14 20:43:34 +00:00
if (!creation)
2021-06-11 12:14:38 +00:00
{
2022-01-14 14:03:00 +00:00
/// Cannot remove part if its creation not committed yet
2022-03-14 20:43:34 +00:00
creation = getCSNAndAssert(creation_tid.getHash(), creation_csn, &creation_tid);
if (creation)
creation_csn.store(creation, std::memory_order_relaxed);
2021-06-11 12:14:38 +00:00
else
return false;
}
2022-01-14 14:03:00 +00:00
/// Part is probably visible for some transactions (part is too new or the oldest snapshot is too old)
2022-03-14 20:43:34 +00:00
if (oldest_snapshot_version < creation)
2021-06-11 12:14:38 +00:00
return false;
2022-03-14 20:43:34 +00:00
TIDHash removal_lock = removal_tid_lock.load(std::memory_order_relaxed);
2022-01-14 14:03:00 +00:00
/// Part is active
2022-03-14 20:43:34 +00:00
if (!removal_lock)
2021-06-11 12:14:38 +00:00
return false;
2022-03-14 20:43:34 +00:00
CSN removal = removal_csn.load(std::memory_order_relaxed);
if (!removal)
2021-06-11 12:14:38 +00:00
{
2022-01-14 14:03:00 +00:00
/// Part removal is not committed yet
2022-03-14 20:43:34 +00:00
removal = getCSNAndAssert(removal_lock, removal_csn);
if (removal)
removal_csn.store(removal, std::memory_order_relaxed);
2021-06-11 12:14:38 +00:00
else
return false;
}
2022-01-14 14:03:00 +00:00
/// We can safely remove part if all running transactions were started after part removal was committed
2022-03-14 20:43:34 +00:00
return removal <= oldest_snapshot_version;
2021-06-11 12:14:38 +00:00
}
#define CREATION_TID_STR "creation_tid: "
#define CREATION_CSN_STR "creation_csn: "
#define REMOVAL_TID_STR "removal_tid: "
#define REMOVAL_CSN_STR "removal_csn: "
2022-02-17 21:26:37 +00:00
void VersionMetadata::writeCSN(WriteBuffer & buf, WhichCSN which_csn, bool internal /* = false*/) const
{
if (which_csn == CREATION)
{
2022-03-14 20:43:34 +00:00
if (CSN creation = creation_csn.load())
2022-02-17 21:26:37 +00:00
{
writeCString("\n" CREATION_CSN_STR, buf);
2022-03-14 20:43:34 +00:00
writeText(creation, buf);
2022-02-17 21:26:37 +00:00
}
else if (!internal)
throw Exception(ErrorCodes::LOGICAL_ERROR, "writeCSN called for creation_csn = 0, it's a bug");
}
else /// if (which_csn == REMOVAL)
{
2022-03-14 20:43:34 +00:00
if (CSN removal = removal_csn.load())
2022-02-17 21:26:37 +00:00
{
writeCString("\n" REMOVAL_CSN_STR, buf);
2022-03-14 20:43:34 +00:00
writeText(removal, buf);
2022-02-17 21:26:37 +00:00
}
else if (!internal)
throw Exception(ErrorCodes::LOGICAL_ERROR, "writeCSN called for removal_csn = 0, it's a bug");
}
}
2022-03-08 19:11:47 +00:00
void VersionMetadata::writeRemovalTID(WriteBuffer & buf, bool clear) const
{
writeCString("\n" REMOVAL_TID_STR, buf);
if (clear)
TransactionID::write(Tx::EmptyTID, buf);
else
TransactionID::write(removal_tid, buf);
}
2021-12-30 13:15:28 +00:00
void VersionMetadata::write(WriteBuffer & buf) const
{
writeCString("version: 1", buf);
writeCString("\n" CREATION_TID_STR, buf);
TransactionID::write(creation_tid, buf);
2022-02-17 21:26:37 +00:00
writeCSN(buf, CREATION, /* internal */ true);
2021-12-30 13:15:28 +00:00
if (removal_tid_lock)
2021-12-30 13:15:28 +00:00
{
2022-05-20 10:41:44 +00:00
chassert(!removal_tid.isEmpty());
chassert(removal_tid.getHash() == removal_tid_lock);
2022-03-08 19:11:47 +00:00
writeRemovalTID(buf);
2022-02-17 21:26:37 +00:00
writeCSN(buf, REMOVAL, /* internal */ true);
2021-12-30 13:15:28 +00:00
}
}
void VersionMetadata::read(ReadBuffer & buf)
{
constexpr size_t size = sizeof(CREATION_TID_STR) - 1;
static_assert(sizeof(CREATION_CSN_STR) - 1 == size);
static_assert(sizeof(REMOVAL_TID_STR) - 1 == size);
static_assert(sizeof(REMOVAL_CSN_STR) - 1 == size);
2021-12-30 13:15:28 +00:00
assertString("version: 1", buf);
assertString("\n" CREATION_TID_STR, buf);
creation_tid = TransactionID::read(buf);
2021-12-30 13:15:28 +00:00
if (buf.eof())
return;
String name;
name.resize(size);
2022-02-17 21:26:37 +00:00
auto read_csn = [&]()
2021-12-30 13:15:28 +00:00
{
2022-02-17 21:26:37 +00:00
UInt64 val;
readText(val, buf);
return val;
};
2021-12-30 13:15:28 +00:00
2022-02-17 21:26:37 +00:00
while (!buf.eof())
2021-12-30 13:15:28 +00:00
{
2022-01-19 18:29:31 +00:00
assertChar('\n', buf);
buf.readStrict(name.data(), size);
2021-12-30 13:15:28 +00:00
2022-02-17 21:26:37 +00:00
if (name == CREATION_CSN_STR)
{
2022-05-20 10:41:44 +00:00
chassert(!creation_csn);
2022-02-17 21:26:37 +00:00
creation_csn = read_csn();
}
else if (name == REMOVAL_TID_STR)
{
2022-03-08 19:11:47 +00:00
/// NOTE Metadata file may actually contain multiple creation TIDs, we need the last one.
2022-02-17 21:26:37 +00:00
removal_tid = TransactionID::read(buf);
2022-05-20 20:08:46 +00:00
if (removal_tid.isEmpty())
removal_tid_lock = 0;
else
removal_tid_lock = removal_tid.getHash();
2022-02-17 21:26:37 +00:00
}
else if (name == REMOVAL_CSN_STR)
{
if (removal_tid.isEmpty())
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Found removal_csn in metadata file, but removal_tid is {}", removal_tid);
2022-05-20 10:41:44 +00:00
chassert(!removal_csn);
2022-02-17 21:26:37 +00:00
removal_csn = read_csn();
}
else
{
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Got unexpected content: {}", name);
}
2021-12-30 13:15:28 +00:00
}
}
String VersionMetadata::toString(bool one_line) const
{
WriteBufferFromOwnString buf;
write(buf);
String res = buf.str();
if (one_line)
std::replace(res.begin(), res.end(), '\n', ' ');
return res;
}
2021-11-08 18:56:09 +00:00
DataTypePtr getTransactionIDDataType()
{
DataTypes types;
types.push_back(std::make_shared<DataTypeUInt64>());
types.push_back(std::make_shared<DataTypeUInt64>());
types.push_back(std::make_shared<DataTypeUUID>());
return std::make_shared<DataTypeTuple>(std::move(types));
}
2021-03-31 17:55:04 +00:00
}