Skip to content

Commit

Permalink
lock categories to control different lock-purposes (#12163)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanmorozov333 authored Dec 2, 2024
1 parent b458331 commit ad82e86
Show file tree
Hide file tree
Showing 23 changed files with 184 additions and 88 deletions.
66 changes: 53 additions & 13 deletions ydb/core/tx/columnshard/data_locks/locks/abstract.h
Original file line number Diff line number Diff line change
@@ -1,53 +1,93 @@
#pragma once
#include <ydb/library/accessor/accessor.h>

#include <util/generic/string.h>
#include <util/generic/hash_set.h>
#include <util/generic/string.h>

#include <optional>
#include <memory>
#include <optional>
#include <set>
#include <vector>

namespace NKikimr::NOlap {
class TPortionInfo;
class TGranuleMeta;
}
} // namespace NKikimr::NOlap

namespace NKikimr::NOlap::NDataLocks {

enum class ELockCategory : ui32 {
Compaction = 0,
Cleanup,
Sharing,
Actualization,
Tables,
Any,
MAX
};

static const inline std::array<std::set<ELockCategory>, (ui32)ELockCategory::MAX> LockCategoriesInteraction = {
//Compaction
std::set<ELockCategory>({ ELockCategory::Compaction, ELockCategory::Actualization, ELockCategory::Tables, ELockCategory::Any}),
//Cleanup
std::set<ELockCategory>({ ELockCategory::Cleanup, ELockCategory::Sharing, ELockCategory::Tables, ELockCategory::Any }),
//Sharing
std::set<ELockCategory>({ ELockCategory::Sharing, ELockCategory::Cleanup, ELockCategory::Tables, ELockCategory::Any }),
//Actualization
std::set<ELockCategory>({ ELockCategory::Actualization, ELockCategory::Compaction, ELockCategory::Tables, ELockCategory::Any }),
//Tables
std::set<ELockCategory>({ ELockCategory::Cleanup, ELockCategory::Sharing, ELockCategory::Actualization, ELockCategory::Compaction,
ELockCategory::Tables, ELockCategory::Any }),
//Any
std::set<ELockCategory>({ ELockCategory::Cleanup, ELockCategory::Sharing, ELockCategory::Actualization, ELockCategory::Compaction,
ELockCategory::Tables, ELockCategory::Any }),
};

class ILock {
private:
YDB_READONLY_DEF(TString, LockName);
YDB_READONLY_FLAG(ReadOnly, false);
const ELockCategory Category;

protected:
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const THashSet<TString>& excludedLocks = {}) const = 0;
virtual std::optional<TString> DoIsLocked(const TGranuleMeta& granule, const THashSet<TString>& excludedLocks = {}) const = 0;
virtual std::optional<TString> DoIsLocked(
const TPortionInfo& portion, const ELockCategory category, const THashSet<TString>& excludedLocks = {}) const = 0;
virtual std::optional<TString> DoIsLocked(
const TGranuleMeta& granule, const ELockCategory category, const THashSet<TString>& excludedLocks = {}) const = 0;
virtual bool DoIsEmpty() const = 0;

public:
ILock(const TString& lockName, const bool isReadOnly = false)
ILock(const TString& lockName, const ELockCategory category, const bool isReadOnly = false)
: LockName(lockName)
, ReadOnlyFlag(isReadOnly)
{

, Category(category) {
}

virtual ~ILock() = default;

std::optional<TString> IsLocked(const TPortionInfo& portion, const THashSet<TString>& excludedLocks = {}, const bool readOnly = false) const {
std::optional<TString> IsLocked(const TPortionInfo& portion, const ELockCategory portionForLock, const THashSet<TString>& excludedLocks = {},
const bool readOnly = false) const {
if (IsReadOnly() && readOnly) {
return {};
}
return DoIsLocked(portion, excludedLocks);
if (!LockCategoriesInteraction[(ui32)Category].contains(portionForLock)) {
return {};
}
return DoIsLocked(portion, portionForLock, excludedLocks);
}
std::optional<TString> IsLocked(const TGranuleMeta& g, const THashSet<TString>& excludedLocks = {}, const bool readOnly = false) const {
std::optional<TString> IsLocked(const TGranuleMeta& g, const ELockCategory portionForLock, const THashSet<TString>& excludedLocks = {},
const bool readOnly = false) const {
if (IsReadOnly() && readOnly) {
return {};
}
return DoIsLocked(g, excludedLocks);
if (!LockCategoriesInteraction[(ui32)Category].contains(portionForLock)) {
return {};
}
return DoIsLocked(g, portionForLock, excludedLocks);
}
bool IsEmpty() const {
return DoIsEmpty();
}
};

}
} // namespace NKikimr::NOlap::NDataLocks
19 changes: 11 additions & 8 deletions ydb/core/tx/columnshard/data_locks/locks/composite.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,24 @@ class TCompositeLock: public ILock {
using TBase = ILock;
std::vector<std::shared_ptr<ILock>> Locks;
protected:
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const THashSet<TString>& excludedLocks) const override {
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const ELockCategory category, const THashSet<TString>& excludedLocks) const override {
for (auto&& i : Locks) {
if (excludedLocks.contains(i->GetLockName())) {
continue;
}
if (auto lockName = i->IsLocked(portion)) {
if (auto lockName = i->IsLocked(portion, category)) {
return lockName;
}
}
return {};
}
virtual std::optional<TString> DoIsLocked(const TGranuleMeta& granule, const THashSet<TString>& excludedLocks) const override {
virtual std::optional<TString> DoIsLocked(
const TGranuleMeta& granule, const ELockCategory category, const THashSet<TString>& excludedLocks) const override {
for (auto&& i : Locks) {
if (excludedLocks.contains(i->GetLockName())) {
continue;
}
if (auto lockName = i->IsLocked(granule)) {
if (auto lockName = i->IsLocked(granule, category)) {
return lockName;
}
}
Expand All @@ -34,8 +35,9 @@ class TCompositeLock: public ILock {
return Locks.empty();
}
public:
TCompositeLock(const TString& lockName, const std::vector<std::shared_ptr<ILock>>& locks, const bool readOnly = false)
: TBase(lockName, readOnly)
TCompositeLock(const TString& lockName, const std::vector<std::shared_ptr<ILock>>& locks,
const ELockCategory category = NDataLocks::ELockCategory::Any, const bool readOnly = false)
: TBase(lockName, category, readOnly)
{
for (auto&& l : locks) {
if (!l || l->IsEmpty()) {
Expand All @@ -45,8 +47,9 @@ class TCompositeLock: public ILock {
}
}

TCompositeLock(const TString& lockName, std::initializer_list<std::shared_ptr<ILock>> locks, const bool readOnly = false)
: TBase(lockName, readOnly)
TCompositeLock(const TString& lockName, std::initializer_list<std::shared_ptr<ILock>> locks,
const ELockCategory category = NDataLocks::ELockCategory::Any, const bool readOnly = false)
: TBase(lockName, category, readOnly)
{
for (auto&& l : locks) {
if (!l || l->IsEmpty()) {
Expand Down
50 changes: 30 additions & 20 deletions ydb/core/tx/columnshard/data_locks/locks/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ class TListPortionsLock: public ILock {
THashSet<TPortionAddress> Portions;
THashSet<ui64> Granules;
protected:
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TPortionInfo& portion, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (Portions.contains(portion.GetAddress())) {
return GetLockName();
}
return {};
}
virtual std::optional<TString> DoIsLocked(const TGranuleMeta& granule, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TGranuleMeta& granule, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (Granules.contains(granule.GetPathId())) {
return GetLockName();
}
Expand All @@ -27,42 +29,46 @@ class TListPortionsLock: public ILock {
return Portions.empty();
}
public:
TListPortionsLock(const TString& lockName, const std::vector<TPortionDataAccessor>& portions, const bool readOnly = false)
: TBase(lockName, readOnly)
TListPortionsLock(const TString& lockName, const std::vector<TPortionDataAccessor>& portions, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly)
{
for (auto&& p : portions) {
Portions.emplace(p.GetPortionInfo().GetAddress());
Granules.emplace(p.GetPortionInfo().GetPathId());
}
}

TListPortionsLock(const TString& lockName, const std::vector<std::shared_ptr<TPortionInfo>>& portions, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(const TString& lockName, const std::vector<std::shared_ptr<TPortionInfo>>& portions, const ELockCategory category,
const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& p : portions) {
Portions.emplace(p->GetAddress());
Granules.emplace(p->GetPathId());
}
}

TListPortionsLock(const TString& lockName, const std::vector<TPortionInfo::TConstPtr>& portions, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(
const TString& lockName, const std::vector<TPortionInfo::TConstPtr>& portions, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& p : portions) {
Portions.emplace(p->GetAddress());
Granules.emplace(p->GetPathId());
}
}

TListPortionsLock(const TString& lockName, const std::vector<TPortionInfo>& portions, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(
const TString& lockName, const std::vector<TPortionInfo>& portions, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& p : portions) {
Portions.emplace(p.GetAddress());
Granules.emplace(p.GetPathId());
}
}

template <class T, class TGetter>
TListPortionsLock(const TString& lockName, const std::vector<T>& portions, const TGetter& g, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(
const TString& lockName, const std::vector<T>& portions, const TGetter& g, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& p : portions) {
const auto address = g(p);
Portions.emplace(address);
Expand All @@ -71,17 +77,19 @@ class TListPortionsLock: public ILock {
}

template <class T>
TListPortionsLock(const TString& lockName, const THashMap<TPortionAddress, T>& portions, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(
const TString& lockName, const THashMap<TPortionAddress, T>& portions, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& p : portions) {
const auto address = p.first;
Portions.emplace(address);
Granules.emplace(address.GetPathId());
}
}

TListPortionsLock(const TString& lockName, const THashSet<TPortionAddress>& portions, const bool readOnly = false)
: TBase(lockName, readOnly) {
TListPortionsLock(
const TString& lockName, const THashSet<TPortionAddress>& portions, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly) {
for (auto&& address : portions) {
Portions.emplace(address);
Granules.emplace(address.GetPathId());
Expand All @@ -94,13 +102,15 @@ class TListTablesLock: public ILock {
using TBase = ILock;
THashSet<ui64> Tables;
protected:
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TPortionInfo& portion, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (Tables.contains(portion.GetPathId())) {
return GetLockName();
}
return {};
}
virtual std::optional<TString> DoIsLocked(const TGranuleMeta& granule, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TGranuleMeta& granule, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (Tables.contains(granule.GetPathId())) {
return GetLockName();
}
Expand All @@ -110,8 +120,8 @@ class TListTablesLock: public ILock {
return Tables.empty();
}
public:
TListTablesLock(const TString& lockName, const THashSet<ui64>& tables, const bool readOnly = false)
: TBase(lockName, readOnly)
TListTablesLock(const TString& lockName, const THashSet<ui64>& tables, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly)
, Tables(tables)
{
}
Expand Down
10 changes: 6 additions & 4 deletions ydb/core/tx/columnshard/data_locks/locks/snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class TSnapshotLock: public ILock {
const TSnapshot SnapshotBarrier;
const THashSet<ui64> PathIds;
protected:
virtual std::optional<TString> DoIsLocked(const TPortionInfo& portion, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TPortionInfo& portion, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (PathIds.contains(portion.GetPathId()) && portion.RecordSnapshotMin() <= SnapshotBarrier) {
return GetLockName();
}
Expand All @@ -20,15 +21,16 @@ class TSnapshotLock: public ILock {
virtual bool DoIsEmpty() const override {
return PathIds.empty();
}
virtual std::optional<TString> DoIsLocked(const TGranuleMeta& granule, const THashSet<TString>& /*excludedLocks*/) const override {
virtual std::optional<TString> DoIsLocked(
const TGranuleMeta& granule, const ELockCategory /*category*/, const THashSet<TString>& /*excludedLocks*/) const override {
if (PathIds.contains(granule.GetPathId())) {
return GetLockName();
}
return {};
}
public:
TSnapshotLock(const TString& lockName, const TSnapshot& snapshotBarrier, const THashSet<ui64>& pathIds, const bool readOnly = false)
: TBase(lockName, readOnly)
TSnapshotLock(const TString& lockName, const TSnapshot& snapshotBarrier, const THashSet<ui64>& pathIds, const ELockCategory category, const bool readOnly = false)
: TBase(lockName, category, readOnly)
, SnapshotBarrier(snapshotBarrier)
, PathIds(pathIds)
{
Expand Down
16 changes: 9 additions & 7 deletions ydb/core/tx/columnshard/data_locks/manager/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,36 @@ void TManager::UnregisterLock(const TString& processId) {
AFL_VERIFY(ProcessLocks.erase(processId))("process_id", processId);
}

std::optional<TString> TManager::IsLocked(const TPortionInfo& portion, const THashSet<TString>& excludedLocks) const {
std::optional<TString> TManager::IsLocked(
const TPortionInfo& portion, const ELockCategory lockCategory, const THashSet<TString>& excludedLocks) const {
for (auto&& i : ProcessLocks) {
if (excludedLocks.contains(i.first)) {
continue;
}
if (auto lockName = i.second->IsLocked(portion, excludedLocks)) {
if (auto lockName = i.second->IsLocked(portion, lockCategory, excludedLocks)) {
return lockName;
}
}
return {};
}

std::optional<TString> TManager::IsLocked(const TGranuleMeta& granule, const THashSet<TString>& excludedLocks) const {
std::optional<TString> TManager::IsLocked(
const TGranuleMeta& granule, const ELockCategory lockCategory, const THashSet<TString>& excludedLocks) const {
for (auto&& i : ProcessLocks) {
if (excludedLocks.contains(i.first)) {
continue;
}
if (auto lockName = i.second->IsLocked(granule, excludedLocks)) {
if (auto lockName = i.second->IsLocked(granule, lockCategory, excludedLocks)) {
return lockName;
}
}
return {};
}

std::optional<TString> TManager::IsLocked(
const std::shared_ptr<const TPortionInfo>& portion, const THashSet<TString>& excludedLocks /*= {}*/) const {
std::optional<TString> TManager::IsLocked(const std::shared_ptr<const TPortionInfo>& portion, const ELockCategory lockCategory,
const THashSet<TString>& excludedLocks /*= {}*/) const {
AFL_VERIFY(!!portion);
return IsLocked(*portion, excludedLocks);
return IsLocked(*portion, lockCategory, excludedLocks);
}

void TManager::Stop() {
Expand Down
9 changes: 6 additions & 3 deletions ydb/core/tx/columnshard/data_locks/manager/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ class TManager {
[[nodiscard]] std::shared_ptr<TGuard> RegisterLock(Args&&... args) {
return RegisterLock(std::make_shared<TLock>(args...));
}
std::optional<TString> IsLocked(const TPortionInfo& portion, const THashSet<TString>& excludedLocks = {}) const;
std::optional<TString> IsLocked(const std::shared_ptr<const TPortionInfo>& portion, const THashSet<TString>& excludedLocks = {}) const;
std::optional<TString> IsLocked(const TGranuleMeta& granule, const THashSet<TString>& excludedLocks = {}) const;
std::optional<TString> IsLocked(
const TPortionInfo& portion, const ELockCategory lockCategory, const THashSet<TString>& excludedLocks = {}) const;
std::optional<TString> IsLocked(
const std::shared_ptr<const TPortionInfo>& portion, const ELockCategory lockCategory, const THashSet<TString>& excludedLocks = {}) const;
std::optional<TString> IsLocked(
const TGranuleMeta& granule, const ELockCategory lockCategory, const THashSet<TString>& excludedLocks = {}) const;

};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ TConclusionStatus TCommonSession::TryStart(NColumnShard::TColumnShard& shard) {
for (auto&& i : GetPathIdsForStart()) {
const auto& g = index.GetGranuleVerified(i);
for (auto&& p : g.GetPortionsOlderThenSnapshot(GetSnapshotBarrier())) {
if (shard.GetDataLocksManager()->IsLocked(*p.second, { "sharing_session:" + GetSessionId() })) {
if (shard.GetDataLocksManager()->IsLocked(
*p.second, NDataLocks::ELockCategory::Sharing, { "sharing_session:" + GetSessionId() })) {
return TConclusionStatus::Fail("failed to start cursor: portion is locked");
}
// portionsByPath[i].emplace_back(p.second);
Expand All @@ -48,7 +49,7 @@ void TCommonSession::PrepareToStart(const NColumnShard::TColumnShard& shard) {
State = EState::Prepared;
AFL_VERIFY(!LockGuard);
LockGuard = shard.GetDataLocksManager()->RegisterLock<NDataLocks::TSnapshotLock>("sharing_session:" + GetSessionId(),
TransferContext.GetSnapshotBarrierVerified(), GetPathIdsForStart(), true);
TransferContext.GetSnapshotBarrierVerified(), GetPathIdsForStart(), NDataLocks::ELockCategory::Sharing, true);
shard.GetSharingSessionsManager()->StartSharingSession();
}

Expand Down
Loading

0 comments on commit ad82e86

Please sign in to comment.