2020-10-22 15:08:00 +00:00
|
|
|
#include <DataTypes/DataTypeString.h>
|
2020-04-05 12:18:51 +00:00
|
|
|
#include <Databases/DatabaseReplicated.h>
|
|
|
|
#include <IO/ReadBufferFromFile.h>
|
2020-05-11 12:55:17 +00:00
|
|
|
#include <IO/ReadBufferFromString.h>
|
2020-04-05 12:18:51 +00:00
|
|
|
#include <IO/ReadHelpers.h>
|
|
|
|
#include <IO/WriteBufferFromFile.h>
|
|
|
|
#include <IO/WriteHelpers.h>
|
|
|
|
#include <Interpreters/Context.h>
|
2020-05-11 12:55:17 +00:00
|
|
|
#include <Interpreters/executeQuery.h>
|
2020-04-05 12:18:51 +00:00
|
|
|
#include <Parsers/queryToString.h>
|
2020-05-12 13:35:05 +00:00
|
|
|
#include <Common/Exception.h>
|
2020-10-22 15:08:00 +00:00
|
|
|
#include <Common/Stopwatch.h>
|
2020-04-05 12:18:51 +00:00
|
|
|
#include <Common/ZooKeeper/KeeperException.h>
|
|
|
|
#include <Common/ZooKeeper/Types.h>
|
|
|
|
#include <Common/ZooKeeper/ZooKeeper.h>
|
2020-11-27 14:04:03 +00:00
|
|
|
#include <Databases/DatabaseReplicatedWorker.h>
|
2020-11-05 09:52:23 +00:00
|
|
|
#include <Interpreters/DDLTask.h>
|
|
|
|
#include <Interpreters/executeDDLQueryOnCluster.h>
|
2020-11-13 18:35:45 +00:00
|
|
|
#include <Interpreters/Cluster.h>
|
|
|
|
#include <common/getFQDNOrHostName.h>
|
2020-11-05 09:52:23 +00:00
|
|
|
#include <Parsers/ASTAlterQuery.h>
|
2020-11-27 14:04:03 +00:00
|
|
|
#include <Parsers/ParserCreateQuery.h>
|
|
|
|
#include <Parsers/parseQuery.h>
|
|
|
|
#include <Interpreters/InterpreterCreateQuery.h>
|
|
|
|
#include <Parsers/formatAST.h>
|
2020-04-05 12:18:51 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int NO_ZOOKEEPER;
|
2020-06-22 14:19:26 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
2020-06-24 12:45:42 +00:00
|
|
|
extern const int BAD_ARGUMENTS;
|
2020-11-13 18:35:45 +00:00
|
|
|
extern const int REPLICA_IS_ALREADY_EXIST;
|
2020-11-19 10:34:45 +00:00
|
|
|
extern const int DATABASE_REPLICATION_FAILED;
|
2020-11-20 16:06:27 +00:00
|
|
|
extern const int UNKNOWN_DATABASE;
|
2020-11-29 11:45:32 +00:00
|
|
|
extern const int NOT_IMPLEMENTED;
|
2021-02-04 19:41:44 +00:00
|
|
|
extern const int INCORRECT_QUERY;
|
2020-04-05 12:18:51 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
zkutil::ZooKeeperPtr DatabaseReplicated::getZooKeeper() const
|
2020-04-05 12:18:51 +00:00
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
return global_context.getZooKeeper();
|
2020-04-05 12:18:51 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 19:29:47 +00:00
|
|
|
static inline String getHostID(const Context & global_context, const UUID & db_uuid)
|
2020-04-05 12:18:51 +00:00
|
|
|
{
|
2021-02-01 19:29:47 +00:00
|
|
|
return Cluster::Address::toString(getFQDNOrHostName(), global_context.getTCPPort()) + ':' + toString(db_uuid);
|
2020-04-05 12:18:51 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
|
2020-11-05 09:52:23 +00:00
|
|
|
DatabaseReplicated::~DatabaseReplicated() = default;
|
2020-04-05 12:18:51 +00:00
|
|
|
|
|
|
|
DatabaseReplicated::DatabaseReplicated(
|
|
|
|
const String & name_,
|
|
|
|
const String & metadata_path_,
|
2020-10-20 16:14:54 +00:00
|
|
|
UUID uuid,
|
2020-04-05 12:18:51 +00:00
|
|
|
const String & zookeeper_path_,
|
2020-10-27 09:19:45 +00:00
|
|
|
const String & shard_name_,
|
2020-04-05 12:18:51 +00:00
|
|
|
const String & replica_name_,
|
2020-11-27 14:04:03 +00:00
|
|
|
const Context & context_)
|
2020-10-20 16:14:54 +00:00
|
|
|
: DatabaseAtomic(name_, metadata_path_, uuid, "DatabaseReplicated (" + name_ + ")", context_)
|
2020-04-05 12:18:51 +00:00
|
|
|
, zookeeper_path(zookeeper_path_)
|
2020-10-27 09:19:45 +00:00
|
|
|
, shard_name(shard_name_)
|
2020-04-05 12:18:51 +00:00
|
|
|
, replica_name(replica_name_)
|
|
|
|
{
|
2020-10-27 09:19:45 +00:00
|
|
|
if (zookeeper_path.empty() || shard_name.empty() || replica_name.empty())
|
2020-11-13 18:35:45 +00:00
|
|
|
throw Exception("ZooKeeper path, shard and replica names must be non-empty", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
if (shard_name.find('/') != std::string::npos || replica_name.find('/') != std::string::npos)
|
|
|
|
throw Exception("Shard and replica names should not contain '/'", ErrorCodes::BAD_ARGUMENTS);
|
2020-06-24 12:45:42 +00:00
|
|
|
|
|
|
|
if (zookeeper_path.back() == '/')
|
2020-04-05 12:18:51 +00:00
|
|
|
zookeeper_path.resize(zookeeper_path.size() - 1);
|
2020-11-13 18:35:45 +00:00
|
|
|
|
2020-10-22 15:08:00 +00:00
|
|
|
/// If zookeeper chroot prefix is used, path should start with '/', because chroot concatenates without it.
|
2020-06-24 12:45:42 +00:00
|
|
|
if (zookeeper_path.front() != '/')
|
2020-04-05 12:18:51 +00:00
|
|
|
zookeeper_path = "/" + zookeeper_path;
|
2020-05-01 13:16:02 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
if (!context_.hasZooKeeper())
|
2020-04-05 12:18:51 +00:00
|
|
|
{
|
2020-10-22 15:08:00 +00:00
|
|
|
throw Exception("Can't create replicated database without ZooKeeper", ErrorCodes::NO_ZOOKEEPER);
|
2020-04-30 16:15:27 +00:00
|
|
|
}
|
2020-11-13 18:35:45 +00:00
|
|
|
//FIXME it will fail on startup if zk is not available
|
|
|
|
|
|
|
|
auto current_zookeeper = global_context.getZooKeeper();
|
2020-04-05 12:18:51 +00:00
|
|
|
|
2020-10-22 15:08:00 +00:00
|
|
|
if (!current_zookeeper->exists(zookeeper_path))
|
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
/// Create new database, multiple nodes can execute it concurrently
|
|
|
|
createDatabaseNodesInZooKeeper(current_zookeeper);
|
2020-10-22 15:08:00 +00:00
|
|
|
}
|
2020-10-27 09:19:45 +00:00
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
replica_path = zookeeper_path + "/replicas/" + shard_name + "/" + replica_name;
|
2020-06-22 14:19:26 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
String replica_host_id;
|
|
|
|
if (current_zookeeper->tryGet(replica_path, replica_host_id))
|
|
|
|
{
|
2021-02-01 19:29:47 +00:00
|
|
|
String host_id = getHostID(global_context, db_uuid);
|
2020-11-13 18:35:45 +00:00
|
|
|
if (replica_host_id != host_id)
|
|
|
|
throw Exception(ErrorCodes::REPLICA_IS_ALREADY_EXIST,
|
|
|
|
"Replica {} of shard {} of replicated database at {} already exists. Replica host ID: '{}', current host ID: '{}'",
|
|
|
|
replica_name, shard_name, zookeeper_path, replica_host_id, host_id);
|
2020-05-24 17:13:53 +00:00
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
log_entry_to_execute = parse<UInt32>(current_zookeeper->get(replica_path + "/log_ptr"));
|
2020-05-13 17:00:47 +00:00
|
|
|
}
|
2020-10-27 09:19:45 +00:00
|
|
|
else
|
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
/// Throws if replica with the same name was created concurrently
|
|
|
|
createReplicaNodesInZooKeeper(current_zookeeper);
|
2020-10-27 09:19:45 +00:00
|
|
|
}
|
2020-11-13 18:35:45 +00:00
|
|
|
}
|
2020-05-24 17:13:53 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
bool DatabaseReplicated::createDatabaseNodesInZooKeeper(const zkutil::ZooKeeperPtr & current_zookeeper)
|
|
|
|
{
|
|
|
|
current_zookeeper->createAncestors(zookeeper_path);
|
2020-06-20 15:39:58 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
Coordination::Requests ops;
|
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", zkutil::CreateMode::Persistent));
|
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log", "", zkutil::CreateMode::Persistent));
|
2020-11-19 10:34:45 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/replicas", "", zkutil::CreateMode::Persistent));
|
2020-11-20 16:06:27 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/counter", "", zkutil::CreateMode::Persistent));
|
2020-11-27 14:04:03 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/counter/cnt-", "", zkutil::CreateMode::Persistent));
|
|
|
|
ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path + "/counter/cnt-", -1));
|
2020-11-19 10:34:45 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/metadata", "", zkutil::CreateMode::Persistent));
|
2020-11-27 14:04:03 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/max_log_ptr", "1", zkutil::CreateMode::Persistent));
|
2021-02-04 19:41:44 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/logs_to_keep", "1000", zkutil::CreateMode::Persistent));
|
2020-11-13 18:35:45 +00:00
|
|
|
|
|
|
|
Coordination::Responses responses;
|
|
|
|
auto res = current_zookeeper->tryMulti(ops, responses);
|
|
|
|
if (res == Coordination::Error::ZOK)
|
|
|
|
return true;
|
|
|
|
if (res == Coordination::Error::ZNODEEXISTS)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
zkutil::KeeperMultiException::check(res, ops, responses);
|
|
|
|
assert(false);
|
2020-11-27 14:04:03 +00:00
|
|
|
__builtin_unreachable();
|
2020-05-11 12:55:17 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
void DatabaseReplicated::createReplicaNodesInZooKeeper(const zkutil::ZooKeeperPtr & current_zookeeper)
|
2020-10-22 15:08:00 +00:00
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
current_zookeeper->createAncestors(replica_path);
|
2020-05-24 17:13:53 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
/// When creating new replica, use latest snapshot version as initial value of log_pointer
|
2020-11-27 14:04:03 +00:00
|
|
|
//log_entry_to_execute = 0; //FIXME
|
2020-11-13 18:35:45 +00:00
|
|
|
|
|
|
|
/// Write host name to replica_path, it will protect from multiple replicas with the same name
|
2021-02-01 19:29:47 +00:00
|
|
|
auto host_id = getHostID(global_context, db_uuid);
|
2020-11-13 18:35:45 +00:00
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
/// On replica creation add empty entry to log. Can be used to trigger some actions on other replicas (e.g. update cluster info).
|
|
|
|
DDLLogEntry entry;
|
|
|
|
entry.hosts = {};
|
|
|
|
entry.query = {};
|
|
|
|
entry.initiator = {};
|
|
|
|
|
2020-11-20 16:06:27 +00:00
|
|
|
String query_path_prefix = zookeeper_path + "/log/query-";
|
|
|
|
String counter_prefix = zookeeper_path + "/counter/cnt-";
|
|
|
|
String counter_path = current_zookeeper->create(counter_prefix, "", zkutil::CreateMode::EphemeralSequential);
|
|
|
|
String query_path = query_path_prefix + counter_path.substr(counter_prefix.size());
|
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
Coordination::Requests ops;
|
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(replica_path, host_id, zkutil::CreateMode::Persistent));
|
2020-11-27 14:04:03 +00:00
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/log_ptr", "0", zkutil::CreateMode::Persistent));
|
|
|
|
ops.emplace_back(zkutil::makeCreateRequest(query_path, entry.toString(), zkutil::CreateMode::Persistent));
|
2020-11-20 16:06:27 +00:00
|
|
|
ops.emplace_back(zkutil::makeRemoveRequest(counter_path, -1));
|
2020-11-13 18:35:45 +00:00
|
|
|
current_zookeeper->multi(ops);
|
2020-06-20 15:39:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
void DatabaseReplicated::loadStoredObjects(Context & context, bool has_force_restore_data_flag, bool force_attach)
|
2020-10-27 09:19:45 +00:00
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
DatabaseAtomic::loadStoredObjects(context, has_force_restore_data_flag, force_attach);
|
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
ddl_worker = std::make_unique<DatabaseReplicatedDDLWorker>(this, global_context);
|
2020-12-01 17:20:42 +00:00
|
|
|
ddl_worker->startup();
|
2020-10-27 09:19:45 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
void DatabaseReplicated::onUnexpectedLogEntry(const String & entry_name, const ZooKeeperPtr & zookeeper)
|
|
|
|
{
|
|
|
|
/// We cannot execute next entry of replication log. Possible reasons:
|
|
|
|
/// 1. Replica is staled, some entries were removed by log cleanup process.
|
|
|
|
/// In this case we should recover replica from the last snapshot.
|
|
|
|
/// 2. Replication log is broken due to manual operations with ZooKeeper or logical error.
|
|
|
|
/// In this case we just stop replication without any attempts to recover it automatically,
|
|
|
|
/// because such attempts may lead to unexpected data removal.
|
|
|
|
|
|
|
|
constexpr const char * name = "query-";
|
|
|
|
if (!startsWith(entry_name, name))
|
|
|
|
throw Exception(ErrorCodes::DATABASE_REPLICATION_FAILED, "Unexpected entry in replication log: {}", entry_name);
|
|
|
|
|
|
|
|
UInt32 entry_number;
|
|
|
|
if (!tryParse(entry_number, entry_name.substr(strlen(name))))
|
|
|
|
throw Exception(ErrorCodes::DATABASE_REPLICATION_FAILED, "Cannot parse number of replication log entry {}", entry_name);
|
|
|
|
|
|
|
|
if (entry_number < log_entry_to_execute)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Entry {} already executed, current pointer is {}", entry_number, log_entry_to_execute);
|
|
|
|
|
2020-11-20 16:06:27 +00:00
|
|
|
/// Entry name is valid. Let's get min log pointer to check if replica is staled.
|
2021-02-04 19:41:44 +00:00
|
|
|
UInt32 min_snapshot = parse<UInt32>(zookeeper->get(zookeeper_path + "/min_log_ptr")); // FIXME
|
2020-11-19 10:34:45 +00:00
|
|
|
|
|
|
|
if (log_entry_to_execute < min_snapshot)
|
|
|
|
{
|
2020-11-20 16:06:27 +00:00
|
|
|
recoverLostReplica(zookeeper, 0); //FIXME log_pointer
|
2020-11-19 10:34:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot recover replica, probably it's a bug. "
|
2020-11-20 16:06:27 +00:00
|
|
|
"Got log entry '{}' when expected entry number {}");
|
2020-11-19 10:34:45 +00:00
|
|
|
}
|
2020-11-13 18:35:45 +00:00
|
|
|
|
2020-05-11 12:55:17 +00:00
|
|
|
|
2021-02-04 19:41:44 +00:00
|
|
|
BlockIO DatabaseReplicated::propose(const ASTPtr & query, const Context & query_context)
|
2020-10-22 15:08:00 +00:00
|
|
|
{
|
2021-02-04 19:41:44 +00:00
|
|
|
if (query_context.getClientInfo().query_kind != ClientInfo::QueryKind::INITIAL_QUERY)
|
|
|
|
throw Exception(ErrorCodes::INCORRECT_QUERY, "It's not initial query. ON CLUSTER is not allowed for Replicated database.");
|
|
|
|
|
2020-11-05 09:52:23 +00:00
|
|
|
if (const auto * query_alter = query->as<ASTAlterQuery>())
|
2020-06-27 13:39:41 +00:00
|
|
|
{
|
2021-01-18 14:09:39 +00:00
|
|
|
for (const auto & command : query_alter->command_list->children)
|
2020-11-05 09:52:23 +00:00
|
|
|
{
|
2021-01-18 14:09:39 +00:00
|
|
|
if (!isSupportedAlterType(command->as<ASTAlterCommand&>().type))
|
2020-11-05 09:52:23 +00:00
|
|
|
throw Exception("Unsupported type of ALTER query", ErrorCodes::NOT_IMPLEMENTED);
|
|
|
|
}
|
2020-06-27 13:39:41 +00:00
|
|
|
}
|
2020-05-27 18:33:37 +00:00
|
|
|
|
2020-11-05 09:52:23 +00:00
|
|
|
LOG_DEBUG(log, "Proposing query: {}", queryToString(query));
|
2020-05-13 17:00:47 +00:00
|
|
|
|
2021-02-01 19:29:47 +00:00
|
|
|
/// TODO maybe write current settings to log entry?
|
2020-11-05 09:52:23 +00:00
|
|
|
DDLLogEntry entry;
|
|
|
|
entry.query = queryToString(query);
|
|
|
|
entry.initiator = ddl_worker->getCommonHostID();
|
2021-02-04 19:41:44 +00:00
|
|
|
String node_path = ddl_worker->tryEnqueueAndExecuteEntry(entry, query_context);
|
2020-07-04 16:32:23 +00:00
|
|
|
|
2020-11-05 09:52:23 +00:00
|
|
|
BlockIO io;
|
2021-02-04 19:41:44 +00:00
|
|
|
if (query_context.getSettingsRef().distributed_ddl_task_timeout == 0)
|
2020-11-05 09:52:23 +00:00
|
|
|
return io;
|
2020-07-04 16:32:23 +00:00
|
|
|
|
2020-11-13 18:35:45 +00:00
|
|
|
//FIXME need list of all replicas, we can obtain it from zk
|
2020-11-05 09:52:23 +00:00
|
|
|
Strings hosts_to_wait;
|
2020-11-29 11:45:32 +00:00
|
|
|
hosts_to_wait.emplace_back(getFullReplicaName());
|
2021-02-04 19:41:44 +00:00
|
|
|
auto stream = std::make_shared<DDLQueryStatusInputStream>(node_path, entry, query_context, hosts_to_wait);
|
2020-11-05 09:52:23 +00:00
|
|
|
io.in = std::move(stream);
|
|
|
|
return io;
|
2020-07-04 16:32:23 +00:00
|
|
|
}
|
|
|
|
|
2020-11-05 09:52:23 +00:00
|
|
|
|
2020-11-29 11:45:32 +00:00
|
|
|
void DatabaseReplicated::recoverLostReplica(const ZooKeeperPtr & current_zookeeper, UInt32 from_snapshot)
|
2020-10-22 15:08:00 +00:00
|
|
|
{
|
2020-11-27 14:04:03 +00:00
|
|
|
LOG_WARNING(log, "Will recover replica");
|
2020-05-24 17:13:53 +00:00
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
//FIXME drop old tables
|
2020-06-20 15:39:58 +00:00
|
|
|
|
2020-11-20 16:06:27 +00:00
|
|
|
String snapshot_metadata_path = zookeeper_path + "/metadata";
|
2020-11-19 10:34:45 +00:00
|
|
|
Strings tables_in_snapshot = current_zookeeper->getChildren(snapshot_metadata_path);
|
|
|
|
snapshot_metadata_path += '/';
|
2020-11-27 14:04:03 +00:00
|
|
|
from_snapshot = parse<UInt32>(current_zookeeper->get(zookeeper_path + "/max_log_ptr"));
|
2020-06-24 12:45:42 +00:00
|
|
|
|
2020-11-19 10:34:45 +00:00
|
|
|
for (const auto & table_name : tables_in_snapshot)
|
2020-10-22 15:08:00 +00:00
|
|
|
{
|
2020-11-20 16:06:27 +00:00
|
|
|
//FIXME It's not atomic. We need multiget here (available since ZooKeeper 3.6.0).
|
2020-11-27 14:04:03 +00:00
|
|
|
String query_text = current_zookeeper->get(snapshot_metadata_path + table_name);
|
|
|
|
auto query_ast = parseQueryFromMetadataInZooKeeper(table_name, query_text);
|
2020-05-24 17:13:53 +00:00
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
Context query_context = global_context;
|
|
|
|
query_context.makeQueryContext();
|
|
|
|
query_context.getClientInfo().query_kind = ClientInfo::QueryKind::REPLICATED_LOG_QUERY;
|
|
|
|
query_context.setCurrentDatabase(database_name);
|
|
|
|
query_context.setCurrentQueryId(""); // generate random query_id
|
2020-07-04 16:32:23 +00:00
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
//FIXME
|
|
|
|
DatabaseCatalog::instance().waitTableFinallyDropped(query_ast->as<ASTCreateQuery>()->uuid);
|
2020-06-22 14:19:26 +00:00
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
LOG_INFO(log, "Executing {}", serializeAST(*query_ast));
|
|
|
|
InterpreterCreateQuery(query_ast, query_context).execute();
|
2020-11-19 10:34:45 +00:00
|
|
|
}
|
2020-07-04 16:32:23 +00:00
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
current_zookeeper->set(replica_path + "/log_ptr", toString(from_snapshot));
|
2020-06-20 15:39:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 14:04:03 +00:00
|
|
|
ASTPtr DatabaseReplicated::parseQueryFromMetadataInZooKeeper(const String & node_name, const String & query)
|
|
|
|
{
|
|
|
|
ParserCreateQuery parser;
|
|
|
|
String description = "in ZooKeeper " + zookeeper_path + "/metadata/" + node_name;
|
|
|
|
auto ast = parseQuery(parser, query, description, 0, global_context.getSettingsRef().max_parser_depth);
|
|
|
|
|
|
|
|
auto & create = ast->as<ASTCreateQuery &>();
|
|
|
|
if (create.uuid == UUIDHelpers::Nil || create.table != TABLE_WITH_UUID_NAME_PLACEHOLDER || ! create.database.empty())
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Got unexpected query from {}: {}", node_name, query);
|
|
|
|
|
|
|
|
create.database = getDatabaseName();
|
|
|
|
create.table = unescapeForFileName(node_name);
|
|
|
|
create.attach = false;
|
|
|
|
|
|
|
|
return ast;
|
|
|
|
}
|
|
|
|
|
2020-06-20 15:39:58 +00:00
|
|
|
void DatabaseReplicated::drop(const Context & context_)
|
|
|
|
{
|
2020-11-13 18:35:45 +00:00
|
|
|
auto current_zookeeper = getZooKeeper();
|
2020-11-27 14:04:03 +00:00
|
|
|
current_zookeeper->set(replica_path, "DROPPED");
|
2020-06-20 15:39:58 +00:00
|
|
|
DatabaseAtomic::drop(context_);
|
2021-02-04 19:41:44 +00:00
|
|
|
current_zookeeper->tryRemoveRecursive(replica_path);
|
2020-05-24 17:13:53 +00:00
|
|
|
}
|
|
|
|
|
2021-02-04 19:41:44 +00:00
|
|
|
void DatabaseReplicated::stopReplication()
|
2020-11-13 18:35:45 +00:00
|
|
|
{
|
|
|
|
if (ddl_worker)
|
|
|
|
ddl_worker->shutdown();
|
2021-02-04 19:41:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseReplicated::shutdown()
|
|
|
|
{
|
|
|
|
stopReplication();
|
|
|
|
ddl_worker = nullptr;
|
2020-11-13 18:35:45 +00:00
|
|
|
DatabaseAtomic::shutdown();
|
|
|
|
}
|
|
|
|
|
2020-11-29 11:45:32 +00:00
|
|
|
|
|
|
|
void DatabaseReplicated::dropTable(const Context & context, const String & table_name, bool no_delay)
|
|
|
|
{
|
|
|
|
auto txn = context.getMetadataTransaction();
|
|
|
|
//assert(!ddl_worker->isCurrentlyActive() || txn /*|| called from DROP DATABASE */);
|
|
|
|
if (txn && txn->is_initial_query)
|
|
|
|
{
|
|
|
|
String metadata_zk_path = zookeeper_path + "/metadata/" + escapeForFileName(table_name);
|
|
|
|
txn->ops.emplace_back(zkutil::makeRemoveRequest(metadata_zk_path, -1));
|
|
|
|
}
|
|
|
|
DatabaseAtomic::dropTable(context, table_name, no_delay);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseReplicated::renameTable(const Context & context, const String & table_name, IDatabase & to_database,
|
|
|
|
const String & to_table_name, bool exchange, bool dictionary)
|
|
|
|
{
|
|
|
|
auto txn = context.getMetadataTransaction();
|
|
|
|
assert(txn);
|
|
|
|
|
|
|
|
if (txn->is_initial_query)
|
|
|
|
{
|
2021-02-04 19:41:44 +00:00
|
|
|
if (!isTableExist(table_name, context))
|
|
|
|
throw Exception(ErrorCodes::UNKNOWN_TABLE, "Table {} does not exist", table_name);
|
|
|
|
if (exchange && !to_database.isTableExist(to_table_name, context))
|
|
|
|
throw Exception(ErrorCodes::UNKNOWN_TABLE, "Table {} does not exist", to_table_name);
|
|
|
|
|
2020-11-29 11:45:32 +00:00
|
|
|
String statement;
|
|
|
|
String statement_to;
|
|
|
|
{
|
2021-02-04 19:41:44 +00:00
|
|
|
/// NOTE It's not atomic (however, we have only one thread)
|
2020-11-29 11:45:32 +00:00
|
|
|
ReadBufferFromFile in(getObjectMetadataPath(table_name), 4096);
|
|
|
|
readStringUntilEOF(statement, in);
|
|
|
|
if (exchange)
|
|
|
|
{
|
|
|
|
ReadBufferFromFile in_to(to_database.getObjectMetadataPath(to_table_name), 4096);
|
|
|
|
readStringUntilEOF(statement_to, in_to);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String metadata_zk_path = txn->zookeeper_path + "/metadata/" + escapeForFileName(table_name);
|
|
|
|
String metadata_zk_path_to = txn->zookeeper_path + "/metadata/" + escapeForFileName(to_table_name);
|
|
|
|
txn->ops.emplace_back(zkutil::makeRemoveRequest(metadata_zk_path, -1));
|
|
|
|
if (exchange)
|
|
|
|
{
|
|
|
|
txn->ops.emplace_back(zkutil::makeRemoveRequest(metadata_zk_path_to, -1));
|
|
|
|
txn->ops.emplace_back(zkutil::makeCreateRequest(metadata_zk_path, statement_to, zkutil::CreateMode::Persistent));
|
|
|
|
}
|
|
|
|
txn->ops.emplace_back(zkutil::makeCreateRequest(metadata_zk_path_to, statement, zkutil::CreateMode::Persistent));
|
|
|
|
}
|
|
|
|
|
|
|
|
DatabaseAtomic::renameTable(context, table_name, to_database, to_table_name, exchange, dictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseReplicated::commitCreateTable(const ASTCreateQuery & query, const StoragePtr & table,
|
|
|
|
const String & table_metadata_tmp_path, const String & table_metadata_path,
|
|
|
|
const Context & query_context)
|
|
|
|
{
|
|
|
|
auto txn = query_context.getMetadataTransaction();
|
|
|
|
assert(!ddl_worker->isCurrentlyActive() || txn);
|
|
|
|
if (txn && txn->is_initial_query)
|
|
|
|
{
|
|
|
|
String metadata_zk_path = txn->zookeeper_path + "/metadata/" + escapeForFileName(query.table);
|
|
|
|
String statement = getObjectDefinitionFromCreateQuery(query.clone());
|
|
|
|
/// zk::multi(...) will throw if `metadata_zk_path` exists
|
|
|
|
txn->ops.emplace_back(zkutil::makeCreateRequest(metadata_zk_path, statement, zkutil::CreateMode::Persistent));
|
|
|
|
}
|
|
|
|
DatabaseAtomic::commitCreateTable(query, table, table_metadata_tmp_path, table_metadata_path, query_context);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseReplicated::commitAlterTable(const StorageID & table_id,
|
|
|
|
const String & table_metadata_tmp_path, const String & table_metadata_path,
|
|
|
|
const String & statement, const Context & query_context)
|
|
|
|
{
|
|
|
|
auto txn = query_context.getMetadataTransaction();
|
|
|
|
if (txn && txn->is_initial_query)
|
|
|
|
{
|
|
|
|
String metadata_zk_path = txn->zookeeper_path + "/metadata/" + escapeForFileName(table_id.table_name);
|
|
|
|
txn->ops.emplace_back(zkutil::makeSetRequest(metadata_zk_path, statement, -1));
|
|
|
|
}
|
|
|
|
DatabaseAtomic::commitAlterTable(table_id, table_metadata_tmp_path, table_metadata_path, statement, query_context);
|
|
|
|
}
|
|
|
|
|
2020-04-05 12:18:51 +00:00
|
|
|
}
|