Merge pull request #30658 from ClickHouse/std_mutex_alter_lock

Change `alter_lock` from `RWLock` to `std::timed_mutex`
This commit is contained in:
alesapin 2021-10-27 11:14:11 +03:00 committed by GitHub
commit 2af950d4d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 39 additions and 37 deletions

View File

@ -76,7 +76,8 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter)
StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext()); StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext());
if (table->isStaticStorage()) if (table->isStaticStorage())
throw Exception(ErrorCodes::TABLE_IS_READ_ONLY, "Table is read-only"); throw Exception(ErrorCodes::TABLE_IS_READ_ONLY, "Table is read-only");
auto alter_lock = table->lockForAlter(getContext()->getCurrentQueryId(), getContext()->getSettingsRef().lock_acquire_timeout); auto table_lock = table->lockForShare(getContext()->getCurrentQueryId(), getContext()->getSettingsRef().lock_acquire_timeout);
auto alter_lock = table->lockForAlter(getContext()->getSettingsRef().lock_acquire_timeout);
auto metadata_snapshot = table->getInMemoryMetadataPtr(); auto metadata_snapshot = table->getInMemoryMetadataPtr();
/// Add default database to table identifiers that we can encounter in e.g. default expressions, mutation expression, etc. /// Add default database to table identifiers that we can encounter in e.g. default expressions, mutation expression, etc.

View File

@ -60,27 +60,31 @@ TableLockHolder IStorage::lockForShare(const String & query_id, const std::chron
return result; return result;
} }
TableLockHolder IStorage::lockForAlter(const String & query_id, const std::chrono::milliseconds & acquire_timeout) IStorage::AlterLockHolder IStorage::lockForAlter(const std::chrono::milliseconds & acquire_timeout)
{ {
TableLockHolder result = tryLockTimed(alter_lock, RWLockImpl::Write, query_id, acquire_timeout); AlterLockHolder lock{alter_lock, std::defer_lock};
if (!lock.try_lock_for(acquire_timeout))
throw Exception(ErrorCodes::DEADLOCK_AVOIDED,
"Locking attempt for ALTER on \"{}\" has timed out! ({} ms) "
"Possible deadlock avoided. Client should retry.",
getStorageID().getFullTableName(), std::to_string(acquire_timeout.count()));
if (is_dropped) if (is_dropped)
throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED);
return result; return lock;
} }
TableExclusiveLockHolder IStorage::lockExclusively(const String & query_id, const std::chrono::milliseconds & acquire_timeout) TableExclusiveLockHolder IStorage::lockExclusively(const String & query_id, const std::chrono::milliseconds & acquire_timeout)
{ {
TableExclusiveLockHolder result; TableExclusiveLockHolder result;
result.alter_lock = tryLockTimed(alter_lock, RWLockImpl::Write, query_id, acquire_timeout); result.drop_lock = tryLockTimed(drop_lock, RWLockImpl::Write, query_id, acquire_timeout);
if (is_dropped) if (is_dropped)
throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED);
result.drop_lock = tryLockTimed(drop_lock, RWLockImpl::Write, query_id, acquire_timeout);
return result; return result;
} }
@ -126,7 +130,7 @@ Pipe IStorage::alterPartition(
throw Exception("Partition operations are not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); throw Exception("Partition operations are not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
} }
void IStorage::alter(const AlterCommands & params, ContextPtr context, TableLockHolder &) void IStorage::alter(const AlterCommands & params, ContextPtr context, AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();
StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata();

View File

@ -238,7 +238,8 @@ public:
/// Lock table for alter. This lock must be acuqired in ALTER queries to be /// Lock table for alter. This lock must be acuqired in ALTER queries to be
/// sure, that we execute only one simultaneous alter. Doesn't affect share lock. /// sure, that we execute only one simultaneous alter. Doesn't affect share lock.
TableLockHolder lockForAlter(const String & query_id, const std::chrono::milliseconds & acquire_timeout); using AlterLockHolder = std::unique_lock<std::timed_mutex>;
AlterLockHolder lockForAlter(const std::chrono::milliseconds & acquire_timeout);
/// Lock table exclusively. This lock must be acquired if you want to be /// Lock table exclusively. This lock must be acquired if you want to be
/// sure, that no other thread (SELECT, merge, ALTER, etc.) doing something /// sure, that no other thread (SELECT, merge, ALTER, etc.) doing something
@ -417,7 +418,7 @@ public:
/** ALTER tables in the form of column changes that do not affect the change /** ALTER tables in the form of column changes that do not affect the change
* to Storage or its parameters. Executes under alter lock (lockForAlter). * to Storage or its parameters. Executes under alter lock (lockForAlter).
*/ */
virtual void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & alter_lock_holder); virtual void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & alter_lock_holder);
/** Checks that alter commands can be applied to storage. For example, columns can be modified, /** Checks that alter commands can be applied to storage. For example, columns can be modified,
* or primary key can be changes, etc. * or primary key can be changes, etc.
@ -596,12 +597,9 @@ public:
virtual bool dropTableImmediately() { return false; } virtual bool dropTableImmediately() { return false; }
private: private:
/// Lock required for alter queries (lockForAlter). Always taken for write /// Lock required for alter queries (lockForAlter).
/// (actually can be replaced with std::mutex, but for consistency we use /// Allows to execute only one simultaneous alter query.
/// RWLock). Allows to execute only one simultaneous alter query. Also it mutable std::timed_mutex alter_lock;
/// should be taken by DROP-like queries, to be sure, that all alters are
/// finished.
mutable RWLock alter_lock = RWLockImpl::create();
/// Lock required for drop queries. Every thread that want to ensure, that /// Lock required for drop queries. Every thread that want to ensure, that
/// table is not dropped have to table this lock for read (lockForShare). /// table is not dropped have to table this lock for read (lockForShare).

View File

@ -2211,7 +2211,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeData::createPart(
void MergeTreeData::changeSettings( void MergeTreeData::changeSettings(
const ASTPtr & new_settings, const ASTPtr & new_settings,
TableLockHolder & /* table_lock_holder */) AlterLockHolder & /* table_lock_holder */)
{ {
if (new_settings) if (new_settings)
{ {

View File

@ -583,7 +583,7 @@ public:
/// Change MergeTreeSettings /// Change MergeTreeSettings
void changeSettings( void changeSettings(
const ASTPtr & new_settings, const ASTPtr & new_settings,
TableLockHolder & table_lock_holder); AlterLockHolder & table_lock_holder);
/// Should be called if part data is suspected to be corrupted. /// Should be called if part data is suspected to be corrupted.
void reportBrokenPart(const String & name) const void reportBrokenPart(const String & name) const

View File

@ -1066,7 +1066,7 @@ std::optional<UInt64> StorageBuffer::totalBytes(const Settings & /*settings*/) c
return total_writes.bytes; return total_writes.bytes;
} }
void StorageBuffer::alter(const AlterCommands & params, ContextPtr local_context, TableLockHolder &) void StorageBuffer::alter(const AlterCommands & params, ContextPtr local_context, AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();
checkAlterIsPossible(params, local_context); checkAlterIsPossible(params, local_context);

View File

@ -107,7 +107,7 @@ public:
void checkAlterIsPossible(const AlterCommands & commands, ContextPtr context) const override; void checkAlterIsPossible(const AlterCommands & commands, ContextPtr context) const override;
/// The structure of the subordinate table is not checked and does not change. /// The structure of the subordinate table is not checked and does not change.
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & table_lock_holder) override;
std::optional<UInt64> totalRows(const Settings & settings) const override; std::optional<UInt64> totalRows(const Settings & settings) const override;
std::optional<UInt64> totalBytes(const Settings & settings) const override; std::optional<UInt64> totalBytes(const Settings & settings) const override;

View File

@ -785,7 +785,7 @@ void StorageDistributed::checkAlterIsPossible(const AlterCommands & commands, Co
} }
} }
void StorageDistributed::alter(const AlterCommands & params, ContextPtr local_context, TableLockHolder &) void StorageDistributed::alter(const AlterCommands & params, ContextPtr local_context, AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();

View File

@ -94,7 +94,7 @@ public:
/// in the sub-tables, you need to manually add and delete columns /// in the sub-tables, you need to manually add and delete columns
/// the structure of the sub-table is not checked /// the structure of the sub-table is not checked
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & table_lock_holder) override;
void startup() override; void startup() override;
void shutdown() override; void shutdown() override;

View File

@ -274,7 +274,7 @@ bool StorageMaterializedView::optimize(
void StorageMaterializedView::alter( void StorageMaterializedView::alter(
const AlterCommands & params, const AlterCommands & params,
ContextPtr local_context, ContextPtr local_context,
TableLockHolder &) AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();
StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata();

View File

@ -50,7 +50,7 @@ public:
const Names & deduplicate_by_columns, const Names & deduplicate_by_columns,
ContextPtr context) override; ContextPtr context) override;
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & table_lock_holder) override;
void checkMutationIsPossible(const MutationCommands & commands, const Settings & settings) const override; void checkMutationIsPossible(const MutationCommands & commands, const Settings & settings) const override;

View File

@ -651,7 +651,7 @@ void StorageMerge::checkAlterIsPossible(const AlterCommands & commands, ContextP
} }
void StorageMerge::alter( void StorageMerge::alter(
const AlterCommands & params, ContextPtr local_context, TableLockHolder &) const AlterCommands & params, ContextPtr local_context, AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();

View File

@ -43,7 +43,7 @@ public:
/// you need to add and remove columns in the sub-tables manually /// you need to add and remove columns in the sub-tables manually
/// the structure of sub-tables is not checked /// the structure of sub-tables is not checked
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & table_lock_holder) override;
bool mayBenefitFromIndexForIn( bool mayBenefitFromIndexForIn(
const ASTPtr & left_in_operand, ContextPtr query_context, const StorageMetadataPtr & metadata_snapshot) const override; const ASTPtr & left_in_operand, ContextPtr query_context, const StorageMetadataPtr & metadata_snapshot) const override;

View File

@ -271,7 +271,7 @@ void StorageMergeTree::truncate(const ASTPtr &, const StorageMetadataPtr &, Cont
void StorageMergeTree::alter( void StorageMergeTree::alter(
const AlterCommands & commands, const AlterCommands & commands,
ContextPtr local_context, ContextPtr local_context,
TableLockHolder & table_lock_holder) AlterLockHolder & table_lock_holder)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();
auto old_storage_settings = getSettings(); auto old_storage_settings = getSettings();

View File

@ -86,7 +86,7 @@ public:
void drop() override; void drop() override;
void truncate(const ASTPtr &, const StorageMetadataPtr &, ContextPtr, TableExclusiveLockHolder &) override; void truncate(const ASTPtr &, const StorageMetadataPtr &, ContextPtr, TableExclusiveLockHolder &) override;
void alter(const AlterCommands & commands, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & commands, ContextPtr context, AlterLockHolder & table_lock_holder) override;
void checkTableCanBeDropped() const override; void checkTableCanBeDropped() const override;

View File

@ -64,7 +64,7 @@ void StorageNull::checkAlterIsPossible(const AlterCommands & commands, ContextPt
} }
void StorageNull::alter(const AlterCommands & params, ContextPtr context, TableLockHolder &) void StorageNull::alter(const AlterCommands & params, ContextPtr context, AlterLockHolder &)
{ {
auto table_id = getStorageID(); auto table_id = getStorageID();

View File

@ -43,7 +43,7 @@ public:
void checkAlterIsPossible(const AlterCommands & commands, ContextPtr context) const override; void checkAlterIsPossible(const AlterCommands & commands, ContextPtr context) const override;
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & table_lock_holder) override;
std::optional<UInt64> totalRows(const Settings &) const override std::optional<UInt64> totalRows(const Settings &) const override
{ {

View File

@ -91,7 +91,7 @@ public:
IStorage::renameInMemory(new_table_id); IStorage::renameInMemory(new_table_id);
} }
void alter(const AlterCommands & params, ContextPtr context, TableLockHolder & alter_lock_holder) override void alter(const AlterCommands & params, ContextPtr context, AlterLockHolder & alter_lock_holder) override
{ {
getNested()->alter(params, context, alter_lock_holder); getNested()->alter(params, context, alter_lock_holder);
IStorage::setInMemoryMetadata(getNested()->getInMemoryMetadata()); IStorage::setInMemoryMetadata(getNested()->getInMemoryMetadata());

View File

@ -4355,7 +4355,8 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer
zookeeper->multi(requests); zookeeper->multi(requests);
{ {
auto lock = lockForAlter(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); auto table_lock_holder = lockForShare(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations);
auto alter_lock_holder = lockForAlter(getSettings()->lock_acquire_timeout_for_background_operations);
LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally.");
auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry);
@ -4427,7 +4428,7 @@ PartitionBlockNumbersHolder StorageReplicatedMergeTree::allocateBlockNumbersInAf
void StorageReplicatedMergeTree::alter( void StorageReplicatedMergeTree::alter(
const AlterCommands & commands, ContextPtr query_context, TableLockHolder & table_lock_holder) const AlterCommands & commands, ContextPtr query_context, AlterLockHolder & table_lock_holder)
{ {
assertNotReadonly(); assertNotReadonly();
@ -4632,7 +4633,7 @@ void StorageReplicatedMergeTree::alter(
} }
} }
table_lock_holder.reset(); table_lock_holder.unlock();
LOG_DEBUG(log, "Updated shared metadata nodes in ZooKeeper. Waiting for replicas to apply changes."); LOG_DEBUG(log, "Updated shared metadata nodes in ZooKeeper. Waiting for replicas to apply changes.");
waitForLogEntryToBeProcessedIfNecessary(*alter_entry, query_context, "Some replicas doesn't finish metadata alter: "); waitForLogEntryToBeProcessedIfNecessary(*alter_entry, query_context, "Some replicas doesn't finish metadata alter: ");

View File

@ -129,7 +129,7 @@ public:
const Names & deduplicate_by_columns, const Names & deduplicate_by_columns,
ContextPtr query_context) override; ContextPtr query_context) override;
void alter(const AlterCommands & commands, ContextPtr query_context, TableLockHolder & table_lock_holder) override; void alter(const AlterCommands & commands, ContextPtr query_context, AlterLockHolder & table_lock_holder) override;
void mutate(const MutationCommands & commands, ContextPtr context) override; void mutate(const MutationCommands & commands, ContextPtr context) override;
void waitMutation(const String & znode_name, size_t mutations_sync) const; void waitMutation(const String & znode_name, size_t mutations_sync) const;

View File

@ -16,8 +16,6 @@ struct TableExclusiveLockHolder
private: private:
friend class IStorage; friend class IStorage;
/// Order is important.
TableLockHolder alter_lock;
TableLockHolder drop_lock; TableLockHolder drop_lock;
}; };