mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 01:51:59 +00:00
dbms: Server: fix race conditions [#METR-14410]
This commit is contained in:
parent
dbca19b7bc
commit
7fdc23c5f2
@ -22,6 +22,12 @@ class RemoteBlockInputStream : public IProfilingBlockInputStream
|
|||||||
private:
|
private:
|
||||||
void init(const Settings * settings_)
|
void init(const Settings * settings_)
|
||||||
{
|
{
|
||||||
|
sent_query.store(false, std::memory_order_release);
|
||||||
|
finished.store(false, std::memory_order_release);
|
||||||
|
got_exception_from_replica.store(false, std::memory_order_release);
|
||||||
|
got_unknown_packet_from_replica.store(false, std::memory_order_release);
|
||||||
|
was_cancelled.store(false, std::memory_order_release);
|
||||||
|
|
||||||
if (settings_)
|
if (settings_)
|
||||||
{
|
{
|
||||||
send_settings = true;
|
send_settings = true;
|
||||||
@ -83,14 +89,13 @@ public:
|
|||||||
if (!__sync_bool_compare_and_swap(&is_cancelled, false, true))
|
if (!__sync_bool_compare_and_swap(&is_cancelled, false, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isQueryInProgress() && !hasThrownException())
|
if (hasNoQueryInProgress() || hasThrownException())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tryCancel())
|
||||||
{
|
{
|
||||||
std::string addresses = parallel_replicas->dumpAddresses();
|
std::string addresses = parallel_replicas->dumpAddresses();
|
||||||
LOG_TRACE(log, "(" + addresses + ") Cancelling query");
|
LOG_TRACE(log, "(" + addresses + ") Cancelling query");
|
||||||
|
|
||||||
/// Если запрошено прервать запрос - попросим удалённые реплики тоже прервать запрос.
|
|
||||||
was_cancelled = true;
|
|
||||||
parallel_replicas->sendCancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +141,14 @@ protected:
|
|||||||
|
|
||||||
Block readImpl() override
|
Block readImpl() override
|
||||||
{
|
{
|
||||||
if (!sent_query)
|
if (!sent_query.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
createParallelReplicas();
|
createParallelReplicas();
|
||||||
setEstablishedState();
|
established = true;
|
||||||
parallel_replicas->sendQuery(query, "", stage, true);
|
parallel_replicas->sendQuery(query, "", stage, true);
|
||||||
|
established = false;
|
||||||
|
sent_query.store(true, std::memory_order_release);
|
||||||
sendExternalTables();
|
sendExternalTables();
|
||||||
setSentQueryState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@ -158,14 +164,14 @@ protected:
|
|||||||
break; /// Если блок пустой - получим другие пакеты до EndOfStream.
|
break; /// Если блок пустой - получим другие пакеты до EndOfStream.
|
||||||
|
|
||||||
case Protocol::Server::Exception:
|
case Protocol::Server::Exception:
|
||||||
got_exception_from_replica = true;
|
got_exception_from_replica.store(true, std::memory_order_release);
|
||||||
packet.exception->rethrow();
|
packet.exception->rethrow();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Protocol::Server::EndOfStream:
|
case Protocol::Server::EndOfStream:
|
||||||
if (!parallel_replicas->hasActiveReplicas())
|
if (!parallel_replicas->hasActiveReplicas())
|
||||||
{
|
{
|
||||||
finished = true;
|
finished.store(true, std::memory_order_release);
|
||||||
return Block();
|
return Block();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -197,7 +203,7 @@ protected:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
got_unknown_packet_from_replica = true;
|
got_unknown_packet_from_replica.store(true, std::memory_order_release);
|
||||||
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,13 +226,10 @@ protected:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/// Отправим просьбу прервать выполнение запроса, если ещё не отправляли.
|
/// Отправим просьбу прервать выполнение запроса, если ещё не отправляли.
|
||||||
if (!was_cancelled)
|
if (tryCancel())
|
||||||
{
|
{
|
||||||
std::string addresses = parallel_replicas->dumpAddresses();
|
std::string addresses = parallel_replicas->dumpAddresses();
|
||||||
LOG_TRACE(log, "(" + addresses + ") Cancelling query because enough data has been read");
|
LOG_TRACE(log, "(" + addresses + ") Cancelling query because enough data has been read");
|
||||||
|
|
||||||
was_cancelled = true;
|
|
||||||
parallel_replicas->sendCancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Получим оставшиеся пакеты, чтобы не было рассинхронизации в соединениях с репликами.
|
/// Получим оставшиеся пакеты, чтобы не было рассинхронизации в соединениях с репликами.
|
||||||
@ -234,16 +237,16 @@ protected:
|
|||||||
switch (packet.type)
|
switch (packet.type)
|
||||||
{
|
{
|
||||||
case Protocol::Server::EndOfStream:
|
case Protocol::Server::EndOfStream:
|
||||||
finished = true;
|
finished.store(true, std::memory_order_release);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Protocol::Server::Exception:
|
case Protocol::Server::Exception:
|
||||||
got_exception_from_replica = true;
|
got_exception_from_replica.store(true, std::memory_order_release);
|
||||||
packet.exception->rethrow();
|
packet.exception->rethrow();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
got_unknown_packet_from_replica = true;
|
got_unknown_packet_from_replica.store(true, std::memory_order_release);
|
||||||
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,19 +264,19 @@ protected:
|
|||||||
/// Возвращает true, если запрос отправлен, а ещё не выполнен.
|
/// Возвращает true, если запрос отправлен, а ещё не выполнен.
|
||||||
bool isQueryInProgress() const
|
bool isQueryInProgress() const
|
||||||
{
|
{
|
||||||
return sent_query && !finished && !was_cancelled;
|
return sent_query.load(std::memory_order_acquire) && !finished.load(std::memory_order_acquire) && !was_cancelled.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Возвращает true, если никакой запрос не отправлен или один запрос уже выполнен.
|
/// Возвращает true, если никакой запрос не отправлен или один запрос уже выполнен.
|
||||||
bool hasNoQueryInProgress() const
|
bool hasNoQueryInProgress() const
|
||||||
{
|
{
|
||||||
return !sent_query || finished;
|
return !sent_query.load(std::memory_order_acquire) || finished.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Возвращает true, если исключение было выкинуто.
|
/// Возвращает true, если исключение было выкинуто.
|
||||||
bool hasThrownException() const
|
bool hasThrownException() const
|
||||||
{
|
{
|
||||||
return got_exception_from_replica || got_unknown_packet_from_replica;
|
return got_exception_from_replica.load(std::memory_order_acquire) || got_unknown_packet_from_replica.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -284,17 +287,18 @@ private:
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Перейти в состояние established (т.е. соединения с репликами установлены).
|
/// Отправить запрос на отмену всех соединений к репликам, если такой запрос ещё не был отправлен.
|
||||||
void NO_INLINE setEstablishedState()
|
bool tryCancel()
|
||||||
{
|
{
|
||||||
established = true;
|
bool old_val = false;
|
||||||
}
|
bool new_val = true;
|
||||||
|
if (was_cancelled.compare_exchange_strong(old_val, new_val, std::memory_order_release, std::memory_order_relaxed))
|
||||||
/// Перейти в состояние sent_query (т.е. запрос отправлен).
|
{
|
||||||
void NO_INLINE setSentQueryState()
|
parallel_replicas->sendCancel();
|
||||||
{
|
return true;
|
||||||
established = false;
|
}
|
||||||
sent_query = true;
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -315,33 +319,33 @@ private:
|
|||||||
Context context;
|
Context context;
|
||||||
|
|
||||||
/// Установили соединения с репликами, но ещё не отправили запрос.
|
/// Установили соединения с репликами, но ещё не отправили запрос.
|
||||||
bool established = false;
|
volatile bool established = false;
|
||||||
|
|
||||||
/// Отправили запрос (это делается перед получением первого блока).
|
/// Отправили запрос (это делается перед получением первого блока).
|
||||||
bool sent_query = false;
|
std::atomic<bool> sent_query;
|
||||||
|
|
||||||
/** Получили все данные от всех реплик, до пакета EndOfStream.
|
/** Получили все данные от всех реплик, до пакета EndOfStream.
|
||||||
* Если при уничтожении объекта, ещё не все данные считаны,
|
* Если при уничтожении объекта, ещё не все данные считаны,
|
||||||
* то для того, чтобы не было рассинхронизации, на реплики отправляются просьбы прервать выполнение запроса,
|
* то для того, чтобы не было рассинхронизации, на реплики отправляются просьбы прервать выполнение запроса,
|
||||||
* и после этого считываются все пакеты до EndOfStream.
|
* и после этого считываются все пакеты до EndOfStream.
|
||||||
*/
|
*/
|
||||||
bool finished = false;
|
std::atomic<bool> finished;
|
||||||
|
|
||||||
/** На каждую реплику была отправлена просьба прервать выполнение запроса, так как данные больше не нужны.
|
/** На каждую реплику была отправлена просьба прервать выполнение запроса, так как данные больше не нужны.
|
||||||
* Это может быть из-за того, что данных достаточно (например, при использовании LIMIT),
|
* Это может быть из-за того, что данных достаточно (например, при использовании LIMIT),
|
||||||
* или если на стороне клиента произошло исключение.
|
* или если на стороне клиента произошло исключение.
|
||||||
*/
|
*/
|
||||||
bool was_cancelled = false;
|
std::atomic<bool> was_cancelled;
|
||||||
|
|
||||||
/** С одной репилки было получено исключение. В этом случае получать больше пакетов или
|
/** С одной репилки было получено исключение. В этом случае получать больше пакетов или
|
||||||
* просить прервать запрос на этой реплике не нужно.
|
* просить прервать запрос на этой реплике не нужно.
|
||||||
*/
|
*/
|
||||||
bool got_exception_from_replica = false;
|
std::atomic<bool> got_exception_from_replica;
|
||||||
|
|
||||||
/** С одной реплики был получен неизвестный пакет. В этом случае получать больше пакетов или
|
/** С одной реплики был получен неизвестный пакет. В этом случае получать больше пакетов или
|
||||||
* просить прервать запрос на этой реплике не нужно.
|
* просить прервать запрос на этой реплике не нужно.
|
||||||
*/
|
*/
|
||||||
bool got_unknown_packet_from_replica = false;
|
std::atomic<bool> got_unknown_packet_from_replica;
|
||||||
|
|
||||||
Logger * log = &Logger::get("RemoteBlockInputStream");
|
Logger * log = &Logger::get("RemoteBlockInputStream");
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user