2015-10-05 00:33:43 +00:00
# include <ext/range.hpp>
2015-10-12 07:05:54 +00:00
# include <DB/Core/FieldVisitors.h>
2015-04-11 03:10:23 +00:00
# include <DB/Storages/ColumnsDescription.h>
2014-03-21 13:42:14 +00:00
# include <DB/Storages/StorageReplicatedMergeTree.h>
2014-04-02 10:10:37 +00:00
# include <DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h>
2014-04-02 13:45:39 +00:00
# include <DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h>
2015-09-11 02:13:59 +00:00
# include <DB/Storages/MergeTree/ReplicatedMergeTreeQuorumEntry.h>
2014-07-22 13:49:52 +00:00
# include <DB/Storages/MergeTree/MergeTreePartChecker.h>
2015-04-16 06:12:35 +00:00
# include <DB/Storages/MergeTree/MergeList.h>
2015-05-20 11:58:21 +00:00
# include <DB/Storages/MergeTree/MergeTreeWhereOptimizer.h>
2015-10-02 18:33:46 +00:00
# include <DB/Storages/MergeTree/ReplicatedMergeTreeAddress.h>
2014-03-22 14:44:44 +00:00
# include <DB/Parsers/formatAST.h>
# include <DB/IO/WriteBufferFromOStream.h>
# include <DB/IO/ReadBufferFromString.h>
2015-09-24 04:50:53 +00:00
# include <DB/IO/Operators.h>
2014-07-10 10:16:50 +00:00
# include <DB/Interpreters/InterpreterAlterQuery.h>
2014-07-28 09:53:57 +00:00
# include <DB/Common/VirtualColumnUtils.h>
2015-03-02 01:10:58 +00:00
# include <DB/Parsers/ASTInsertQuery.h>
2014-12-30 18:04:53 +00:00
# include <DB/DataStreams/AddingConstColumnBlockInputStream.h>
2015-10-02 21:28:19 +00:00
# include <DB/DataStreams/RemoteBlockInputStream.h>
# include <DB/DataStreams/NullBlockOutputStream.h>
# include <DB/DataStreams/copyData.h>
2015-04-16 06:12:35 +00:00
# include <DB/Common/Macros.h>
2015-06-11 00:35:36 +00:00
# include <DB/Common/formatReadable.h>
2015-09-24 18:54:21 +00:00
# include <DB/Common/setThreadName.h>
2015-04-16 06:12:35 +00:00
# include <Poco/DirectoryIterator.h>
2014-05-13 10:10:26 +00:00
# include <time.h>
2014-03-21 13:42:14 +00:00
2014-12-30 18:04:53 +00:00
2014-03-21 13:42:14 +00:00
namespace DB
{
2014-04-03 11:48:28 +00:00
2014-07-15 14:37:49 +00:00
const auto ERROR_SLEEP_MS = 1000 ;
2014-07-02 10:16:49 +00:00
const auto MERGE_SELECTING_SLEEP_MS = 5 * 1000 ;
2014-04-03 11:48:28 +00:00
2015-08-17 21:09:36 +00:00
const Int64 RESERVED_BLOCK_NUMBERS = 200 ;
2014-08-08 08:28:13 +00:00
2014-04-03 11:48:28 +00:00
2015-09-19 05:56:40 +00:00
/** Для каждого куска есть сразу три места, где он должен быть:
* 1. В о п е р а т и в к е ( RAM ) , MergeTreeData : : data_parts , all_data_parts .
* 2. В ф а й л о в о й с и с т е м е ( FS ) , д и р е к т о р и я с д а н н ы м и т а б л и ц ы .
* 3. В ZooKeeper ( ZK ) .
*
* П р и д о б а в л е н и и к у с к а , е г о н а д о д о б а в и т ь с р а з у в э т и т р и м е с т а .
* Э т о д е л а е т с я т а к :
* - [ FS ] с н а ч а л а з а п и с ы в а е м к у с о к в о в р е м е н н у ю д и р е к т о р и ю н а ф а й л о в о й с и с т е м е ;
* - [ FS ] п е р е и м е н о в ы в а е м в р е м е н н ы й к у с о к в р е з у л ь т и р у ю щ и й н а ф а й л о в о й с и с т е м е ;
* - [ RAM ] с р а з у ж е п о с л е э т о г о д о б а в л я е м е г о в data_parts , и у д а л я е м и з data_parts п о к р ы в а е м ы е и м к у с к и ;
* - [ RAM ] т а к ж е у с т а н а в л и в а е м о б ъ е к т Transaction , к о т о р ы й в с л у ч а е и с к л ю ч е н и я ( в с л е д у ю щ е м п у н к т е ) ,
* о т к а т и т и з м е н е н и я в data_parts ( и з п р е д ы д у щ е г о п у н к т а ) н а з а д ;
* - [ ZK ] з а т е м о т п р а в л я е м т р а н з а к ц и ю ( multi ) н а д о б а в л е н и е к у с к а в ZooKeeper ( и е щ ё н е к о т о р ы х д е й с т в и й ) ;
* - [ FS , ZK ] к с т а т и , у д а л е н и е п о к р ы в а е м ы х ( с т а р ы х ) к у с к о в и з ф а й л о в о й с и с т е м ы , и з ZooKeeper и и з all_data_parts
* д е л а е т с я о т л о ж е н н о , ч е р е з н е с к о л ь к о м и н у т .
*
* З д е с ь н е т н и к а к о й а т о м а р н о с т и .
* М о ж н о б ы л о б ы д о б и т ь с я а т о м а р н о с т и с п о м о щ ь ю undo / redo л о г о в и ф л а г а в DataPart , к о г д а о н п о л н о с т ь ю г о т о в .
* Н о э т о б ы л о б ы н е у д о б н о - п р и ш л о с ь б ы п и с а т ь undo / redo л о г и д л я к а ж д о г о Part - а в ZK , а э т о у в е л и ч и л о б ы и б е з т о г о б о л ь ш о е к о л и ч е с т в о в з а и м о д е й с т в и й .
*
* В м е с т о э т о г о , м ы в ы н у ж д е н ы р а б о т а т ь в с и т у а ц и и , к о г д а в л ю б о й м о м е н т в р е м е н и
* ( и з д р у г о г о п о т о к а , и л и п о с л е р е с т а р т а с е р в е р а ) м о ж е т н а б л ю д а т ь с я н е д о д е л а н н а я д о к о н ц а т р а н з а к ц и я .
* ( з а м е т и м - д л я э т о г о к у с о к д о л ж е н б ы т ь в RAM )
* И з э т и х с л у ч а е в н а и б о л е е ч а с т ы й - к о г д а к у с о к у ж е е с т ь в data_parts , н о е г о е щ ё н е т в ZooKeeper .
* Э т о т с л у ч а й н а д о о т л и ч и т ь о т с л у ч а я , к о г д а т а к а я с и т у а ц и я д о с т и г а е т с я в с л е д с т в и е к а к о г о - т о п о в р е ж д е н и я с о с т о я н и я .
*
* Д е л а е м э т о с п о м о щ ь ю п о р о г а н а в р е м я .
* Е с л и к у с о к д о с т а т о ч н о м о л о д о й , т о е г о о т с у т с т в и е в ZooKeeper б у д е м в о с п р и н и м а т ь о п т и м и с т и ч н о - к а к б у д т о о н п р о с т о н е у с п е л е щ ё т у д а д о б а в и т ь с я
* - к а к б у д т о т р а н з а к ц и я е щ ё н е в ы п о л н е н а , н о с к о р о в ы п о л н и т с я .
* А е с л и к у с о к с т а р ы й , т о е г о о т с у т с т в и е в ZooKeeper б у д е м в о с п р и н и м а т ь к а к н е д о д е л а н н у ю т р а н з а к ц и ю , к о т о р у ю н у ж н о о т к а т и т ь .
*
* PS . В о з м о ж н о , б ы л о б ы л у ч ш е д о б а в и т ь в DataPart ф л а г о т о м , ч т о к у с о к в с т а в л е н в ZK .
* Н о з д е с ь у ж е с л и ш к о м л е г к о з а п у т а т ь с я с к о н с и с т е н т н о с т ь ю э т о г о ф л а г а .
*/
const auto MAX_AGE_OF_LOCAL_PART_THAT_WASNT_ADDED_TO_ZOOKEEPER = 5 * 60 ;
2014-03-21 13:42:14 +00:00
StorageReplicatedMergeTree : : StorageReplicatedMergeTree (
const String & zookeeper_path_ ,
const String & replica_name_ ,
2014-03-21 19:17:59 +00:00
bool attach ,
2014-05-08 07:12:01 +00:00
const String & path_ , const String & database_name_ , const String & name_ ,
NamesAndTypesListPtr columns_ ,
2014-10-03 15:30:10 +00:00
const NamesAndTypesList & materialized_columns_ ,
2014-09-30 03:08:47 +00:00
const NamesAndTypesList & alias_columns_ ,
const ColumnDefaults & column_defaults_ ,
2014-03-22 14:44:44 +00:00
Context & context_ ,
2014-03-21 13:42:14 +00:00
ASTPtr & primary_expr_ast_ ,
const String & date_column_name_ ,
const ASTPtr & sampling_expression_ ,
size_t index_granularity_ ,
MergeTreeData : : Mode mode_ ,
const String & sign_column_ ,
2014-11-22 02:22:30 +00:00
const Names & columns_to_sum_ ,
2014-03-21 13:42:14 +00:00
const MergeTreeSettings & settings_ )
2014-10-03 15:30:10 +00:00
: IStorage { materialized_columns_ , alias_columns_ , column_defaults_ } , context ( context_ ) ,
2014-12-12 20:50:32 +00:00
current_zookeeper ( context . getZooKeeper ( ) ) , database_name ( database_name_ ) ,
2014-08-11 15:59:01 +00:00
table_name ( name_ ) , full_path ( path_ + escapeForFileName ( table_name ) + ' / ' ) ,
zookeeper_path ( context . getMacros ( ) . expand ( zookeeper_path_ ) ) ,
replica_name ( context . getMacros ( ) . expand ( replica_name_ ) ) ,
2014-10-03 15:30:10 +00:00
data ( full_path , columns_ ,
materialized_columns_ , alias_columns_ , column_defaults_ ,
context_ , primary_expr_ast_ , date_column_name_ ,
2014-11-22 02:22:30 +00:00
sampling_expression_ , index_granularity_ , mode_ , sign_column_ , columns_to_sum_ ,
2014-10-03 15:30:10 +00:00
settings_ , database_name_ + " . " + table_name , true ,
std : : bind ( & StorageReplicatedMergeTree : : enqueuePartForCheck , this , std : : placeholders : : _1 ) ) ,
2014-12-15 04:06:39 +00:00
reader ( data ) , writer ( data ) , merger ( data ) , fetcher ( data ) , shutdown_event ( false ) ,
log ( & Logger : : get ( database_name + " . " + table_name + " (StorageReplicatedMergeTree) " ) )
2014-03-21 13:42:14 +00:00
{
2014-10-09 23:14:06 +00:00
if ( ! zookeeper_path . empty ( ) & & zookeeper_path . back ( ) = = ' / ' )
zookeeper_path . resize ( zookeeper_path . size ( ) - 1 ) ;
2014-08-13 08:07:52 +00:00
replica_path = zookeeper_path + " /replicas/ " + replica_name ;
bool skip_sanity_checks = false ;
2014-11-07 01:12:55 +00:00
try
2014-08-13 08:07:52 +00:00
{
2014-12-12 20:50:32 +00:00
if ( current_zookeeper & & current_zookeeper - > exists ( replica_path + " /flags/force_restore_data " ) )
2014-11-07 01:12:55 +00:00
{
skip_sanity_checks = true ;
2014-12-12 20:50:32 +00:00
current_zookeeper - > remove ( replica_path + " /flags/force_restore_data " ) ;
2014-08-13 08:07:52 +00:00
2014-11-07 01:12:55 +00:00
LOG_WARNING ( log , " Skipping the limits on severity of changes to data parts and columns (flag "
< < replica_path < < " /flags/force_restore_data). " ) ;
}
}
catch ( const zkutil : : KeeperException & e )
{
/// Н е удалось соединиться с ZK (о б этом стало известно при попытке выполнить первую операцию).
if ( e . code = = ZCONNECTIONLOSS )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2014-12-12 20:50:32 +00:00
current_zookeeper = nullptr ;
2014-11-07 01:12:55 +00:00
}
else
throw ;
2014-08-13 08:07:52 +00:00
}
data . loadDataParts ( skip_sanity_checks ) ;
2014-12-12 20:50:32 +00:00
if ( ! current_zookeeper )
2014-05-13 11:24:04 +00:00
{
2014-07-23 13:58:38 +00:00
if ( ! attach )
throw Exception ( " Can't create replicated table without ZooKeeper " , ErrorCodes : : NO_ZOOKEEPER ) ;
2014-12-11 01:56:42 +00:00
/// Н е активируем реплику. Она будет в режиме readonly.
2014-05-13 11:24:04 +00:00
return ;
}
2014-03-21 19:17:59 +00:00
if ( ! attach )
{
2015-09-24 07:39:47 +00:00
if ( ! data . getDataParts ( ) . empty ( ) )
throw Exception ( " Data directory for table already containing data parts - probably it was unclean DROP table or manual intervention. You must either clear directory by hand or use ATTACH TABLE instead of CREATE TABLE if you need to use that parts. " , ErrorCodes : : INCORRECT_DATA ) ;
2014-08-11 14:00:24 +00:00
createTableIfNotExists ( ) ;
2014-03-22 14:44:44 +00:00
2014-08-12 12:41:39 +00:00
checkTableStructure ( false , false ) ;
2014-07-10 10:16:50 +00:00
createReplica ( ) ;
2014-03-21 19:17:59 +00:00
}
else
{
2014-08-12 12:41:39 +00:00
checkTableStructure ( skip_sanity_checks , true ) ;
2014-07-10 08:40:59 +00:00
checkParts ( skip_sanity_checks ) ;
2014-03-21 19:17:59 +00:00
}
2014-03-22 14:44:44 +00:00
2015-11-09 20:30:54 +00:00
createNewZooKeeperNodes ( ) ;
2014-04-24 10:20:02 +00:00
String unreplicated_path = full_path + " unreplicated/ " ;
if ( Poco : : File ( unreplicated_path ) . exists ( ) )
{
2014-10-03 15:30:10 +00:00
unreplicated_data . reset ( new MergeTreeData ( unreplicated_path , columns_ ,
materialized_columns_ , alias_columns_ , column_defaults_ ,
2014-09-30 03:08:47 +00:00
context_ , primary_expr_ast_ ,
2014-11-22 02:22:30 +00:00
date_column_name_ , sampling_expression_ , index_granularity_ , mode_ , sign_column_ , columns_to_sum_ , settings_ ,
2014-07-09 13:39:19 +00:00
database_name_ + " . " + table_name + " [unreplicated] " , false ) ) ;
2014-08-15 20:10:44 +00:00
unreplicated_data - > loadDataParts ( skip_sanity_checks ) ;
2015-09-27 07:11:00 +00:00
if ( unreplicated_data - > getDataPartsVector ( ) . empty ( ) )
{
unreplicated_data . reset ( ) ;
}
else
{
LOG_INFO ( log , " Have unreplicated data " ) ;
unreplicated_reader . reset ( new MergeTreeDataSelectExecutor ( * unreplicated_data ) ) ;
unreplicated_merger . reset ( new MergeTreeDataMerger ( * unreplicated_data ) ) ;
}
2014-04-24 10:20:02 +00:00
}
2014-04-25 13:55:15 +00:00
2016-01-10 04:43:30 +00:00
queue . initialize (
zookeeper_path , replica_path ,
database_name + " . " + table_name + " (ReplicatedMergeTreeQueue) " ,
data . getDataParts ( ) , current_zookeeper ) ;
2014-05-13 10:10:26 +00:00
2014-09-11 03:13:27 +00:00
/// В этом потоке реплика будет активирована.
2014-10-17 01:05:51 +00:00
restarting_thread . reset ( new ReplicatedMergeTreeRestartingThread ( * this ) ) ;
2014-03-21 13:42:14 +00:00
}
2014-10-17 01:05:51 +00:00
2015-11-09 20:30:54 +00:00
void StorageReplicatedMergeTree : : createNewZooKeeperNodes ( )
{
auto zookeeper = getZooKeeper ( ) ;
/// Работа с кворумом.
zookeeper - > createIfNotExists ( zookeeper_path + " /quorum " , " " ) ;
zookeeper - > createIfNotExists ( zookeeper_path + " /quorum/last_part " , " " ) ;
zookeeper - > createIfNotExists ( zookeeper_path + " /quorum/failed_parts " , " " ) ;
/// Отслеживание отставания реплик.
zookeeper - > createIfNotExists ( replica_path + " /min_unprocessed_insert_time " , " " ) ;
}
2014-03-21 13:42:14 +00:00
StoragePtr StorageReplicatedMergeTree : : create (
const String & zookeeper_path_ ,
const String & replica_name_ ,
2014-03-21 19:17:59 +00:00
bool attach ,
2014-05-08 07:12:01 +00:00
const String & path_ , const String & database_name_ , const String & name_ ,
NamesAndTypesListPtr columns_ ,
2014-10-03 15:30:10 +00:00
const NamesAndTypesList & materialized_columns_ ,
2014-09-30 03:08:47 +00:00
const NamesAndTypesList & alias_columns_ ,
const ColumnDefaults & column_defaults_ ,
2014-03-22 14:44:44 +00:00
Context & context_ ,
2014-03-21 13:42:14 +00:00
ASTPtr & primary_expr_ast_ ,
const String & date_column_name_ ,
const ASTPtr & sampling_expression_ ,
size_t index_granularity_ ,
MergeTreeData : : Mode mode_ ,
const String & sign_column_ ,
2015-07-16 21:06:45 +00:00
const Names & columns_to_sum_ ,
2014-03-21 13:42:14 +00:00
const MergeTreeSettings & settings_ )
{
2014-09-30 03:08:47 +00:00
auto res = new StorageReplicatedMergeTree {
zookeeper_path_ , replica_name_ , attach ,
path_ , database_name_ , name_ ,
2014-10-03 15:30:10 +00:00
columns_ , materialized_columns_ , alias_columns_ , column_defaults_ ,
2014-09-30 03:08:47 +00:00
context_ , primary_expr_ast_ , date_column_name_ ,
sampling_expression_ , index_granularity_ , mode_ ,
2014-12-12 20:50:32 +00:00
sign_column_ , columns_to_sum_ , settings_ } ;
2014-04-02 07:59:43 +00:00
StoragePtr res_ptr = res - > thisPtr ( ) ;
2014-10-17 01:05:51 +00:00
2014-12-12 20:50:32 +00:00
if ( res - > getZooKeeper ( ) )
2014-05-13 11:24:04 +00:00
{
String endpoint_name = " ReplicatedMergeTree: " + res - > replica_path ;
2014-07-22 13:49:52 +00:00
InterserverIOEndpointPtr endpoint = new ReplicatedMergeTreePartsServer ( res - > data , * res ) ;
2014-05-13 11:24:04 +00:00
res - > endpoint_holder = new InterserverIOEndpointHolder ( endpoint_name , endpoint , res - > context . getInterserverIOHandler ( ) ) ;
}
2014-10-17 01:05:51 +00:00
2014-04-02 07:59:43 +00:00
return res_ptr ;
2014-03-21 13:42:14 +00:00
}
2014-10-18 17:37:55 +00:00
2014-03-22 14:44:44 +00:00
static String formattedAST ( const ASTPtr & ast )
{
if ( ! ast )
return " " ;
std : : stringstream ss ;
formatAST ( * ast , ss , 0 , false , true ) ;
return ss . str ( ) ;
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2014-08-11 14:00:24 +00:00
void StorageReplicatedMergeTree : : createTableIfNotExists ( )
2014-03-21 19:17:59 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-11 14:00:24 +00:00
if ( zookeeper - > exists ( zookeeper_path ) )
return ;
2014-05-26 18:27:25 +00:00
2014-08-11 14:00:24 +00:00
LOG_DEBUG ( log , " Creating table " < < zookeeper_path ) ;
2014-03-21 19:17:59 +00:00
2014-08-11 14:05:38 +00:00
zookeeper - > createAncestors ( zookeeper_path ) ;
2014-07-10 10:16:50 +00:00
/// Запишем метаданные таблицы, чтобы реплики могли сверять с ними параметры таблицы.
2015-09-24 07:39:47 +00:00
std : : string metadata ;
{
WriteBufferFromString out ( metadata ) ;
out < < " metadata format version: 1 " < < " \n "
< < " date column: " < < data . date_column_name < < " \n "
< < " sampling expression: " < < formattedAST ( data . sampling_expression ) < < " \n "
< < " index granularity: " < < data . index_granularity < < " \n "
< < " mode: " < < static_cast < int > ( data . mode ) < < " \n "
< < " sign column: " < < data . sign_column < < " \n "
< < " primary key: " < < formattedAST ( data . primary_expr_ast ) < < " \n " ;
}
2014-03-21 19:17:59 +00:00
2015-09-24 04:52:02 +00:00
auto acl = zookeeper - > getDefaultACL ( ) ;
2014-08-11 14:00:24 +00:00
zkutil : : Ops ops ;
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2015-09-24 07:39:47 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /metadata " , metadata ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-11-10 16:16:43 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /columns " , ColumnsDescription < false > {
data . getColumnsListNonMaterialized ( ) , data . materialized_columns ,
data . alias_columns , data . column_defaults } . toString ( ) ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /log " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /blocks " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /block_numbers " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /nonincrement_block_numbers " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /leader_election " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /temp " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
ops . push_back ( new zkutil : : Op : : Create ( zookeeper_path + " /replicas " , " " ,
2015-09-24 04:52:02 +00:00
acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-11 14:00:24 +00:00
auto code = zookeeper - > tryMulti ( ops ) ;
if ( code ! = ZOK & & code ! = ZNODEEXISTS )
throw zkutil : : KeeperException ( code ) ;
2014-03-22 14:44:44 +00:00
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2014-03-22 14:44:44 +00:00
/** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata).
* Е с л и н е т - б р о с и т ь и с к л ю ч е н и е .
2014-03-21 19:17:59 +00:00
*/
2014-08-12 12:41:39 +00:00
void StorageReplicatedMergeTree : : checkTableStructure ( bool skip_sanity_checks , bool allow_alter )
2014-03-22 14:44:44 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-05-13 10:10:26 +00:00
String metadata_str = zookeeper - > get ( zookeeper_path + " /metadata " ) ;
2014-03-22 14:44:44 +00:00
ReadBufferFromString buf ( metadata_str ) ;
assertString ( " metadata format version: 1 " , buf ) ;
assertString ( " \n date column: " , buf ) ;
assertString ( data . date_column_name , buf ) ;
assertString ( " \n sampling expression: " , buf ) ;
assertString ( formattedAST ( data . sampling_expression ) , buf ) ;
assertString ( " \n index granularity: " , buf ) ;
assertString ( toString ( data . index_granularity ) , buf ) ;
assertString ( " \n mode: " , buf ) ;
assertString ( toString ( static_cast < int > ( data . mode ) ) , buf ) ;
assertString ( " \n sign column: " , buf ) ;
assertString ( data . sign_column , buf ) ;
assertString ( " \n primary key: " , buf ) ;
2014-07-10 10:16:50 +00:00
/// NOTE: Можно сделать менее строгую проверку совпадения выражений, чтобы таблицы не ломались от небольших изменений
/// в коде formatAST.
2014-03-22 14:44:44 +00:00
assertString ( formattedAST ( data . primary_expr_ast ) , buf ) ;
2014-07-15 11:42:06 +00:00
assertString ( " \n " , buf ) ;
2014-07-10 08:40:59 +00:00
assertEOF ( buf ) ;
2014-07-10 10:16:50 +00:00
zkutil : : Stat stat ;
2015-05-28 03:49:28 +00:00
auto columns_desc = ColumnsDescription < true > : : parse ( zookeeper - > get ( zookeeper_path + " /columns " , & stat ) ) ;
2014-11-10 16:16:43 +00:00
auto & columns = columns_desc . columns ;
auto & materialized_columns = columns_desc . materialized ;
auto & alias_columns = columns_desc . alias ;
auto & column_defaults = columns_desc . defaults ;
2014-07-10 10:16:50 +00:00
columns_version = stat . version ;
2014-11-10 16:16:43 +00:00
2014-10-21 12:11:20 +00:00
if ( columns ! = data . getColumnsListNonMaterialized ( ) | |
materialized_columns ! = data . materialized_columns | |
alias_columns ! = data . alias_columns | |
column_defaults ! = data . column_defaults )
2014-03-22 14:44:44 +00:00
{
2014-10-21 12:11:20 +00:00
if ( allow_alter & &
( skip_sanity_checks | |
data . getColumnsListNonMaterialized ( ) . sizeOfDifference ( columns ) +
data . materialized_columns . sizeOfDifference ( materialized_columns ) < = 2 ) )
2014-07-10 10:16:50 +00:00
{
2014-07-10 08:40:59 +00:00
LOG_WARNING ( log , " Table structure in ZooKeeper is a little different from local table structure. Assuming ALTER. " ) ;
2014-07-14 15:49:03 +00:00
/// Без всяких блокировок, потому что таблица еще не создана.
2014-10-16 13:37:01 +00:00
InterpreterAlterQuery : : updateMetadata ( database_name , table_name , columns ,
materialized_columns , alias_columns , column_defaults , context ) ;
2014-07-14 15:49:03 +00:00
data . setColumnsList ( columns ) ;
2014-10-16 13:37:01 +00:00
data . materialized_columns = std : : move ( materialized_columns ) ;
data . alias_columns = std : : move ( alias_columns ) ;
data . column_defaults = std : : move ( column_defaults ) ;
2014-07-10 10:16:50 +00:00
}
2014-07-10 08:40:59 +00:00
else
2014-07-10 10:16:50 +00:00
{
2014-08-12 12:41:39 +00:00
throw Exception ( " Table structure in ZooKeeper is too different from local table structure. " ,
2014-07-10 08:40:59 +00:00
ErrorCodes : : INCOMPATIBLE_COLUMNS ) ;
2014-07-10 10:16:50 +00:00
}
2014-03-22 14:44:44 +00:00
}
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2015-09-20 05:50:15 +00:00
/** При необходимости восстановить кусок, реплика сама добавляет в свою очередь запись на е г о получение.
* К а к о е п о с т а в и т ь в р е м я д л я э т о й з а п и с и в о ч е р е д и ? В р е м я у ч и т ы в а е т с я п р и р а с ч ё т е о т с т а в а н и я р е п л и к и .
* Д л я э т и х ц е л е й и м е е т с м ы с л и с п о л ь з о в а т ь в р е м я с о з д а н и я н е д о с т а ю щ е г о к у с к а
* ( т о е с т ь , п р и р а с ч ё т е о т с т а в а н и я б у д е т у ч и т а н о , н а с к о л ь к о с т а р ы й к у с о к н а м н у ж н о в о с с т а н о в и т ь ) .
*/
static time_t tryGetPartCreateTime ( zkutil : : ZooKeeperPtr & zookeeper , const String & replica_path , const String & part_name )
{
time_t res = 0 ;
/// Узнаем время создания part-а , если он ещё существует (не был, например, смерджен).
zkutil : : Stat stat ;
String unused ;
if ( zookeeper - > tryGet ( replica_path + " /parts/ " + part_name , unused , & stat ) )
res = stat . ctime / 1000 ;
return res ;
}
2014-03-22 14:44:44 +00:00
void StorageReplicatedMergeTree : : createReplica ( )
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-05-26 18:29:29 +00:00
LOG_DEBUG ( log , " Creating replica " < < replica_path ) ;
2014-05-26 18:27:25 +00:00
2014-08-11 14:00:24 +00:00
/// Создадим пустую реплику. Ноду columns создадим в конце - будем использовать е е в качестве признака, что создание реплики завершено.
2015-09-24 04:52:02 +00:00
auto acl = zookeeper - > getDefaultACL ( ) ;
2014-08-11 14:00:24 +00:00
zkutil : : Ops ops ;
2015-09-24 04:52:02 +00:00
ops . push_back ( new zkutil : : Op : : Create ( replica_path , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create ( replica_path + " /host " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create ( replica_path + " /log_pointer " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create ( replica_path + " /queue " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create ( replica_path + " /parts " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create ( replica_path + " /flags " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-10-02 23:31:18 +00:00
try
{
zookeeper - > multi ( ops ) ;
}
catch ( const zkutil : : KeeperException & e )
{
if ( e . code = = ZNODEEXISTS )
throw Exception ( " Replica " + replica_path + " is already exist. " , ErrorCodes : : REPLICA_IS_ALREADY_EXIST ) ;
throw ;
}
2014-05-26 18:14:52 +00:00
2014-07-15 14:37:49 +00:00
/** Нужно изменить данные ноды /replicas на что угодно, чтобы поток, удаляющий старые записи в логе,
* с п о т к н у л с я о б э т о и з м е н е н и е и н е у д а л и л з а п и с и , к о т о р ы е м ы е щ е н е п р о ч и т а л и .
*/
zookeeper - > set ( zookeeper_path + " /replicas " , " last added replica: " + replica_name ) ;
2014-08-11 14:00:24 +00:00
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
2014-05-26 18:14:52 +00:00
2014-08-11 14:00:24 +00:00
/** "Эталонная" реплика, у которой мы возьмем информацию о множестве кусков, очередь и указатель на лог.
* В о з ь м е м с л у ч а й н у ю и з р е п л и к , с о з д а н н ы х р а н ь ш е э т о й .
*/
String source_replica ;
2014-05-26 18:14:52 +00:00
2014-08-11 14:00:24 +00:00
Stat stat ;
zookeeper - > exists ( replica_path , & stat ) ;
auto my_create_time = stat . czxid ;
2014-05-26 18:14:52 +00:00
2014-08-11 14:00:24 +00:00
std : : random_shuffle ( replicas . begin ( ) , replicas . end ( ) ) ;
for ( const String & replica : replicas )
2014-05-26 18:14:52 +00:00
{
2014-08-11 14:00:24 +00:00
if ( ! zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica , & stat ) )
throw Exception ( " Replica " + zookeeper_path + " /replicas/ " + replica + " was removed from right under our feet. " ,
ErrorCodes : : NO_SUCH_REPLICA ) ;
if ( stat . czxid < my_create_time )
{
source_replica = replica ;
break ;
}
2014-05-26 18:14:52 +00:00
}
2014-08-11 14:00:24 +00:00
if ( source_replica . empty ( ) )
2014-05-26 18:14:52 +00:00
{
2014-08-11 14:00:24 +00:00
LOG_INFO ( log , " This is the first replica " ) ;
2014-05-26 18:14:52 +00:00
}
2014-08-11 14:00:24 +00:00
else
2014-05-26 18:14:52 +00:00
{
2014-08-11 14:00:24 +00:00
LOG_INFO ( log , " Will mimic " < < source_replica ) ;
2014-05-26 18:14:52 +00:00
2014-08-11 14:00:24 +00:00
String source_path = zookeeper_path + " /replicas/ " + source_replica ;
2014-05-26 18:14:52 +00:00
2014-08-11 14:00:24 +00:00
/** Если эталонная реплика еще не до конца создана, подождем.
* NOTE : Е с л и п р и е е с о з д а н и и ч т о - т о п о ш л о н е т а к , м о ж е м п р о в и с е т ь т у т в е ч н о .
* М о ж н о с о з д а в а т ь н а в р е м я с о з д а н и я э ф е м е р н у ю н о д у , ч т о б ы б ы т ь у в е р е н н ы м , ч т о р е п л и к а с о з д а е т с я , а н е з а б р о ш е н а .
* Т о ж е м о ж н о д е л а т ь и д л я т а б л и ц ы . М о ж н о а в т о м а т и ч е с к и у д а л я т ь н о д у р е п л и к и / т а б л и ц ы ,
* е с л и в и д н о , ч т о о н а с о з д а н а н е д о к о н ц а , а с о з д а ю щ и й е е у м е р .
*/
while ( ! zookeeper - > exists ( source_path + " /columns " ) )
{
LOG_INFO ( log , " Waiting for replica " < < source_path < < " to be fully created " ) ;
zkutil : : EventPtr event = new Poco : : Event ;
if ( zookeeper - > exists ( source_path + " /columns " , nullptr , event ) )
{
LOG_WARNING ( log , " Oops, a watch has leaked " ) ;
break ;
}
event - > wait ( ) ;
}
/// Порядок следующих трех действий важен. Записи в логе могут продублироваться, но не могут потеряться.
/// Скопируем у эталонной реплики ссылку на лог.
zookeeper - > set ( replica_path + " /log_pointer " , zookeeper - > get ( source_path + " /log_pointer " ) ) ;
/// Запомним очередь эталонной реплики.
Strings source_queue_names = zookeeper - > getChildren ( source_path + " /queue " ) ;
std : : sort ( source_queue_names . begin ( ) , source_queue_names . end ( ) ) ;
Strings source_queue ;
for ( const String & entry_name : source_queue_names )
{
String entry ;
if ( ! zookeeper - > tryGet ( source_path + " /queue/ " + entry_name , entry ) )
continue ;
source_queue . push_back ( entry ) ;
}
/// Добавим в очередь задания на получение всех активных кусков, которые есть у эталонной реплики.
Strings parts = zookeeper - > getChildren ( source_path + " /parts " ) ;
2014-10-10 00:46:37 +00:00
ActiveDataPartSet active_parts_set ( parts ) ;
2014-08-11 14:00:24 +00:00
Strings active_parts = active_parts_set . getParts ( ) ;
for ( const String & name : active_parts )
{
LogEntry log_entry ;
log_entry . type = LogEntry : : GET_PART ;
log_entry . source_replica = " " ;
log_entry . new_part_name = name ;
2015-09-20 05:50:15 +00:00
log_entry . create_time = tryGetPartCreateTime ( zookeeper , source_path , name ) ;
2015-09-17 21:30:43 +00:00
2014-08-11 14:00:24 +00:00
zookeeper - > create ( replica_path + " /queue/queue- " , log_entry . toString ( ) , zkutil : : CreateMode : : PersistentSequential ) ;
}
LOG_DEBUG ( log , " Queued " < < active_parts . size ( ) < < " parts to be fetched " ) ;
/// Добавим в очередь содержимое очереди эталонной реплики.
for ( const String & entry : source_queue )
{
zookeeper - > create ( replica_path + " /queue/queue- " , entry , zkutil : : CreateMode : : PersistentSequential ) ;
}
2015-09-20 05:50:15 +00:00
2016-01-10 04:43:30 +00:00
/// Далее оно будет загружено в переменную queue в методе queue.load.
2015-09-20 05:50:15 +00:00
2014-08-11 14:00:24 +00:00
LOG_DEBUG ( log , " Copied " < < source_queue . size ( ) < < " queue entries " ) ;
2014-05-26 18:14:52 +00:00
}
2014-08-11 14:00:24 +00:00
2014-11-10 16:16:43 +00:00
zookeeper - > create ( replica_path + " /columns " , ColumnsDescription < false > {
data . getColumnsListNonMaterialized ( ) ,
data . materialized_columns ,
data . alias_columns ,
data . column_defaults
} . toString ( ) , zkutil : : CreateMode : : Persistent ) ;
2014-03-22 14:44:44 +00:00
}
2014-03-21 19:17:59 +00:00
2014-07-10 08:40:59 +00:00
void StorageReplicatedMergeTree : : checkParts ( bool skip_sanity_checks )
2014-04-02 07:59:43 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-05-13 10:10:26 +00:00
Strings expected_parts_vec = zookeeper - > getChildren ( replica_path + " /parts " ) ;
2014-05-22 10:37:17 +00:00
/// Куски в ZK.
2014-04-02 07:59:43 +00:00
NameSet expected_parts ( expected_parts_vec . begin ( ) , expected_parts_vec . end ( ) ) ;
2014-04-09 16:32:32 +00:00
MergeTreeData : : DataParts parts = data . getAllDataParts ( ) ;
2014-04-02 07:59:43 +00:00
2014-05-22 10:37:17 +00:00
/// Локальные куски, которых нет в ZK.
2014-04-08 17:45:21 +00:00
MergeTreeData : : DataParts unexpected_parts ;
2014-05-22 10:37:17 +00:00
2014-04-02 07:59:43 +00:00
for ( const auto & part : parts )
{
if ( expected_parts . count ( part - > name ) )
expected_parts . erase ( part - > name ) ;
else
2014-04-08 17:45:21 +00:00
unexpected_parts . insert ( part ) ;
2014-04-02 07:59:43 +00:00
}
2014-05-22 10:37:17 +00:00
/// Какие локальные куски добавить в ZK.
2014-04-08 17:45:21 +00:00
MergeTreeData : : DataPartsVector parts_to_add ;
2014-05-22 10:37:17 +00:00
/// Какие куски нужно забрать с других реплик.
Strings parts_to_fetch ;
2014-04-08 17:45:21 +00:00
for ( const String & missing_name : expected_parts )
{
2014-05-22 10:37:17 +00:00
/// Если локально не хватает какого-то куска, но есть покрывающий е г о кусок, можно заменить в ZK недостающий покрывающим.
2014-07-25 11:38:46 +00:00
auto containing = data . getActiveContainingPart ( missing_name ) ;
2014-05-22 10:37:17 +00:00
if ( containing )
{
LOG_ERROR ( log , " Ignoring missing local part " < < missing_name < < " because part " < < containing - > name < < " exists " ) ;
if ( unexpected_parts . count ( containing ) )
{
parts_to_add . push_back ( containing ) ;
unexpected_parts . erase ( containing ) ;
}
}
else
2014-04-08 17:45:21 +00:00
{
2014-08-13 08:07:52 +00:00
LOG_ERROR ( log , " Fetching missing part " < < missing_name ) ;
2014-05-22 10:37:17 +00:00
parts_to_fetch . push_back ( missing_name ) ;
2014-04-08 17:45:21 +00:00
}
}
2014-04-02 07:59:43 +00:00
2014-05-22 10:37:17 +00:00
for ( const String & name : parts_to_fetch )
expected_parts . erase ( name ) ;
2015-09-24 01:17:10 +00:00
/** Для проверки адекватности, для кусков, которые есть в ФС, но нет в ZK, будем учитывать только не самые новые куски.
* П о т о м у ч т о н е о ж и д а н н ы е н о в ы е к у с к и о б ы ч н о в о з н и к а ю т л и ш ь о т т о г о , ч т о о н и н е у с п е л и з а п и с а т ь с я в ZK п р и г р у б о м п е р е з а п у с к е с е р в е р а .
* Т а к ж е э т о в о з н и к а е т о т д е д у п л и ц и р о в а н н ы х к у с к о в , к о т о р ы е н е у с п е л и у д а л и т ь с я .
*/
size_t unexpected_parts_nonnew = 0 ;
for ( const auto & part : unexpected_parts )
if ( part - > level > 0 | | part - > right < RESERVED_BLOCK_NUMBERS )
+ + unexpected_parts_nonnew ;
String sanity_report = " There are "
+ toString ( unexpected_parts . size ( ) ) + " unexpected parts ( "
+ toString ( unexpected_parts_nonnew ) + " of them is not just-written), "
+ toString ( parts_to_add . size ( ) ) + " unexpectedly merged parts, "
+ toString ( expected_parts . size ( ) ) + " missing obsolete parts, "
+ toString ( parts_to_fetch . size ( ) ) + " missing parts " ;
2014-05-22 10:37:17 +00:00
2014-10-15 19:42:32 +00:00
/** Можно автоматически синхронизировать данные,
2015-09-24 01:17:10 +00:00
* е с л и к о л и ч е с т в о о ш и б о к к а ж д о г о и з ч е т ы р ё х т и п о в н е б о л ь ш е с о о т в е т с т в у ю щ и х п о р о г о в ,
* и л и е с л и о т н о ш е н и е о б щ е г о к о л и ч е с т в а о ш и б о к к о б щ е м у к о л и ч е с т в у к у с к о в ( м и н и м а л ь н о м у - в л о к а л ь н о й ф а й л о в о й с и с т е м е и л и в ZK )
2014-10-15 19:42:32 +00:00
* н е б о л ь ш е н е к о т о р о г о о т н о ш е н и я ( н а п р и м е р 5 % ) .
2015-09-24 01:17:10 +00:00
*
* Б о л ь ш о е к о л и ч е с т в о н е с о в п а д е н и й в д а н н ы х н а ф а й л о в о й с и с т е м е и о ж и д а е м ы х д а н н ы х
* м о ж е т с в и д е т е л ь с т в о в а т ь о б о ш и б к е к о н ф и г у р а ц и и ( с е р в е р с л у ч а й н о п о д к л ю ч и л и к а к р е п л и к у н е о т т о г о ш а р д а ) .
* В э т о м с л у ч а е , з а щ и т н ы й м е х а н и з м н е д а ё т с т а р т о в а т ь с е р в е р у .
2014-10-15 19:42:32 +00:00
*/
size_t min_parts_local_or_expected = std : : min ( expected_parts_vec . size ( ) , parts . size ( ) ) ;
2015-09-24 01:17:10 +00:00
size_t total_difference = parts_to_add . size ( ) + unexpected_parts_nonnew + expected_parts . size ( ) + parts_to_fetch . size ( ) ;
2014-10-15 19:42:32 +00:00
bool insane =
( parts_to_add . size ( ) > data . settings . replicated_max_unexpectedly_merged_parts
2015-09-24 01:17:10 +00:00
| | unexpected_parts_nonnew > data . settings . replicated_max_unexpected_parts
2014-10-15 19:42:32 +00:00
| | expected_parts . size ( ) > data . settings . replicated_max_missing_obsolete_parts
| | parts_to_fetch . size ( ) > data . settings . replicated_max_missing_active_parts )
2015-09-24 01:17:10 +00:00
& & ( total_difference > min_parts_local_or_expected * data . settings . replicated_max_ratio_of_wrong_parts ) ;
2014-04-08 17:45:21 +00:00
2015-09-24 01:17:10 +00:00
if ( insane & & ! skip_sanity_checks )
throw Exception ( " The local set of parts of table " + getTableName ( ) + " doesn't look like the set of parts in ZooKeeper. "
+ sanity_report , ErrorCodes : : TOO_MANY_UNEXPECTED_DATA_PARTS ) ;
if ( total_difference > 0 )
LOG_WARNING ( log , sanity_report ) ;
2014-07-10 10:16:50 +00:00
2014-04-08 17:45:21 +00:00
/// Добавим в ZK информацию о кусках, покрывающих недостающие куски.
2014-09-29 20:26:46 +00:00
for ( const MergeTreeData : : DataPartPtr & part : parts_to_add )
2014-04-08 17:45:21 +00:00
{
2014-04-09 16:32:32 +00:00
LOG_ERROR ( log , " Adding unexpected local part to ZooKeeper: " < < part - > name ) ;
2014-05-22 10:37:17 +00:00
2014-04-08 17:45:21 +00:00
zkutil : : Ops ops ;
2014-07-15 09:56:17 +00:00
checkPartAndAddToZooKeeper ( part , ops ) ;
2014-05-13 10:10:26 +00:00
zookeeper - > multi ( ops ) ;
2014-04-08 17:45:21 +00:00
}
2014-04-02 07:59:43 +00:00
2014-04-08 17:45:21 +00:00
/// Удалим из ZK информацию о кусках, покрытых только что добавленными.
for ( const String & name : expected_parts )
{
2014-05-22 10:37:17 +00:00
LOG_ERROR ( log , " Removing unexpectedly merged local part from ZooKeeper: " < < name ) ;
zkutil : : Ops ops ;
2015-09-20 11:02:59 +00:00
removePartFromZooKeeper ( name , ops ) ;
2014-05-22 10:37:17 +00:00
zookeeper - > multi ( ops ) ;
}
/// Добавим в очередь задание забрать недостающие куски с других реплик и уберем из ZK информацию, что они у нас есть.
for ( const String & name : parts_to_fetch )
{
LOG_ERROR ( log , " Removing missing part from ZooKeeper and queueing a fetch: " < < name ) ;
LogEntry log_entry ;
log_entry . type = LogEntry : : GET_PART ;
2014-05-26 17:12:46 +00:00
log_entry . source_replica = " " ;
2014-05-22 10:37:17 +00:00
log_entry . new_part_name = name ;
2015-09-20 05:50:15 +00:00
log_entry . create_time = tryGetPartCreateTime ( zookeeper , replica_path , name ) ;
2014-05-22 10:37:17 +00:00
2016-01-10 04:43:30 +00:00
/// Полагаемся, что это происходит до загрузки очереди (queue.load).
2014-04-08 17:45:21 +00:00
zkutil : : Ops ops ;
2015-09-20 11:02:59 +00:00
removePartFromZooKeeper ( name , ops ) ;
2014-05-22 10:37:17 +00:00
ops . push_back ( new zkutil : : Op : : Create (
replica_path + " /queue/queue- " , log_entry . toString ( ) , zookeeper - > getDefaultACL ( ) , zkutil : : CreateMode : : PersistentSequential ) ) ;
2014-05-13 10:10:26 +00:00
zookeeper - > multi ( ops ) ;
2014-04-08 17:45:21 +00:00
}
/// Удалим лишние локальные куски.
2014-09-29 20:26:46 +00:00
for ( const MergeTreeData : : DataPartPtr & part : unexpected_parts )
2014-04-02 07:59:43 +00:00
{
2014-05-22 10:37:17 +00:00
LOG_ERROR ( log , " Renaming unexpected part " < < part - > name < < " to ignored_ " + part - > name ) ;
2014-07-28 09:46:28 +00:00
data . renameAndDetachPart ( part , " ignored_ " , true ) ;
2014-04-02 07:59:43 +00:00
}
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2014-09-29 20:26:46 +00:00
void StorageReplicatedMergeTree : : checkPartAndAddToZooKeeper ( const MergeTreeData : : DataPartPtr & part , zkutil : : Ops & ops , String part_name )
2014-04-08 17:45:21 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-08 08:28:13 +00:00
if ( part_name . empty ( ) )
part_name = part - > name ;
2014-07-14 15:49:03 +00:00
check ( part - > columns ) ;
2014-07-15 09:56:17 +00:00
int expected_columns_version = columns_version ;
2014-07-14 15:49:03 +00:00
2014-07-10 10:16:50 +00:00
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
std : : random_shuffle ( replicas . begin ( ) , replicas . end ( ) ) ;
String expected_columns_str = part - > columns . toString ( ) ;
for ( const String & replica : replicas )
2014-04-08 17:45:21 +00:00
{
2014-07-10 10:16:50 +00:00
zkutil : : Stat stat_before , stat_after ;
String columns_str ;
2014-08-08 08:28:13 +00:00
if ( ! zookeeper - > tryGet ( zookeeper_path + " /replicas/ " + replica + " /parts/ " + part_name + " /columns " , columns_str , & stat_before ) )
2014-07-10 10:16:50 +00:00
continue ;
if ( columns_str ! = expected_columns_str )
{
2014-08-08 08:28:13 +00:00
LOG_INFO ( log , " Not checking checksums of part " < < part_name < < " with replica " < < replica
2014-07-10 10:16:50 +00:00
< < " because columns are different " ) ;
continue ;
}
2014-04-14 13:08:26 +00:00
String checksums_str ;
2014-07-10 10:16:50 +00:00
/// Проверим, что версия ноды с о столбцами не изменилась, пока мы читали checksums.
/// Это гарантирует, что столбцы и чексуммы относятся к одним и тем же данным.
2014-08-08 08:28:13 +00:00
if ( ! zookeeper - > tryGet ( zookeeper_path + " /replicas/ " + replica + " /parts/ " + part_name + " /checksums " , checksums_str ) | |
! zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica + " /parts/ " + part_name + " /columns " , & stat_after ) | |
2014-07-10 10:16:50 +00:00
stat_before . version ! = stat_after . version )
2014-04-14 13:08:26 +00:00
{
2014-08-08 08:28:13 +00:00
LOG_INFO ( log , " Not checking checksums of part " < < part_name < < " with replica " < < replica
2014-07-10 10:16:50 +00:00
< < " because part changed while we were reading its checksums " ) ;
continue ;
2014-04-14 13:08:26 +00:00
}
2014-07-10 10:16:50 +00:00
auto checksums = MergeTreeData : : DataPart : : Checksums : : parse ( checksums_str ) ;
checksums . checkEqual ( part - > checksums , true ) ;
2014-04-08 17:45:21 +00:00
}
2014-08-08 08:28:13 +00:00
if ( zookeeper - > exists ( replica_path + " /parts/ " + part_name ) )
2014-07-28 14:31:07 +00:00
{
2014-08-08 08:28:13 +00:00
LOG_ERROR ( log , " checkPartAndAddToZooKeeper: node " < < replica_path + " /parts/ " + part_name < < " already exists " ) ;
2014-07-28 14:31:07 +00:00
return ;
}
2015-09-24 04:52:02 +00:00
auto acl = zookeeper - > getDefaultACL ( ) ;
2014-07-14 15:49:03 +00:00
ops . push_back ( new zkutil : : Op : : Check (
zookeeper_path + " /columns " ,
expected_columns_version ) ) ;
2014-04-08 17:45:21 +00:00
ops . push_back ( new zkutil : : Op : : Create (
2014-08-08 08:28:13 +00:00
replica_path + " /parts/ " + part_name ,
2014-04-08 17:45:21 +00:00
" " ,
2015-09-24 04:52:02 +00:00
acl ,
2014-04-08 17:45:21 +00:00
zkutil : : CreateMode : : Persistent ) ) ;
2014-07-10 10:16:50 +00:00
ops . push_back ( new zkutil : : Op : : Create (
2014-08-08 08:28:13 +00:00
replica_path + " /parts/ " + part_name + " /columns " ,
2014-07-10 10:16:50 +00:00
part - > columns . toString ( ) ,
2015-09-24 04:52:02 +00:00
acl ,
2014-07-10 10:16:50 +00:00
zkutil : : CreateMode : : Persistent ) ) ;
2014-04-08 17:45:21 +00:00
ops . push_back ( new zkutil : : Op : : Create (
2014-08-08 08:28:13 +00:00
replica_path + " /parts/ " + part_name + " /checksums " ,
2014-04-08 17:45:21 +00:00
part - > checksums . toString ( ) ,
2015-09-24 04:52:02 +00:00
acl ,
2014-04-08 17:45:21 +00:00
zkutil : : CreateMode : : Persistent ) ) ;
}
2014-10-18 17:37:55 +00:00
2014-07-15 14:37:49 +00:00
void StorageReplicatedMergeTree : : pullLogsToQueue ( zkutil : : EventPtr next_update_event )
2014-04-03 08:47:59 +00:00
{
2016-01-10 04:43:30 +00:00
if ( queue . pullLogsToQueue ( getZooKeeper ( ) , next_update_event ) )
2014-07-04 11:18:04 +00:00
{
2016-01-10 04:43:30 +00:00
if ( queue_task_handle )
queue_task_handle - > wake ( ) ;
2014-07-04 11:18:04 +00:00
}
2014-04-03 11:48:28 +00:00
}
2014-10-18 17:37:55 +00:00
2014-07-18 15:41:04 +00:00
bool StorageReplicatedMergeTree : : executeLogEntry ( const LogEntry & entry , BackgroundProcessingPool : : Context & pool_context )
2014-04-03 11:48:28 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-05 13:49:44 +00:00
if ( entry . type = = LogEntry : : DROP_RANGE )
2014-08-08 08:28:13 +00:00
{
executeDropRange ( entry ) ;
return true ;
}
2014-08-05 13:49:44 +00:00
2014-04-03 11:48:28 +00:00
if ( entry . type = = LogEntry : : GET_PART | |
2014-08-08 08:28:13 +00:00
entry . type = = LogEntry : : MERGE_PARTS | |
entry . type = = LogEntry : : ATTACH_PART )
2014-04-03 11:48:28 +00:00
{
/// Если у нас уже есть этот кусок или покрывающий е г о кусок, ничего делать не нужно.
2014-07-25 11:38:46 +00:00
MergeTreeData : : DataPartPtr containing_part = data . getActiveContainingPart ( entry . new_part_name ) ;
2014-04-03 11:48:28 +00:00
2015-09-14 22:45:19 +00:00
/// Даже если кусок есть локально, е г о (в исключительных случаях) может не быть в zookeeper. Проверим, что он там есть.
2014-05-13 10:10:26 +00:00
if ( containing_part & & zookeeper - > exists ( replica_path + " /parts/ " + containing_part - > name ) )
2014-04-04 12:47:57 +00:00
{
2014-04-07 15:45:46 +00:00
if ( ! ( entry . type = = LogEntry : : GET_PART & & entry . source_replica = = replica_name ) )
2015-09-20 11:02:59 +00:00
LOG_DEBUG ( log , " Skipping action for part " < < entry . new_part_name < < " - part already exists. " ) ;
2014-07-18 15:41:04 +00:00
return true ;
2014-04-04 12:47:57 +00:00
}
2014-04-03 11:48:28 +00:00
}
2014-04-07 15:45:46 +00:00
if ( entry . type = = LogEntry : : GET_PART & & entry . source_replica = = replica_name )
2014-05-27 10:03:13 +00:00
LOG_WARNING ( log , " Part " < < entry . new_part_name < < " from own log doesn't exist. " ) ;
2014-04-07 15:45:46 +00:00
2015-09-20 11:02:59 +00:00
/// Возможно, этот кусок нам не нужен, так как при записи с кворумом, кворум пофейлился (см. ниже про /quorum/failed_parts).
2015-09-21 18:47:46 +00:00
if ( entry . quorum & & zookeeper - > exists ( zookeeper_path + " /quorum/failed_parts/ " + entry . new_part_name ) )
2015-09-20 11:02:59 +00:00
{
LOG_DEBUG ( log , " Skipping action for part " < < entry . new_part_name < < " because quorum for that part was failed. " ) ;
return true ; /// NOTE Удаление из virtual_parts не делается, но оно нужно только для мерджей.
}
2014-04-07 15:45:46 +00:00
bool do_fetch = false ;
2014-04-04 12:47:57 +00:00
if ( entry . type = = LogEntry : : GET_PART )
{
2014-04-07 15:45:46 +00:00
do_fetch = true ;
2014-04-04 12:47:57 +00:00
}
2014-08-08 08:28:13 +00:00
else if ( entry . type = = LogEntry : : ATTACH_PART )
{
do_fetch = ! executeAttachPart ( entry ) ;
}
2014-04-04 12:47:57 +00:00
else if ( entry . type = = LogEntry : : MERGE_PARTS )
{
2014-09-17 23:57:25 +00:00
std : : stringstream log_message ;
log_message < < " Executing log entry to merge parts " ;
for ( auto i : ext : : range ( 0 , entry . parts_to_merge . size ( ) ) )
log_message < < ( i ! = 0 ? " , " : " " ) < < entry . parts_to_merge [ i ] ;
log_message < < " to " < < entry . new_part_name ;
LOG_TRACE ( log , log_message . rdbuf ( ) ) ;
2014-04-04 13:27:47 +00:00
MergeTreeData : : DataPartsVector parts ;
2014-07-14 15:49:03 +00:00
bool have_all_parts = true ;
2014-04-04 13:27:47 +00:00
for ( const String & name : entry . parts_to_merge )
{
2014-07-25 11:38:46 +00:00
MergeTreeData : : DataPartPtr part = data . getActiveContainingPart ( name ) ;
2014-04-07 15:45:46 +00:00
if ( ! part )
{
have_all_parts = false ;
break ;
}
if ( part - > name ! = name )
{
2014-08-08 10:46:38 +00:00
LOG_WARNING ( log , " Part " < < name < < " is covered by " < < part - > name
< < " but should be merged into " < < entry . new_part_name < < " . This shouldn't happen often. " ) ;
2014-04-07 15:45:46 +00:00
have_all_parts = false ;
break ;
}
2014-04-04 13:27:47 +00:00
parts . push_back ( part ) ;
}
2014-04-04 17:20:45 +00:00
2014-04-07 15:45:46 +00:00
if ( ! have_all_parts )
2014-04-04 13:27:47 +00:00
{
2014-04-07 15:45:46 +00:00
/// Если нет всех нужных кусков, попробуем взять у кого-нибудь уже помердженный кусок.
do_fetch = true ;
LOG_DEBUG ( log , " Don't have all parts for merge " < < entry . new_part_name < < " ; will try to fetch it instead " ) ;
2014-04-04 13:27:47 +00:00
}
2014-04-07 15:45:46 +00:00
else
{
2014-07-02 12:30:38 +00:00
/// Если собираемся сливать большие куски, увеличим счетчик потоков, сливающих большие куски.
for ( const auto & part : parts )
{
2014-07-23 15:24:45 +00:00
if ( part - > size_in_bytes > data . settings . max_bytes_to_merge_parts_small )
2014-07-02 12:30:38 +00:00
{
pool_context . incrementCounter ( " big merges " ) ;
pool_context . incrementCounter ( " replicated big merges " ) ;
break ;
}
}
2015-06-11 00:35:36 +00:00
size_t sum_parts_size_in_bytes = MergeTreeDataMerger : : estimateDiskSpaceForMerge ( parts ) ;
2015-09-20 05:21:43 +00:00
/// Может бросить исключение.
DiskSpaceMonitor : : ReservationPtr reserved_space = DiskSpaceMonitor : : reserve ( full_path , sum_parts_size_in_bytes ) ;
2015-06-11 00:35:36 +00:00
2014-07-28 14:31:07 +00:00
auto table_lock = lockStructure ( false ) ;
2014-07-14 15:49:03 +00:00
2014-09-10 11:34:26 +00:00
const auto & merge_entry = context . getMergeList ( ) . insert ( database_name , table_name , entry . new_part_name ) ;
2014-07-01 15:58:25 +00:00
MergeTreeData : : Transaction transaction ;
2015-04-10 17:51:39 +00:00
size_t aio_threshold = context . getSettings ( ) . min_bytes_to_use_direct_io ;
2015-09-20 05:21:43 +00:00
MergeTreeData : : DataPartPtr part = merger . mergeParts (
parts , entry . new_part_name , * merge_entry , aio_threshold , & transaction , reserved_space ) ;
2014-04-07 15:45:46 +00:00
zkutil : : Ops ops ;
2014-07-15 09:56:17 +00:00
checkPartAndAddToZooKeeper ( part , ops ) ;
2014-04-07 15:45:46 +00:00
2014-08-08 10:41:53 +00:00
/** TODO: Переименование нового куска лучше делать здесь, а не пятью строчками выше,
* ч т о б ы о н о б ы л о к а к м о ж н о б л и ж е к zookeeper - > multi .
*/
2014-05-13 10:10:26 +00:00
zookeeper - > multi ( ops ) ;
2014-07-28 14:31:07 +00:00
2015-09-14 22:45:19 +00:00
/** Удаление старых кусков из ZK и с диска делается отложенно - см. ReplicatedMergeTreeCleanupThread, clearOldParts.
*/
2014-07-28 14:31:07 +00:00
/** При ZCONNECTIONLOSS или ZOPERATIONTIMEOUT можем зря откатить локальные изменения кусков.
* Э т о н е п р о б л е м а , п о т о м у ч т о в т а к о м с л у ч а е с л и я н и е о с т а н е т с я в о ч е р е д и , и м ы п о п р о б у е м с н о в а .
*/
2014-07-01 15:58:25 +00:00
transaction . commit ( ) ;
2014-07-02 10:16:49 +00:00
merge_selecting_event . set ( ) ;
2014-04-07 15:45:46 +00:00
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartMerges ) ;
}
2014-04-04 12:47:57 +00:00
}
else
{
throw Exception ( " Unexpected log entry type: " + toString ( static_cast < int > ( entry . type ) ) ) ;
}
2014-04-07 15:45:46 +00:00
if ( do_fetch )
{
2014-07-18 15:41:04 +00:00
String replica ;
2014-04-07 15:45:46 +00:00
try
{
2014-07-18 15:41:04 +00:00
replica = findReplicaHavingPart ( entry . new_part_name , true ) ;
2014-10-18 17:37:55 +00:00
if ( replica . empty ( ) & & entry . type = = LogEntry : : ATTACH_PART )
{
/** Если ATTACH - куска может не быть, потому что реплика, на которой кусок есть, ещё сама не успела е г о прицепить.
* В т а к о м с л у ч а е , н а д о п о д о ж д а т ь э т о г о .
*/
/// Кусок должен быть на реплике-инициаторе.
if ( entry . source_replica . empty ( ) | | entry . source_replica = = replica_name )
throw Exception ( " Logical error: no source replica specified for ATTACH_PART log entry; "
" or trying to fetch part on source replica " , ErrorCodes : : LOGICAL_ERROR ) ;
/// Подождём, пока реплика-инициатор подцепит кусок.
waitForReplicaToProcessLogEntry ( entry . source_replica , entry ) ;
replica = findReplicaHavingPart ( entry . new_part_name , true ) ;
}
2014-04-08 17:45:21 +00:00
if ( replica . empty ( ) )
{
2015-09-20 11:02:59 +00:00
/** Если кусок должен быть записан с кворумом, и кворум ещё недостигнут,
* т о ( и з - з а т о г о , ч т о к у с о к н е в о з м о ж н о п р я м о с е й ч а с с к а ч а т ь ) ,
* к в о р у м н у ю з а п и с ь с л е д у е т с ч и т а т ь б е з у с п е ш н о й .
* TODO С л о ж н ы й к о д , в ы н е с т и о т д е л ь н о .
*/
if ( entry . quorum )
{
if ( entry . type ! = LogEntry : : GET_PART )
throw Exception ( " Logical error: log entry with quorum but type is not GET_PART " , ErrorCodes : : LOGICAL_ERROR ) ;
2015-09-27 10:45:49 +00:00
if ( entry . block_id . empty ( ) )
throw Exception ( " Logical error: log entry with quorum have empty block_id " , ErrorCodes : : LOGICAL_ERROR ) ;
2015-09-20 11:02:59 +00:00
LOG_DEBUG ( log , " No active replica has part " < < entry . new_part_name < < " which needs to be written with quorum. "
" Will try to mark that quorum as failed. " ) ;
/** Атомарно:
* - е с л и р е п л и к и н е с т а л и а к т и в н ы м и ;
* - е с л и с у щ е с т в у е т у з е л quorum с э т и м к у с к о м ;
* - у д а л и м у з е л quorum ;
* - у с т а н о в и м nonincrement_block_numbers , ч т о б ы р а з р е ш и т ь м е р д ж и ч е р е з н о м е р п о т е р я н н о г о к у с к а ;
2015-09-27 10:45:49 +00:00
* - д о б а в и м к у с о к в с п и с о к quorum / failed_parts ;
* - е с л и к у с о к е щ ё н е у д а л ё н и з с п и с к а д л я д е д у п л и к а ц и и blocks / block_num , т о у д а л и м е г о ;
2015-09-21 22:43:38 +00:00
*
2015-09-20 11:02:59 +00:00
* Е с л и ч т о - т о и з м е н и т с я , т о н и ч е г о н е с д е л а е м - п о п а д ё м с ю д а с н о в а в с л е д у ю щ и й р а з .
*/
/** Соберём версии узлов host у реплик.
* К о г д а р е п л и к а с т а н о в и т с я а к т и в н о й , о н а в т о й ж е т р а н з а к ц и и ( с с о з д а н и е м is_active ) , м е н я е т з н а ч е н и е host .
* Э т о п о з в о л и т п р о с л е д и т ь , ч т о р е п л и к и н е с т а л и а к т и в н ы м и .
*/
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
zkutil : : Ops ops ;
for ( size_t i = 0 , size = replicas . size ( ) ; i < size ; + + i )
{
Stat stat ;
String path = zookeeper_path + " /replicas/ " + replicas [ i ] + " /host " ;
zookeeper - > get ( path , & stat ) ;
ops . push_back ( new zkutil : : Op : : Check ( path , stat . version ) ) ;
}
/// Проверяем, что пока мы собирали версии, не ожила реплика с нужным куском.
replica = findReplicaHavingPart ( entry . new_part_name , true ) ;
/// Также за это время могла быть создана совсем новая реплика. Н о если на старых не появится куска, то на новой е г о тоже не может быть.
if ( replica . empty ( ) )
{
Stat quorum_stat ;
String quorum_path = zookeeper_path + " /quorum/status " ;
String quorum_str = zookeeper - > get ( quorum_path , & quorum_stat ) ;
ReplicatedMergeTreeQuorumEntry quorum_entry ;
quorum_entry . fromString ( quorum_str ) ;
if ( quorum_entry . part_name = = entry . new_part_name )
{
ops . push_back ( new zkutil : : Op : : Remove ( quorum_path , quorum_stat . version ) ) ;
const auto partition_str = entry . new_part_name . substr ( 0 , 6 ) ;
ActiveDataPartSet : : Part part_info ;
ActiveDataPartSet : : parsePartName ( entry . new_part_name , part_info ) ;
if ( part_info . left ! = part_info . right )
throw Exception ( " Logical error: log entry with quorum for part covering more than one block number " ,
ErrorCodes : : LOGICAL_ERROR ) ;
2015-09-24 04:52:02 +00:00
auto acl = zookeeper - > getDefaultACL ( ) ;
2015-09-20 11:02:59 +00:00
ops . push_back ( new zkutil : : Op : : Create (
zookeeper_path + " /nonincrement_block_numbers/ " + partition_str + " /block- " + padIndex ( part_info . left ) ,
" " ,
2015-09-24 04:52:02 +00:00
acl ,
2015-09-20 11:02:59 +00:00
zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Create (
zookeeper_path + " /quorum/failed_parts/ " + entry . new_part_name ,
" " ,
2015-09-24 04:52:02 +00:00
acl ,
2015-09-20 11:02:59 +00:00
zkutil : : CreateMode : : Persistent ) ) ;
2015-09-27 10:45:49 +00:00
/// Удаление из blocks.
if ( zookeeper - > exists ( zookeeper_path + " /blocks/ " + entry . block_id ) )
{
ops . push_back ( new zkutil : : Op : : Remove ( zookeeper_path + " /blocks/ " + entry . block_id + " /number " , - 1 ) ) ;
ops . push_back ( new zkutil : : Op : : Remove ( zookeeper_path + " /blocks/ " + entry . block_id + " /checksum " , - 1 ) ) ;
ops . push_back ( new zkutil : : Op : : Remove ( zookeeper_path + " /blocks/ " + entry . block_id , - 1 ) ) ;
}
2015-09-20 11:02:59 +00:00
auto code = zookeeper - > tryMulti ( ops ) ;
if ( code = = ZOK )
{
LOG_DEBUG ( log , " Marked quorum for part " < < entry . new_part_name < < " as failed. " ) ;
return true ; /// NOTE Удаление из virtual_parts не делается, но оно нужно только для мерджей.
}
else if ( code = = ZBADVERSION | | code = = ZNONODE | | code = = ZNODEEXISTS )
{
LOG_DEBUG ( log , " State was changed or isn't expected when trying to mark quorum for part "
< < entry . new_part_name < < " as failed. " ) ;
}
else
throw zkutil : : KeeperException ( code ) ;
}
else
{
LOG_WARNING ( log , " No active replica has part " < < entry . new_part_name
< < " , but that part needs quorum and /quorum/status contains entry about another part " < < quorum_entry . part_name
< < " . It means that part was successfully written to " < < entry . quorum < < " replicas, but then all of them goes offline. "
< < " Or it is a bug. " ) ;
}
}
}
if ( replica . empty ( ) )
{
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartFailedFetches ) ;
throw Exception ( " No active replica has part " + entry . new_part_name , ErrorCodes : : NO_REPLICA_HAS_PART ) ;
}
2014-04-08 17:45:21 +00:00
}
2014-10-18 17:37:55 +00:00
2015-09-11 02:13:59 +00:00
fetchPart ( entry . new_part_name , zookeeper_path + " /replicas/ " + replica , false , entry . quorum ) ;
2014-04-07 15:45:46 +00:00
if ( entry . type = = LogEntry : : MERGE_PARTS )
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartFetchesOfMerged ) ;
}
catch ( . . . )
{
/** Если не получилось скачать кусок, нужный для какого-то мерджа, лучше не пытаться получить другие куски для этого мерджа,
* а п о п ы т а т ь с я с р а з у п о л у ч и т ь п о м е р д ж е н н ы й к у с о к . Ч т о б ы т а к п о л у ч и л о с ь , п е р е м е с т и м д е й с т в и я д л я п о л у ч е н и я о с т а л ь н ы х к у с к о в
* д л я э т о г о м е р д ж а в к о н е ц о ч е р е д и .
*/
try
{
2016-01-10 04:43:30 +00:00
auto parts_for_merge = queue . moveSiblingPartsForMergeToEndOfQueue ( entry . new_part_name ) ;
2014-04-07 15:45:46 +00:00
2016-01-10 04:43:30 +00:00
if ( ! parts_for_merge . empty ( ) & & replica . empty ( ) )
2014-04-07 15:45:46 +00:00
{
2016-01-10 04:43:30 +00:00
LOG_INFO ( log , " No active replica has part " < < entry . new_part_name < < " . Will fetch merged part instead. " ) ;
return false ;
2014-04-07 15:45:46 +00:00
}
2014-07-23 09:15:41 +00:00
2015-09-14 22:45:19 +00:00
/** Если ни у какой активной реплики нет куска, и в очереди нет слияний с е г о участием,
* п р о в е р и м , е с т ь л и у л ю б о й ( а к т и в н о й и л и н е а к т и в н о й ) р е п л и к и т а к о й к у с о к и л и п о к р ы в а ю щ и й е г о .
*/
2014-07-23 09:15:41 +00:00
if ( replica . empty ( ) )
enqueuePartForCheck ( entry . new_part_name ) ;
2014-04-07 15:45:46 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
}
throw ;
}
}
2014-07-18 15:41:04 +00:00
return true ;
2014-04-03 11:48:28 +00:00
}
2014-10-18 17:37:55 +00:00
2014-08-08 08:28:13 +00:00
void StorageReplicatedMergeTree : : executeDropRange ( const StorageReplicatedMergeTree : : LogEntry & entry )
2014-08-05 13:49:44 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-07 09:23:55 +00:00
LOG_INFO ( log , ( entry . detach ? " Detaching " : " Removing " ) < < " parts inside " < < entry . new_part_name < < " . " ) ;
2014-08-05 13:49:44 +00:00
2016-01-10 04:43:30 +00:00
queue . removeGetsAndMergesInRange ( zookeeper , entry . new_part_name ) ;
2014-08-05 13:49:44 +00:00
2014-08-07 09:23:55 +00:00
LOG_DEBUG ( log , ( entry . detach ? " Detaching " : " Removing " ) < < " parts. " ) ;
2014-08-05 13:49:44 +00:00
size_t removed_parts = 0 ;
/// Удалим куски, содержащиеся в удаляемом диапазоне.
auto parts = data . getDataParts ( ) ;
for ( const auto & part : parts )
{
if ( ! ActiveDataPartSet : : contains ( entry . new_part_name , part - > name ) )
continue ;
LOG_DEBUG ( log , " Removing part " < < part - > name ) ;
+ + removed_parts ;
2014-08-07 09:23:55 +00:00
/// Если кусок удалять не нужно, надежнее переместить директорию до изменений в ZooKeeper.
if ( entry . detach )
data . renameAndDetachPart ( part ) ;
2014-08-05 13:49:44 +00:00
zkutil : : Ops ops ;
2015-09-20 11:02:59 +00:00
removePartFromZooKeeper ( part - > name , ops ) ;
2014-08-05 13:49:44 +00:00
zookeeper - > multi ( ops ) ;
2014-08-07 11:46:01 +00:00
/// Если кусок нужно удалить, надежнее удалить директорию после изменений в ZooKeeper.
2014-08-07 09:23:55 +00:00
if ( ! entry . detach )
2014-08-08 08:28:13 +00:00
data . replaceParts ( { part } , { } , true ) ;
2014-08-05 13:49:44 +00:00
}
2014-08-07 11:46:01 +00:00
LOG_INFO ( log , ( entry . detach ? " Detached " : " Removed " ) < < removed_parts < < " parts inside " < < entry . new_part_name < < " . " ) ;
2014-08-08 08:28:13 +00:00
}
2014-10-18 17:37:55 +00:00
2014-08-08 08:28:13 +00:00
bool StorageReplicatedMergeTree : : executeAttachPart ( const StorageReplicatedMergeTree : : LogEntry & entry )
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-08 08:28:13 +00:00
String source_path = ( entry . attach_unreplicated ? " unreplicated/ " : " detached/ " ) + entry . source_part_name ;
LOG_INFO ( log , " Attaching part " < < entry . source_part_name < < " from " < < source_path < < " as " < < entry . new_part_name ) ;
2014-08-08 10:28:46 +00:00
if ( ! Poco : : File ( data . getFullPath ( ) + source_path ) . exists ( ) )
2014-08-08 08:28:13 +00:00
{
2014-08-08 10:28:46 +00:00
LOG_INFO ( log , " No part at " < < source_path < < " . Will fetch it instead " ) ;
2014-08-08 08:28:13 +00:00
return false ;
}
LOG_DEBUG ( log , " Checking data " ) ;
MergeTreeData : : MutableDataPartPtr part = data . loadPartAndFixMetadata ( source_path ) ;
zkutil : : Ops ops ;
checkPartAndAddToZooKeeper ( part , ops , entry . new_part_name ) ;
if ( entry . attach_unreplicated & & unreplicated_data )
{
MergeTreeData : : DataPartPtr unreplicated_part = unreplicated_data - > getPartIfExists ( entry . source_part_name ) ;
if ( unreplicated_part )
unreplicated_data - > detachPartInPlace ( unreplicated_part ) ;
else
LOG_WARNING ( log , " Unreplicated part " < < entry . source_part_name < < " is already detached " ) ;
}
zookeeper - > multi ( ops ) ;
/// NOTE: Н е можем использовать renameTempPartAndAdd, потому что кусок не временный - если что-то пойдет не так, е г о не нужно удалять.
part - > renameTo ( entry . new_part_name ) ;
part - > name = entry . new_part_name ;
ActiveDataPartSet : : parsePartName ( part - > name , * part ) ;
data . attachPart ( part ) ;
LOG_INFO ( log , " Finished attaching part " < < entry . new_part_name ) ;
2014-08-05 13:49:44 +00:00
2014-08-08 09:58:56 +00:00
/// Н а месте удаленных кусков могут появиться новые, с другими данными.
context . resetCaches ( ) ;
2014-08-05 13:49:44 +00:00
return true ;
}
2014-10-18 17:37:55 +00:00
2014-04-03 11:48:28 +00:00
void StorageReplicatedMergeTree : : queueUpdatingThread ( )
{
2015-09-24 18:54:21 +00:00
setThreadName ( " ReplMTQueueUpd " ) ;
2014-04-03 11:48:28 +00:00
while ( ! shutdown_called )
{
2014-04-04 10:37:33 +00:00
try
{
2014-07-15 14:37:49 +00:00
pullLogsToQueue ( queue_updating_event ) ;
queue_updating_event - > wait ( ) ;
2014-04-04 10:37:33 +00:00
}
2014-10-02 23:31:18 +00:00
catch ( const zkutil : : KeeperException & e )
2014-08-08 12:53:55 +00:00
{
if ( e . code = = ZINVALIDSTATE )
2014-10-17 01:05:51 +00:00
restarting_thread - > wakeup ( ) ;
2014-08-08 12:53:55 +00:00
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
queue_updating_event - > tryWait ( ERROR_SLEEP_MS ) ;
}
2014-04-04 10:37:33 +00:00
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2014-07-15 14:37:49 +00:00
queue_updating_event - > tryWait ( ERROR_SLEEP_MS ) ;
}
2014-04-03 11:48:28 +00:00
}
2014-07-15 14:37:49 +00:00
2014-10-06 05:18:17 +00:00
LOG_DEBUG ( log , " Queue updating thread finished " ) ;
2014-04-03 08:47:59 +00:00
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2014-07-02 12:30:38 +00:00
bool StorageReplicatedMergeTree : : queueTask ( BackgroundProcessingPool : : Context & pool_context )
2014-04-03 11:48:28 +00:00
{
2014-08-05 13:49:44 +00:00
LogEntryPtr entry ;
2014-04-03 11:48:28 +00:00
2014-07-02 12:30:38 +00:00
try
{
2016-01-10 04:43:30 +00:00
entry = queue . selectEntryToProcess ( merger ) ;
2014-07-02 12:30:38 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
}
2014-03-21 19:17:59 +00:00
2014-08-05 13:49:44 +00:00
if ( ! entry )
2014-07-02 12:30:38 +00:00
return false ;
2014-03-21 19:17:59 +00:00
2016-01-10 04:43:30 +00:00
bool res = queue . processEntry ( getZooKeeper ( ) , entry , [ & ] ( LogEntryPtr & entry )
2014-07-02 12:30:38 +00:00
{
2015-09-21 22:43:38 +00:00
try
2014-07-18 16:05:57 +00:00
{
2016-01-10 04:43:30 +00:00
return executeLogEntry ( * entry , pool_context ) ;
}
catch ( const Exception & e )
{
if ( e . code ( ) = = ErrorCodes : : NO_REPLICA_HAS_PART )
2015-09-21 22:43:38 +00:00
{
2016-01-10 04:43:30 +00:00
/// Если ни у кого нет нужного куска, наверно, просто не все реплики работают; не будем писать в лог с уровнем Error.
LOG_INFO ( log , e . displayText ( ) ) ;
2015-09-21 22:43:38 +00:00
}
2016-01-10 04:43:30 +00:00
else if ( e . code ( ) = = ErrorCodes : : ABORTED )
{
/// Прерванный мердж или скачивание куска - не ошибка.
LOG_INFO ( log , e . message ( ) ) ;
}
else
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2015-09-21 22:43:38 +00:00
}
catch ( . . . )
{
2014-04-04 10:37:33 +00:00
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2014-08-04 15:48:03 +00:00
}
2016-01-10 04:43:30 +00:00
return false ;
} ) ;
2014-07-02 12:30:38 +00:00
2014-07-18 15:41:04 +00:00
/// Если не было исключения, не нужно спать.
2016-01-10 04:43:30 +00:00
return res ;
2014-04-03 11:48:28 +00:00
}
2014-10-18 17:37:55 +00:00
2014-04-04 10:37:33 +00:00
void StorageReplicatedMergeTree : : mergeSelectingThread ( )
{
2015-09-24 18:54:21 +00:00
setThreadName ( " ReplMTMergeSel " ) ;
2014-07-25 14:31:50 +00:00
bool need_pull = true ;
2014-04-04 10:37:33 +00:00
2014-09-12 00:32:27 +00:00
/** Может много времени тратиться на определение, можно ли мерджить два рядом стоящих куска.
* Д в а р я д о м с т о я щ и х к у с к а м о ж н о м е р д ж и т ь , е с л и в с е н о м е р а б л о к о в м е ж д у и х н о м е р а м и н е и с п о л ь з у ю т с я ( " заброшены " , abandoned ) .
* Э т о з н а ч и т , ч т о м е ж д у э т и м и к у с к а м и н е м о ж е т б ы т ь в с т а в л е н д р у г о й к у с о к .
*
* Н о е с л и н о м е р а с о с е д н и х б л о к о в о т л и ч а ю т с я д о с т а т о ч н о с и л ь н о ( о б ы ч н о , е с л и м е ж д у н и м и м н о г о " заброшенных " б л о к о в ) ,
* т о д е л а е т с я с л и ш к о м м н о г о ч т е н и й и з ZooKeeper , ч т о б ы у з н а т ь , м о ж н о л и и х м е р д ж и т ь .
*
2014-09-12 01:06:38 +00:00
* В о с п о л ь з у е м с я у т в е р ж д е н и е м , ч т о е с л и п а р у к у с к о в б ы л о м о ж н о м е р д ж и т ь , и и х м е р д ж е щ ё н е з а п л а н и р о в а н ,
* т о и с е й ч а с и х м о ж н о м е р д ж и т ь , и б у д е м з а п о м и н а т ь э т о с о с т о я н и е , ч т о б ы н е д е л а т ь м н о г о р а з о д и н а к о в ы е з а п р о с ы в ZooKeeper .
2014-09-12 00:32:27 +00:00
*
* TODO И н т е р е с н о , к а к э т о с о ч е т а е т с я с DROP PARTITION и з а т е м ATTACH PARTITION .
*/
std : : set < std : : pair < std : : string , std : : string > > memoized_parts_that_could_be_merged ;
auto can_merge = [ & memoized_parts_that_could_be_merged , this ]
( const MergeTreeData : : DataPartPtr & left , const MergeTreeData : : DataPartPtr & right ) - > bool
{
2014-09-12 01:06:38 +00:00
/// Если какой-то из кусков уже собираются слить в больший, не соглашаемся е г о сливать.
2016-01-10 04:43:30 +00:00
if ( queue . partWillBeMergedOrMergesDisabled ( left - > name ) | | queue . partWillBeMergedOrMergesDisabled ( right - > name ) )
2014-09-12 01:06:38 +00:00
return false ;
2014-09-12 00:32:27 +00:00
auto key = std : : make_pair ( left - > name , right - > name ) ;
if ( memoized_parts_that_could_be_merged . count ( key ) )
return true ;
2014-09-12 01:06:38 +00:00
String month_name = left - > name . substr ( 0 , 6 ) ;
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-09-12 01:06:38 +00:00
2015-09-11 02:13:59 +00:00
/// Нельзя сливать куски, среди которых находится кусок, для которого неудовлетворён кворум.
/// Замечание: теоретически, это можно было бы разрешить. Н о это сделает логику более сложной.
String quorum_node_value ;
if ( zookeeper - > tryGet ( zookeeper_path + " /quorum/status " , quorum_node_value ) )
{
ReplicatedMergeTreeQuorumEntry quorum_entry ;
quorum_entry . fromString ( quorum_node_value ) ;
ActiveDataPartSet : : Part part_info ;
ActiveDataPartSet : : parsePartName ( quorum_entry . part_name , part_info ) ;
if ( part_info . left ! = part_info . right )
throw Exception ( " Logical error: part written with quorum covers more than one block numbers " , ErrorCodes : : LOGICAL_ERROR ) ;
if ( left - > right < = part_info . left & & right - > left > = part_info . right )
return false ;
}
2014-09-12 01:06:38 +00:00
/// Можно слить куски, если все номера между ними заброшены - не соответствуют никаким блокам.
2015-08-17 21:09:36 +00:00
/// Номера до RESERVED_BLOCK_NUMBERS всегда не соответствуют никаким блокам.
for ( Int64 number = std : : max ( RESERVED_BLOCK_NUMBERS , left - > right + 1 ) ; number < = right - > left - 1 ; + + number )
2014-09-12 01:06:38 +00:00
{
String path1 = zookeeper_path + " /block_numbers/ " + month_name + " /block- " + padIndex ( number ) ;
String path2 = zookeeper_path + " /nonincrement_block_numbers/ " + month_name + " /block- " + padIndex ( number ) ;
2014-09-12 00:32:27 +00:00
2014-09-12 01:06:38 +00:00
if ( AbandonableLockInZooKeeper : : check ( path1 , * zookeeper ) ! = AbandonableLockInZooKeeper : : ABANDONED & &
AbandonableLockInZooKeeper : : check ( path2 , * zookeeper ) ! = AbandonableLockInZooKeeper : : ABANDONED )
return false ;
}
memoized_parts_that_could_be_merged . insert ( key ) ;
return true ;
2014-09-12 00:32:27 +00:00
} ;
2014-04-04 10:37:33 +00:00
while ( ! shutdown_called & & is_leader_node )
{
2014-04-07 18:14:39 +00:00
bool success = false ;
2014-04-04 10:37:33 +00:00
2014-04-07 18:14:39 +00:00
try
2014-04-04 10:37:33 +00:00
{
2014-12-02 19:08:18 +00:00
std : : lock_guard < std : : mutex > merge_selecting_lock ( merge_selecting_mutex ) ;
2014-08-07 09:23:55 +00:00
2014-07-25 14:31:50 +00:00
if ( need_pull )
{
2016-01-10 04:43:30 +00:00
/// Нужно загрузить новые записи в очередь перед тем, как выбирать куски для слияния.
/// (чтобы мы знали, какие куски уже собираются сливать).
2014-07-25 14:31:50 +00:00
pullLogsToQueue ( ) ;
need_pull = false ;
}
2014-09-17 23:57:25 +00:00
/** Сколько в очереди или в фоновом потоке мерджей крупных кусков.
* Е с л и и х б о л ь ш е п о л о в и н ы о т р а з м е р а п у л а п о т о к о в д л я м е р д ж а , т о м о ж н о м е р д ж и т ь т о л ь к о м е л к и е к у с к и .
*/
auto & background_pool = context . getBackgroundPool ( ) ;
size_t big_merges_current = background_pool . getCounter ( " replicated big merges " ) ;
size_t max_number_of_big_merges = background_pool . getNumberOfThreads ( ) / 2 ;
2014-04-07 18:14:39 +00:00
size_t merges_queued = 0 ;
2014-09-17 23:57:25 +00:00
size_t big_merges_queued = 0 ;
2014-04-04 10:37:33 +00:00
2014-09-17 23:57:25 +00:00
if ( big_merges_current < max_number_of_big_merges )
2014-04-07 18:14:39 +00:00
{
2016-01-10 04:43:30 +00:00
queue . countMerges ( merges_queued , big_merges_queued , max_number_of_big_merges - big_merges_current ,
[ & ] ( const String & name )
2014-05-26 11:40:22 +00:00
{
2016-01-10 04:43:30 +00:00
MergeTreeData : : DataPartPtr part = data . getActiveContainingPart ( name ) ;
if ( ! part | | part - > name ! = name )
return false ;
2014-05-26 11:40:22 +00:00
2016-01-10 04:43:30 +00:00
return part - > size_in_bytes > data . settings . max_bytes_to_merge_parts_small ;
} ) ;
2014-04-07 18:14:39 +00:00
}
2014-04-04 10:37:33 +00:00
2014-09-17 23:57:25 +00:00
bool only_small = big_merges_current + big_merges_queued > = max_number_of_big_merges ;
2014-12-21 01:27:56 +00:00
if ( big_merges_current | | merges_queued )
LOG_TRACE ( log , " Currently executing big merges: " < < big_merges_current
< < " . Queued big merges: " < < big_merges_queued
< < " . All merges in queue: " < < merges_queued
< < " . Max number of big merges: " < < max_number_of_big_merges
< < ( only_small ? " . So, will select only small parts to merge. " : " . " ) ) ;
2014-09-17 23:57:25 +00:00
2014-07-02 10:16:49 +00:00
do
2014-04-07 18:14:39 +00:00
{
2014-07-02 12:30:38 +00:00
if ( merges_queued > = data . settings . max_replicated_merges_in_queue )
2014-09-17 23:57:25 +00:00
{
2015-09-19 05:56:40 +00:00
LOG_TRACE ( log , " Number of queued merges ( " < < merges_queued
< < " ) is greater than max_replicated_merges_in_queue ( "
< < data . settings . max_replicated_merges_in_queue < < " ), so won't select new parts to merge. " ) ;
2014-07-02 10:16:49 +00:00
break ;
2014-09-17 23:57:25 +00:00
}
2014-04-04 10:37:33 +00:00
2014-07-02 10:16:49 +00:00
MergeTreeData : : DataPartsVector parts ;
2014-04-04 12:47:57 +00:00
2014-04-04 17:20:45 +00:00
String merged_name ;
2015-06-11 00:35:36 +00:00
size_t disk_space = DiskSpaceMonitor : : getUnreservedFreeSpace ( full_path ) ;
if ( ! merger . selectPartsToMerge ( parts , merged_name , disk_space , false , false , only_small , can_merge )
& & ! merger . selectPartsToMerge ( parts , merged_name , disk_space , true , false , only_small , can_merge ) )
2014-09-17 23:57:25 +00:00
{
2014-07-02 10:16:49 +00:00
break ;
2014-09-17 23:57:25 +00:00
}
2014-04-04 17:20:45 +00:00
2016-01-10 04:43:30 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-01 09:52:55 +00:00
bool all_in_zk = true ;
for ( const auto & part : parts )
{
/// Если о каком-то из кусков нет информации в ZK, не будем сливать.
if ( ! zookeeper - > exists ( replica_path + " /parts/ " + part - > name ) )
{
all_in_zk = false ;
2015-09-19 05:56:40 +00:00
if ( part - > modification_time + MAX_AGE_OF_LOCAL_PART_THAT_WASNT_ADDED_TO_ZOOKEEPER < time ( 0 ) )
{
LOG_WARNING ( log , " Part " < < part - > name < < " (that was selected for merge) "
< < " with age " < < ( time ( 0 ) - part - > modification_time )
< < " seconds exists locally but not in ZooKeeper. "
< < " Won't do merge with that part and will check it. " ) ;
enqueuePartForCheck ( part - > name ) ;
}
2014-08-01 09:52:55 +00:00
}
}
if ( ! all_in_zk )
break ;
2014-07-02 10:16:49 +00:00
LogEntry entry ;
entry . type = LogEntry : : MERGE_PARTS ;
entry . source_replica = replica_name ;
entry . new_part_name = merged_name ;
2015-09-27 14:22:23 +00:00
entry . create_time = time ( 0 ) ;
2014-04-04 17:20:45 +00:00
2014-07-02 10:16:49 +00:00
for ( const auto & part : parts )
entry . parts_to_merge . push_back ( part - > name ) ;
2014-04-04 10:37:33 +00:00
2014-07-25 14:31:50 +00:00
need_pull = true ;
2014-07-02 10:16:49 +00:00
2014-07-25 14:31:50 +00:00
zookeeper - > create ( zookeeper_path + " /log/log- " , entry . toString ( ) , zkutil : : CreateMode : : PersistentSequential ) ;
2014-05-07 13:58:20 +00:00
String month_name = parts [ 0 ] - > name . substr ( 0 , 6 ) ;
for ( size_t i = 0 ; i + 1 < parts . size ( ) ; + + i )
2014-04-04 17:20:45 +00:00
{
2014-05-07 13:58:20 +00:00
/// Уберем больше не нужные отметки о несуществующих блоках.
2015-08-17 21:09:36 +00:00
for ( Int64 number = std : : max ( RESERVED_BLOCK_NUMBERS , parts [ i ] - > right + 1 ) ; number < = parts [ i + 1 ] - > left - 1 ; + + number )
2014-05-07 13:58:20 +00:00
{
2014-07-25 16:32:02 +00:00
zookeeper - > tryRemove ( zookeeper_path + " /block_numbers/ " + month_name + " /block- " + padIndex ( number ) ) ;
zookeeper - > tryRemove ( zookeeper_path + " /nonincrement_block_numbers/ " + month_name + " /block- " + padIndex ( number ) ) ;
2014-05-07 13:58:20 +00:00
}
2014-04-04 17:20:45 +00:00
}
2014-07-02 10:16:49 +00:00
success = true ;
2014-04-04 10:37:33 +00:00
}
2014-09-12 01:06:38 +00:00
while ( false ) ;
2014-04-04 10:37:33 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
}
2014-04-14 10:56:06 +00:00
if ( shutdown_called | | ! is_leader_node )
2014-04-04 10:37:33 +00:00
break ;
2014-04-14 10:56:06 +00:00
if ( ! success )
2014-07-02 10:16:49 +00:00
merge_selecting_event . tryWait ( MERGE_SELECTING_SLEEP_MS ) ;
2014-04-14 10:56:06 +00:00
}
2014-07-15 14:37:49 +00:00
2014-09-12 02:30:01 +00:00
LOG_DEBUG ( log , " Merge selecting thread finished " ) ;
2014-04-14 10:56:06 +00:00
}
2014-04-04 10:37:33 +00:00
2014-07-15 15:51:27 +00:00
void StorageReplicatedMergeTree : : alterThread ( )
{
2015-09-24 18:54:21 +00:00
setThreadName ( " ReplMTAlter " ) ;
2014-07-15 15:51:27 +00:00
bool force_recheck_parts = true ;
while ( ! shutdown_called )
{
try
{
2014-09-26 00:49:50 +00:00
/** Имеем описание столбцов в ZooKeeper, общее для всех реплик (Пример: /clickhouse/tables/02-06/visits/columns),
* а т а к ж е о п и с а н и е с т о л б ц о в в л о к а л ь н о м ф а й л е с м е т а д а н н ы м и ( data . getColumnsList ( ) ) .
*
* Е с л и э т и о п и с а н и я о т л и ч а ю т с я - н у ж н о с д е л а т ь ALTER .
*
* Е с л и з а п о м н е н н а я в е р с и я н о д ы ( columns_version ) о т л и ч а е т с я о т в е р с и и в ZK ,
* т о о п и с а н и е с т о л б ц о в в ZK н е о б я з а т е л ь н о о т л и ч а е т с я о т л о к а л ь н о г о
* - т а к о е м о ж е т б ы т ь п р и ц и к л е и з ALTER - о в , к о т о р ы й в ц е л о м , н и ч е г о н е м е н я е т .
* В э т о м с л у ч а е , н а д о о б н о в и т ь з а п о м н е н н ы й н о м е р в е р с и и ,
* а т а к ж е в с ё - р а в н о п р о в е р и т ь с т р у к т у р у к у с к о в , и , п р и н е о б х о д и м о с т и , с д е л а т ь ALTER .
*
* З а п о м н е н н ы й н о м е р в е р с и и н у ж н о о б н о в и т ь п о с л е о б н о в л е н и я м е т а д а н н ы х , п о д б л о к и р о в к о й .
* Э т о т н о м е р в е р с и и п р о в е р я е т с я н а с о о т в е т с т в и е а к т у а л ь н о м у п р и INSERT - е .
* Т о е с т ь , т а к д о б и в а е м с я , ч т о б ы в с т а в л я л и с ь б л о к и с п р а в и л ь н о й с т р у к т у р о й .
*
* П р и с т а р т е с е р в е р а , м о г б ы т ь н е з а в е р ш ё н п р е д ы д у щ и й ALTER .
* П о э т о м у , в п е р в ы й р а з , н е з а в и с и м о о т и з м е н е н и й , п р о в е р я е м с т р у к т у р у в с е х part - о в ,
* ( П р и м е р : / clickhouse / tables / 02 - 06 / visits / replicas / example02 - 06 - 1. yandex . ru / parts / 20140806 _20140831_131664_134988_3296 / columns )
* и д е л а е м ALTER , е с л и н е о б х о д и м о .
*
* TODO : С л и ш к о м с л о ж н о , в с ё п е р е д е л а т ь .
*/
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-07-15 15:51:27 +00:00
zkutil : : Stat stat ;
2014-10-21 12:11:20 +00:00
const String columns_str = zookeeper - > get ( zookeeper_path + " /columns " , & stat , alter_thread_event ) ;
2015-05-28 03:49:28 +00:00
auto columns_desc = ColumnsDescription < true > : : parse ( columns_str ) ;
2014-11-10 16:16:43 +00:00
auto & columns = columns_desc . columns ;
auto & materialized_columns = columns_desc . materialized ;
auto & alias_columns = columns_desc . alias ;
auto & column_defaults = columns_desc . defaults ;
2014-07-15 15:51:27 +00:00
2014-09-26 00:49:50 +00:00
bool changed_version = ( stat . version ! = columns_version ) ;
2014-07-15 15:51:27 +00:00
{
2015-11-06 17:34:48 +00:00
/// Если потребуется блокировать структуру таблицы, то приостановим мерджи.
std : : unique_ptr < MergeTreeMergeBlocker > merge_blocker ;
std : : unique_ptr < MergeTreeMergeBlocker > unreplicated_merge_blocker ;
2014-09-26 00:49:50 +00:00
2015-11-06 17:34:48 +00:00
if ( changed_version | | force_recheck_parts )
2014-07-15 15:51:27 +00:00
{
2015-11-06 17:34:48 +00:00
merge_blocker = std : : make_unique < MergeTreeMergeBlocker > ( merger ) ;
if ( unreplicated_merger )
unreplicated_merge_blocker = std : : make_unique < MergeTreeMergeBlocker > ( * unreplicated_merger ) ;
}
2014-09-26 00:49:50 +00:00
2015-11-06 17:34:48 +00:00
MergeTreeData : : DataParts parts ;
2014-10-16 13:37:01 +00:00
2015-11-06 17:34:48 +00:00
/// Если описание столбцов изменилось, обновим структуру таблицы локально.
if ( changed_version )
{
LOG_INFO ( log , " Changed version of 'columns' node in ZooKeeper. Waiting for structure write lock. " ) ;
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
auto table_lock = lockStructureForAlter ( ) ;
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
const auto columns_changed = columns ! = data . getColumnsListNonMaterialized ( ) ;
const auto materialized_columns_changed = materialized_columns ! = data . materialized_columns ;
const auto alias_columns_changed = alias_columns ! = data . alias_columns ;
const auto column_defaults_changed = column_defaults ! = data . column_defaults ;
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
if ( columns_changed | | materialized_columns_changed | | alias_columns_changed | |
column_defaults_changed )
2014-10-21 12:11:20 +00:00
{
2015-11-06 17:34:48 +00:00
LOG_INFO ( log , " Columns list changed in ZooKeeper. Applying changes locally. " ) ;
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
InterpreterAlterQuery : : updateMetadata ( database_name , table_name , columns ,
materialized_columns , alias_columns , column_defaults , context ) ;
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
if ( columns_changed )
{
data . setColumnsList ( columns ) ;
2014-09-26 00:49:50 +00:00
2015-11-06 17:34:48 +00:00
if ( unreplicated_data )
unreplicated_data - > setColumnsList ( columns ) ;
}
2014-09-26 00:49:50 +00:00
2015-11-06 17:34:48 +00:00
if ( materialized_columns_changed )
{
this - > materialized_columns = materialized_columns ;
data . materialized_columns = std : : move ( materialized_columns ) ;
}
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
if ( alias_columns_changed )
{
this - > alias_columns = alias_columns ;
data . alias_columns = std : : move ( alias_columns ) ;
}
2014-09-26 00:49:50 +00:00
2015-11-06 17:34:48 +00:00
if ( column_defaults_changed )
{
this - > column_defaults = column_defaults ;
data . column_defaults = std : : move ( column_defaults ) ;
}
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
LOG_INFO ( log , " Applied changes to table. " ) ;
}
else
{
LOG_INFO ( log , " Columns version changed in ZooKeeper, but data wasn't changed. It's like cyclic ALTERs. " ) ;
}
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
/// Нужно получить список кусков под блокировкой таблицы, чтобы избежать race condition с мерджем.
2014-07-28 14:31:07 +00:00
parts = data . getDataParts ( ) ;
2015-11-06 17:34:48 +00:00
columns_version = stat . version ;
}
2014-10-21 12:11:20 +00:00
2015-11-06 17:34:48 +00:00
/// Обновим куски.
if ( changed_version | | force_recheck_parts )
2014-07-15 15:51:27 +00:00
{
2015-11-06 17:34:48 +00:00
auto table_lock = lockStructure ( false ) ;
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
if ( changed_version )
LOG_INFO ( log , " ALTER-ing parts " ) ;
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
int changed_parts = 0 ;
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
if ( ! changed_version )
parts = data . getDataParts ( ) ;
2014-07-15 15:51:27 +00:00
2015-11-06 17:34:48 +00:00
const auto columns_plus_materialized = data . getColumnsList ( ) ;
2014-07-16 09:32:15 +00:00
for ( const MergeTreeData : : DataPartPtr & part : parts )
{
2015-11-06 17:34:48 +00:00
/// Обновим кусок и запишем результат во временные файлы.
/// TODO: Можно пропускать проверку на слишком большие изменения, если в ZooKeeper есть, например,
/// нода /flags/force_alter.
auto transaction = data . alterDataPart ( part , columns_plus_materialized ) ;
2014-07-16 09:32:15 +00:00
if ( ! transaction )
continue ;
+ + changed_parts ;
2015-11-06 17:34:48 +00:00
/// Обновим метаданные куска в ZooKeeper.
zkutil : : Ops ops ;
ops . push_back ( new zkutil : : Op : : SetData (
replica_path + " /parts/ " + part - > name + " /columns " , transaction - > getNewColumns ( ) . toString ( ) , - 1 ) ) ;
ops . push_back ( new zkutil : : Op : : SetData (
replica_path + " /parts/ " + part - > name + " /checksums " , transaction - > getNewChecksums ( ) . toString ( ) , - 1 ) ) ;
zookeeper - > multi ( ops ) ;
/// Применим изменения файлов.
2014-07-16 09:32:15 +00:00
transaction - > commit ( ) ;
}
2014-07-16 08:58:59 +00:00
2015-11-06 17:34:48 +00:00
/// Т о же самое для нереплицируемых данных.
if ( unreplicated_data )
{
parts = unreplicated_data - > getDataParts ( ) ;
2014-07-16 08:58:59 +00:00
2015-11-06 17:34:48 +00:00
for ( const MergeTreeData : : DataPartPtr & part : parts )
{
auto transaction = unreplicated_data - > alterDataPart ( part , columns_plus_materialized ) ;
if ( ! transaction )
continue ;
+ + changed_parts ;
transaction - > commit ( ) ;
}
}
/// Список столбцов для конкретной реплики.
zookeeper - > set ( replica_path + " /columns " , columns_str ) ;
if ( changed_version )
{
if ( changed_parts ! = 0 )
LOG_INFO ( log , " ALTER-ed " < < changed_parts < < " parts " ) ;
else
LOG_INFO ( log , " No parts ALTER-ed " ) ;
}
force_recheck_parts = false ;
2014-09-26 00:49:50 +00:00
}
2015-11-06 17:34:48 +00:00
/// Важно, что уничтожается parts и merge_blocker перед wait-ом.
2014-07-15 15:51:27 +00:00
}
alter_thread_event - > wait ( ) ;
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
force_recheck_parts = true ;
alter_thread_event - > tryWait ( ERROR_SLEEP_MS ) ;
}
}
2014-09-26 00:49:50 +00:00
LOG_DEBUG ( log , " Alter thread finished " ) ;
2014-07-15 15:51:27 +00:00
}
2014-10-18 17:37:55 +00:00
2015-09-20 11:02:59 +00:00
void StorageReplicatedMergeTree : : removePartFromZooKeeper ( const String & part_name , zkutil : : Ops & ops )
{
String part_path = replica_path + " /parts/ " + part_name ;
ops . push_back ( new zkutil : : Op : : Remove ( part_path + " /checksums " , - 1 ) ) ;
ops . push_back ( new zkutil : : Op : : Remove ( part_path + " /columns " , - 1 ) ) ;
ops . push_back ( new zkutil : : Op : : Remove ( part_path , - 1 ) ) ;
}
2014-07-22 13:49:52 +00:00
void StorageReplicatedMergeTree : : removePartAndEnqueueFetch ( const String & part_name )
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-07-22 13:49:52 +00:00
String part_path = replica_path + " /parts/ " + part_name ;
2014-08-05 13:49:44 +00:00
LogEntryPtr log_entry = new LogEntry ;
log_entry - > type = LogEntry : : GET_PART ;
2015-09-20 05:50:15 +00:00
log_entry - > create_time = tryGetPartCreateTime ( zookeeper , replica_path , part_name ) ;
2014-08-05 13:49:44 +00:00
log_entry - > source_replica = " " ;
log_entry - > new_part_name = part_name ;
2014-07-22 13:49:52 +00:00
zkutil : : Ops ops ;
ops . push_back ( new zkutil : : Op : : Create (
2014-08-05 13:49:44 +00:00
replica_path + " /queue/queue- " , log_entry - > toString ( ) , zookeeper - > getDefaultACL ( ) ,
2014-07-22 13:49:52 +00:00
zkutil : : CreateMode : : PersistentSequential ) ) ;
2015-09-20 11:02:59 +00:00
removePartFromZooKeeper ( part_name , ops ) ;
2014-07-22 13:49:52 +00:00
auto results = zookeeper - > multi ( ops ) ;
2016-01-10 04:43:30 +00:00
String path_created = dynamic_cast < zkutil : : Op : : Create & > ( ops [ 0 ] ) . getPathCreated ( ) ;
log_entry - > znode_name = path_created . substr ( path_created . find_last_of ( ' / ' ) + 1 ) ;
queue . insert ( log_entry ) ;
2014-07-22 13:49:52 +00:00
}
2014-10-18 17:37:55 +00:00
2014-07-22 13:49:52 +00:00
void StorageReplicatedMergeTree : : enqueuePartForCheck ( const String & name )
{
2014-10-06 05:18:17 +00:00
std : : lock_guard < std : : mutex > lock ( parts_to_check_mutex ) ;
2014-07-22 13:49:52 +00:00
if ( parts_to_check_set . count ( name ) )
return ;
parts_to_check_queue . push_back ( name ) ;
parts_to_check_set . insert ( name ) ;
parts_to_check_event . set ( ) ;
}
2015-09-15 01:20:40 +00:00
void StorageReplicatedMergeTree : : searchForMissingPart ( const String & part_name )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
auto zookeeper = getZooKeeper ( ) ;
String part_path = replica_path + " /parts/ " + part_name ;
/// Если кусок есть в ZooKeeper, удалим е г о оттуда и добавим в очередь задание скачать е г о .
if ( zookeeper - > exists ( part_path ) )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
LOG_WARNING ( log , " Checker: Part " < < part_name < < " exists in ZooKeeper but not locally. "
" Removing from ZooKeeper and queueing a fetch. " ) ;
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartChecksFailed ) ;
2014-12-12 20:50:32 +00:00
2015-09-15 01:20:40 +00:00
removePartAndEnqueueFetch ( part_name ) ;
return ;
}
/// Если куска нет в ZooKeeper, проверим есть ли он хоть у кого-то.
ActiveDataPartSet : : Part part_info ;
ActiveDataPartSet : : parsePartName ( part_name , part_info ) ;
/** Логика такая:
* - е с л и у к а к о й - т о ж и в о й и л и н е а к т и в н о й р е п л и к и е с т ь т а к о й к у с о к , и л и п о к р ы в а ю щ и й е г о к у с о к
* - в с ё О к , н и ч е г о д е л а т ь н е н у ж н о , о н с к а ч а е т с я з а т е м п р и о б р а б о т к е о ч е р е д и , к о г д а р е п л и к а о ж и в ё т ;
* - и л и , е с л и р е п л и к а н и к о г д а н е о ж и в ё т , т о а д м и н и с т р а т о р у д а л и т и л и с о з д а с т н о в у ю р е п л и к у с т е м ж е а д р е с о м и с м . в с ё с н а ч а л а ;
* - е с л и н и у к о г о н е т т а к о г о и л и п о к р ы в а ю щ е г о е г о к у с к а , т о
* - е с л и у к о г о - т о е с т ь в с е с о с т а в л я ю щ и е к у с к и , т о н и ч е г о д е л а т ь н е б у д е м - э т о п р о с т о з н а ч и т , ч т о д р у г и е р е п л и к и е щ ё н е д о д е л а л и м е р д ж
* - е с л и н и у к о г о н е т в с е х с о с т а в л я ю щ и х к у с к о в , т о п р и з н а е м к у с о к н а в е ч н о п о т е р я н н ы м ,
* и у д а л и м з а п и с ь и з о ч е р е д и р е п л и к а ц и и .
*/
LOG_WARNING ( log , " Checker: Checking if anyone has part covering " < < part_name < < " . " ) ;
bool found = false ;
size_t part_length_in_blocks = part_info . right + 1 - part_info . left ;
std : : vector < char > found_blocks ( part_length_in_blocks ) ;
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
for ( const String & replica : replicas )
{
Strings parts = zookeeper - > getChildren ( zookeeper_path + " /replicas/ " + replica + " /parts " ) ;
for ( const String & part_on_replica : parts )
{
if ( part_on_replica = = part_name | | ActiveDataPartSet : : contains ( part_on_replica , part_name ) )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
found = true ;
LOG_WARNING ( log , " Checker: Found part " < < part_on_replica < < " on " < < replica ) ;
break ;
2014-07-21 10:05:56 +00:00
}
2015-09-15 01:20:40 +00:00
if ( ActiveDataPartSet : : contains ( part_name , part_on_replica ) )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
ActiveDataPartSet : : Part part_on_replica_info ;
ActiveDataPartSet : : parsePartName ( part_on_replica , part_on_replica_info ) ;
2015-09-16 20:38:50 +00:00
for ( auto block_num = part_on_replica_info . left ; block_num < = part_on_replica_info . right ; + + block_num )
2015-09-16 20:40:01 +00:00
found_blocks . at ( block_num - part_info . left ) = 1 ;
2014-07-21 10:05:56 +00:00
}
2015-09-15 01:20:40 +00:00
}
if ( found )
break ;
}
2014-07-21 10:05:56 +00:00
2015-09-15 01:20:40 +00:00
if ( found )
{
/// Н а какой-то живой или мёртвой реплике есть нужный кусок или покрывающий е г о .
return ;
}
2014-07-21 10:05:56 +00:00
2015-09-15 01:20:40 +00:00
size_t num_found_blocks = 0 ;
for ( auto found_block : found_blocks )
num_found_blocks + = ( found_block = = 1 ) ;
2014-07-21 10:05:56 +00:00
2015-09-15 01:20:40 +00:00
if ( num_found_blocks = = part_length_in_blocks )
{
/// Н а совокупности живых или мёртвых реплик есть все куски, из которых можно составить нужный кусок. Ничего делать не будем.
LOG_WARNING ( log , " Checker: Found all blocks for missing part. Will wait for them to be merged. " ) ;
return ;
}
2015-09-14 22:45:19 +00:00
2015-09-15 01:20:40 +00:00
/// Ни у кого нет такого куска.
LOG_ERROR ( log , " Checker: No replica has part covering " < < part_name ) ;
2014-07-21 10:05:56 +00:00
2015-09-15 01:20:40 +00:00
if ( num_found_blocks ! = 0 )
LOG_WARNING ( log , " When looking for smaller parts, that is covered by " < < part_name
< < " , we found just " < < num_found_blocks < < " of " < < part_length_in_blocks < < " blocks. " ) ;
2014-07-23 11:02:22 +00:00
2015-09-15 01:20:40 +00:00
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartChecksFailed ) ;
2015-09-14 22:45:19 +00:00
2015-09-15 01:20:40 +00:00
/// Есть ли он в очереди репликации? Если есть - удалим, так как задачу невозможно обработать.
2016-01-10 04:43:30 +00:00
if ( ! queue . remove ( zookeeper , part_name ) )
2015-09-15 01:20:40 +00:00
{
/// Куска не было в нашей очереди. С чего бы это?
LOG_ERROR ( log , " Checker: Missing part " < < part_name < < " is not in our queue. " ) ;
return ;
}
2014-07-22 13:49:52 +00:00
2015-09-15 01:20:40 +00:00
/** Такая ситуация возможна, если на всех репликах, где был кусок, он испортился.
* Н а п р и м е р , у р е п л и к и , к о т о р а я т о л ь к о ч т о е г о з а п и с а л а , о т к л ю ч и л и п и т а н и е , и д а н н ы е н е з а п и с а л и с ь и з к е ш а н а д и с к .
*/
LOG_ERROR ( log , " Checker: Part " < < part_name < < " is lost forever. " ) ;
ProfileEvents : : increment ( ProfileEvents : : ReplicatedDataLoss ) ;
/** Нужно добавить отсутствующий кусок в block_numbers, чтобы он не мешал слияниям.
* В о т т о л ь к о в с а м block_numbers м ы е г о д о б а в и т ь н е м о ж е м - е с л и т а к с д е л а т ь ,
* ZooKeeper з а ч е м - т о п р о п у с т и т о д и н н о м е р д л я а в т о и н к р е м е н т а ,
* и в н о м е р а х б л о к о в в с е р а в н о о с т а н е т с я д ы р к а .
* С п е ц и а л ь н о и з - з а э т о г о п р и х о д и т с я о т д е л ь н о и м е т ь nonincrement_block_numbers .
*
* К с т а т и , е с л и м ы з д е с ь с д о х н е м , т о с л и я н и я н е б у д у т д е л а т ь с я с к в о з ь э т и о т с у т с т в у ю щ и е к у с к и .
2015-09-23 02:39:07 +00:00
*
* А е щ ё , н е б у д е м д о б а в л я т ь , е с л и :
* - п о т р е б о в а л о с ь б ы с о з д а т ь с л и ш к о м м н о г о ( б о л ь ш е 1000 ) у з л о в ;
* - к у с о к я в л я е т с я п е р в ы м в п а р т и ц и и и л и б ы л п р и - ATTACH - е н .
* NOTE В о з м о ж н о , д о б а в и т ь т а к ж е у с л о в и е , е с л и з а п и с ь в о ч е р е д и о ч е н ь с т а р а я .
2015-09-15 01:20:40 +00:00
*/
2015-09-23 02:39:07 +00:00
if ( part_length_in_blocks > 1000 )
{
LOG_ERROR ( log , " Won't add nonincrement_block_numbers because part spans too much blocks ( " < < part_length_in_blocks < < " ) " ) ;
return ;
}
if ( part_info . left < = RESERVED_BLOCK_NUMBERS )
{
LOG_ERROR ( log , " Won't add nonincrement_block_numbers because part is one of first in partition " ) ;
return ;
}
2015-09-15 01:20:40 +00:00
const auto partition_str = part_name . substr ( 0 , 6 ) ;
for ( auto i = part_info . left ; i < = part_info . right ; + + i )
{
zookeeper - > createIfNotExists ( zookeeper_path + " /nonincrement_block_numbers " , " " ) ;
zookeeper - > createIfNotExists ( zookeeper_path + " /nonincrement_block_numbers/ " + partition_str , " " ) ;
AbandonableLockInZooKeeper : : createAbandonedIfNotExists (
zookeeper_path + " /nonincrement_block_numbers/ " + partition_str + " /block- " + padIndex ( i ) ,
* zookeeper ) ;
}
}
2014-07-23 11:02:22 +00:00
2015-09-15 01:20:40 +00:00
void StorageReplicatedMergeTree : : checkPart ( const String & part_name )
{
LOG_WARNING ( log , " Checker: Checking part " < < part_name ) ;
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartChecks ) ;
2014-07-25 16:09:58 +00:00
2015-09-15 01:20:40 +00:00
auto part = data . getActiveContainingPart ( part_name ) ;
2014-07-23 11:02:22 +00:00
2015-09-15 01:20:40 +00:00
/// Этого или покрывающего куска у нас нет.
if ( ! part )
{
searchForMissingPart ( part_name ) ;
}
/// У нас есть этот кусок, и он активен. Будем проверять, нужен ли нам этот кусок и правильные ли у него данные.
else if ( part - > name = = part_name )
{
auto zookeeper = getZooKeeper ( ) ;
auto table_lock = lockStructure ( false ) ;
2014-07-25 16:09:58 +00:00
2015-09-15 01:20:40 +00:00
/// Если кусок есть в ZooKeeper, сверим е г о данные с е г о чексуммами, а их с ZooKeeper.
if ( zookeeper - > exists ( replica_path + " /parts/ " + part_name ) )
{
LOG_WARNING ( log , " Checker: Checking data of part " < < part_name < < " . " ) ;
try
{
auto zk_checksums = MergeTreeData : : DataPart : : Checksums : : parse (
zookeeper - > get ( replica_path + " /parts/ " + part_name + " /checksums " ) ) ;
zk_checksums . checkEqual ( part - > checksums , true ) ;
auto zk_columns = NamesAndTypesList : : parse (
zookeeper - > get ( replica_path + " /parts/ " + part_name + " /columns " ) ) ;
if ( part - > columns ! = zk_columns )
throw Exception ( " Columns of local part " + part_name + " are different from ZooKeeper " ) ;
MergeTreePartChecker : : Settings settings ;
settings . setIndexGranularity ( data . index_granularity ) ;
settings . setRequireChecksums ( true ) ;
settings . setRequireColumnFiles ( true ) ;
MergeTreePartChecker : : checkDataPart (
2015-11-29 08:06:29 +00:00
data . getFullPath ( ) + part_name , settings , data . primary_key_data_types ) ;
2015-09-15 01:20:40 +00:00
LOG_INFO ( log , " Checker: Part " < < part_name < < " looks good. " ) ;
2014-07-21 10:05:56 +00:00
}
2015-09-15 01:20:40 +00:00
catch ( . . . )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2014-07-28 14:31:07 +00:00
2015-09-15 01:20:40 +00:00
LOG_ERROR ( log , " Checker: Part " < < part_name < < " looks broken. Removing it and queueing a fetch. " ) ;
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartChecksFailed ) ;
2014-07-22 13:49:52 +00:00
2015-09-15 01:20:40 +00:00
removePartAndEnqueueFetch ( part_name ) ;
2014-07-22 13:49:52 +00:00
2015-09-15 01:20:40 +00:00
/// Удалим кусок локально.
data . renameAndDetachPart ( part , " broken_ " ) ;
}
}
2015-09-19 05:56:40 +00:00
else if ( part - > modification_time + MAX_AGE_OF_LOCAL_PART_THAT_WASNT_ADDED_TO_ZOOKEEPER < time ( 0 ) )
2015-09-15 01:20:40 +00:00
{
/// Если куска нет в ZooKeeper, удалим е г о локально.
/// Возможно, кусок кто-то только что записал, и еще не успел добавить в ZK.
/// Поэтому удаляем только если кусок старый (не очень надежно).
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartChecksFailed ) ;
2015-09-19 05:56:40 +00:00
LOG_ERROR ( log , " Checker: Unexpected part " < < part_name < < " in filesystem. Removing. " ) ;
2015-09-15 01:20:40 +00:00
data . renameAndDetachPart ( part , " unexpected_ " ) ;
}
2015-09-19 05:56:40 +00:00
else
{
LOG_TRACE ( log , " Checker: Young part " < < part_name
< < " with age " < < ( time ( 0 ) - part - > modification_time )
< < " seconds hasn't been added to ZooKeeper yet. It's ok. " ) ;
}
2015-09-15 01:20:40 +00:00
}
else
{
/// Если у нас есть покрывающий кусок, игнорируем все проблемы с этим куском.
/// В худшем случае в лог еще old_parts_lifetime секунд будут валиться ошибки, пока кусок не удалится как старый.
LOG_WARNING ( log , " Checker: We have part " < < part - > name < < " covering part " < < part_name ) ;
}
}
2014-07-22 13:49:52 +00:00
2015-09-15 01:20:40 +00:00
void StorageReplicatedMergeTree : : partCheckThread ( )
{
2015-09-24 18:54:21 +00:00
setThreadName ( " ReplMTPartCheck " ) ;
2015-09-15 01:20:40 +00:00
while ( ! shutdown_called )
{
try
{
/// Достанем из очереди кусок для проверки.
String part_name ;
{
std : : lock_guard < std : : mutex > lock ( parts_to_check_mutex ) ;
if ( parts_to_check_queue . empty ( ) )
{
if ( ! parts_to_check_set . empty ( ) )
{
LOG_ERROR ( log , " Checker: Non-empty parts_to_check_set with empty parts_to_check_queue. This is a bug. " ) ;
parts_to_check_set . clear ( ) ;
2014-07-22 13:49:52 +00:00
}
2014-07-21 10:05:56 +00:00
}
2015-09-15 01:20:40 +00:00
else
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
part_name = parts_to_check_queue . front ( ) ;
2014-07-21 10:05:56 +00:00
}
}
2015-09-15 01:20:40 +00:00
if ( part_name . empty ( ) )
2014-07-21 10:05:56 +00:00
{
2015-09-15 01:20:40 +00:00
parts_to_check_event . wait ( ) ;
continue ;
2014-07-21 10:05:56 +00:00
}
2015-09-15 01:20:40 +00:00
checkPart ( part_name ) ;
/// Удалим кусок из очереди проверок.
2014-07-21 10:05:56 +00:00
{
2014-10-06 05:18:17 +00:00
std : : lock_guard < std : : mutex > lock ( parts_to_check_mutex ) ;
2014-07-21 10:05:56 +00:00
if ( parts_to_check_queue . empty ( ) | | parts_to_check_queue . front ( ) ! = part_name )
{
2015-09-15 01:20:40 +00:00
LOG_ERROR ( log , " Checker: Someone changed parts_to_check_queue.front(). This is a bug. " ) ;
2014-07-21 10:05:56 +00:00
}
else
{
parts_to_check_queue . pop_front ( ) ;
parts_to_check_set . erase ( part_name ) ;
}
}
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
parts_to_check_event . tryWait ( ERROR_SLEEP_MS ) ;
}
}
}
2014-04-04 10:37:33 +00:00
void StorageReplicatedMergeTree : : becomeLeader ( )
{
2014-04-04 12:47:57 +00:00
LOG_INFO ( log , " Became leader " ) ;
2014-04-04 10:37:33 +00:00
is_leader_node = true ;
merge_selecting_thread = std : : thread ( & StorageReplicatedMergeTree : : mergeSelectingThread , this ) ;
}
2014-10-18 17:37:55 +00:00
2014-04-08 17:45:21 +00:00
String StorageReplicatedMergeTree : : findReplicaHavingPart ( const String & part_name , bool active )
2014-04-03 11:48:28 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-05-13 10:10:26 +00:00
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
2014-04-03 11:48:28 +00:00
/// Из реплик, у которых есть кусок, выберем одну равновероятно.
std : : random_shuffle ( replicas . begin ( ) , replicas . end ( ) ) ;
for ( const String & replica : replicas )
{
2014-05-13 10:10:26 +00:00
if ( zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica + " /parts/ " + part_name ) & &
( ! active | | zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica + " /is_active " ) ) )
2014-04-03 11:48:28 +00:00
return replica ;
2015-09-15 01:20:40 +00:00
/// Конечно, реплика может перестать быть активной или даже перестать существовать после возврата из этой функции.
2014-04-03 11:48:28 +00:00
}
2014-04-08 17:45:21 +00:00
return " " ;
2014-04-03 11:48:28 +00:00
}
2014-10-18 17:37:55 +00:00
2015-09-11 02:13:59 +00:00
/** Если для куска отслеживается кворум, то обновить информацию о нём в ZK.
*/
2015-09-20 11:02:59 +00:00
void StorageReplicatedMergeTree : : updateQuorum ( const String & part_name )
2015-09-11 02:13:59 +00:00
{
2015-09-20 11:02:59 +00:00
auto zookeeper = getZooKeeper ( ) ;
2015-09-11 02:13:59 +00:00
2015-09-20 11:02:59 +00:00
/// Информация, на какие реплики был добавлен кусок, если кворум ещё не достигнут.
2015-09-11 02:13:59 +00:00
const String quorum_status_path = zookeeper_path + " /quorum/status " ;
2015-09-20 11:02:59 +00:00
/// Имя предыдущего куска, для которого был достигнут кворум.
const String quorum_last_part_path = zookeeper_path + " /quorum/last_part " ;
2015-09-11 02:13:59 +00:00
String value ;
zkutil : : Stat stat ;
/// Если узла нет, значит по всем кворумным INSERT-ам уже был достигнут кворум, и ничего делать не нужно.
while ( zookeeper - > tryGet ( quorum_status_path , value , & stat ) )
{
ReplicatedMergeTreeQuorumEntry quorum_entry ;
quorum_entry . fromString ( value ) ;
if ( quorum_entry . part_name ! = part_name )
{
/// Кворум уже был достигнут. Более того, уже начался другой INSERT с кворумом.
break ;
}
quorum_entry . replicas . insert ( replica_name ) ;
if ( quorum_entry . replicas . size ( ) > = quorum_entry . required_number_of_replicas )
{
2015-09-20 11:02:59 +00:00
/// Кворум достигнут. Удаляем узел, а также обновляем информацию о последнем куске, который был успешно записан с кворумом.
zkutil : : Ops ops ;
ops . push_back ( new zkutil : : Op : : Remove ( quorum_status_path , stat . version ) ) ;
ops . push_back ( new zkutil : : Op : : SetData ( quorum_last_part_path , part_name , - 1 ) ) ;
auto code = zookeeper - > tryMulti ( ops ) ;
2015-09-11 02:13:59 +00:00
2015-09-11 02:51:35 +00:00
if ( code = = ZOK )
{
break ;
}
else if ( code = = ZNONODE )
2015-09-11 02:13:59 +00:00
{
/// Кворум уже был достигнут.
break ;
}
else if ( code = = ZBADVERSION )
{
/// Узел успели обновить. Надо заново е г о прочитать и повторить все действия.
continue ;
}
else
throw zkutil : : KeeperException ( code , quorum_status_path ) ;
}
else
{
/// Обновляем узел, прописывая туда на одну реплику больше.
auto code = zookeeper - > trySet ( quorum_status_path , quorum_entry . toString ( ) , stat . version ) ;
2015-09-11 02:51:35 +00:00
if ( code = = ZOK )
{
break ;
}
else if ( code = = ZNONODE )
2015-09-11 02:13:59 +00:00
{
/// Кворум уже был достигнут.
break ;
}
else if ( code = = ZBADVERSION )
{
/// Узел успели обновить. Надо заново е г о прочитать и повторить все действия.
continue ;
}
else
throw zkutil : : KeeperException ( code , quorum_status_path ) ;
}
}
}
void StorageReplicatedMergeTree : : fetchPart ( const String & part_name , const String & replica_path , bool to_detached , size_t quorum )
2014-04-03 11:48:28 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-10-13 17:28:59 +00:00
LOG_DEBUG ( log , " Fetching part " < < part_name < < " from " < < replica_path ) ;
2014-04-04 12:47:57 +00:00
2014-10-13 17:28:59 +00:00
TableStructureReadLockPtr table_lock ;
if ( ! to_detached )
table_lock = lockStructure ( true ) ;
2014-04-03 11:48:28 +00:00
2015-10-02 18:33:46 +00:00
ReplicatedMergeTreeAddress address ( zookeeper - > get ( replica_path + " /host " ) ) ;
2014-04-03 11:48:28 +00:00
2015-10-02 18:33:46 +00:00
MergeTreeData : : MutableDataPartPtr part = fetcher . fetchPart ( part_name , replica_path , address . host , address . replication_port , to_detached ) ;
2014-07-01 15:58:25 +00:00
2014-10-13 17:28:59 +00:00
if ( ! to_detached )
{
zkutil : : Ops ops ;
checkPartAndAddToZooKeeper ( part , ops , part_name ) ;
2014-08-08 10:41:53 +00:00
2014-10-13 17:28:59 +00:00
MergeTreeData : : Transaction transaction ;
auto removed_parts = data . renameTempPartAndReplace ( part , nullptr , & transaction ) ;
2014-04-07 15:45:46 +00:00
2014-10-13 17:28:59 +00:00
zookeeper - > multi ( ops ) ;
transaction . commit ( ) ;
2015-09-11 02:13:59 +00:00
/** Если для этого куска отслеживается кворум, то надо е г о обновить.
2015-09-20 11:02:59 +00:00
* Е с л и н е у с п е е м , в с л у ч а е п о т е р и с е с с и и , п р и п е р е з а п у с к е с е р в е р а - с м . м е т о д ReplicatedMergeTreeRestartingThread : : updateQuorumIfWeHavePart .
2015-09-11 02:13:59 +00:00
*/
2015-09-20 11:02:59 +00:00
if ( quorum )
updateQuorum ( part_name ) ;
2015-09-11 02:13:59 +00:00
2014-10-13 17:28:59 +00:00
merge_selecting_event . set ( ) ;
2014-04-09 15:52:47 +00:00
2014-10-13 17:28:59 +00:00
for ( const auto & removed_part : removed_parts )
{
LOG_DEBUG ( log , " Part " < < removed_part - > name < < " is rendered obsolete by fetching part " < < part_name ) ;
ProfileEvents : : increment ( ProfileEvents : : ObsoleteReplicatedParts ) ;
}
}
else
2014-04-07 18:14:39 +00:00
{
2014-10-13 17:28:59 +00:00
Poco : : File ( data . getFullPath ( ) + " detached/tmp_ " + part_name ) . renameTo ( data . getFullPath ( ) + " detached/ " + part_name ) ;
2014-04-07 18:14:39 +00:00
}
2014-04-07 15:45:46 +00:00
ProfileEvents : : increment ( ProfileEvents : : ReplicatedPartFetches ) ;
2015-09-19 08:19:25 +00:00
LOG_DEBUG ( log , " Fetched part " < < part_name < < " from " < < replica_path < < ( to_detached ? " (to 'detached' directory) " : " " ) ) ;
2014-04-03 11:48:28 +00:00
}
2014-03-21 19:17:59 +00:00
2014-10-18 17:37:55 +00:00
2014-03-22 14:44:44 +00:00
void StorageReplicatedMergeTree : : shutdown ( )
{
2014-10-17 01:05:51 +00:00
if ( restarting_thread )
2014-05-26 11:40:22 +00:00
{
2014-10-17 01:05:51 +00:00
restarting_thread - > stop ( ) ;
restarting_thread . reset ( ) ;
2014-05-26 11:40:22 +00:00
}
2014-07-15 14:37:49 +00:00
endpoint_holder = nullptr ;
2015-12-24 21:28:18 +00:00
fetcher . cancel ( ) ;
2014-04-25 13:55:15 +00:00
}
2014-03-22 14:44:44 +00:00
StorageReplicatedMergeTree : : ~ StorageReplicatedMergeTree ( )
{
try
{
shutdown ( ) ;
}
catch ( . . . )
{
2014-10-17 01:05:51 +00:00
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
2014-03-22 14:44:44 +00:00
}
}
2014-10-18 17:37:55 +00:00
2014-03-22 14:44:44 +00:00
BlockInputStreams StorageReplicatedMergeTree : : read (
2014-12-17 11:53:17 +00:00
const Names & column_names ,
ASTPtr query ,
const Context & context ,
const Settings & settings ,
QueryProcessingStage : : Enum & processed_stage ,
const size_t max_block_size ,
const unsigned threads )
2014-03-22 14:44:44 +00:00
{
2015-09-21 22:33:20 +00:00
/** У таблицы может быть два вида данных:
* - р е п л и ц и р у е м ы е д а н н ы е ;
* - с т а р ы е , н е р е п л и ц и р у е м ы е д а н н ы е - о н и л е ж а т о т д е л ь н о и и х ц е л о с т н о с т ь н и к а к н е к о н т р о л и р у е т с я .
* А е щ ё д в и ж о к т а б л и ц ы п р е д о с т а в л я е т в о з м о ж н о с т ь и с п о л ь з о в а т ь " виртуальные столбцы " .
* О д и н и з н и х - _replicated п о з в о л я е т о п р е д е л и т ь , и з к а к о й ч а с т и п р о ч и т а н ы д а н н ы е ,
* и л и , п р и и с п о л ь з о в а н и и в WHERE - в ы б р а т ь д а н н ы е т о л ь к о и з о д н о й ч а с т и .
*/
2014-07-28 09:53:57 +00:00
Names virt_column_names ;
Names real_column_names ;
for ( const auto & it : column_names )
if ( it = = " _replicated " )
virt_column_names . push_back ( it ) ;
else
real_column_names . push_back ( it ) ;
2015-09-21 12:13:05 +00:00
auto & select = typeid_cast < const ASTSelectQuery & > ( * query ) ;
2015-05-20 11:58:21 +00:00
/// Try transferring some condition from WHERE to PREWHERE if enabled and viable
if ( settings . optimize_move_to_prewhere )
if ( select . where_expression & & ! select . prewhere_expression )
2015-09-21 12:13:05 +00:00
MergeTreeWhereOptimizer { query , context , data , real_column_names , log } ;
2015-05-20 11:58:21 +00:00
2014-07-28 09:53:57 +00:00
Block virtual_columns_block ;
ColumnUInt8 * column = new ColumnUInt8 ( 2 ) ;
ColumnPtr column_ptr = column ;
column - > getData ( ) [ 0 ] = 0 ;
column - > getData ( ) [ 1 ] = 1 ;
2015-07-17 01:27:35 +00:00
virtual_columns_block . insert ( ColumnWithTypeAndName ( column_ptr , new DataTypeUInt8 , " _replicated " ) ) ;
2014-07-28 09:53:57 +00:00
2015-09-21 22:33:20 +00:00
/// Если запрошен столбец _replicated, пробуем индексировать.
2014-07-28 09:53:57 +00:00
if ( ! virt_column_names . empty ( ) )
2014-10-07 18:42:35 +00:00
VirtualColumnUtils : : filterBlockWithQuery ( query , virtual_columns_block , context ) ;
2014-07-28 09:53:57 +00:00
2014-07-29 14:05:15 +00:00
std : : multiset < UInt8 > values = VirtualColumnUtils : : extractSingleValueFromBlock < UInt8 > ( virtual_columns_block , " _replicated " ) ;
2014-04-24 10:20:02 +00:00
2014-07-28 09:53:57 +00:00
BlockInputStreams res ;
2014-07-30 12:10:34 +00:00
size_t part_index = 0 ;
2015-09-21 22:33:20 +00:00
/** Настройки parallel_replica_offset и parallel_replicas_count позволяют читать с одной реплики одну часть данных, а с другой - другую.
* Д л я р е п л и ц и р у е м ы х , д а н н ы е р а з б и в а ю т с я т а к и м ж е м е х а н и з м о м , к а к р а б о т а е т с е к ц и я SAMPLE .
* А д л я н е р е п л и ц и р у е м ы х д а н н ы х , т а к к а к и х ц е л о с т н о с т ь м е ж д у р е п л и к а м и н е к о н т р о л и р у е т с я ,
* с п е р в о й ( settings . parallel_replica_offset = = 0 ) р е п л и к и в ы б и р а ю т с я в с е д а н н ы е , а с о с т а л ь н ы х - н и к а к и е .
*/
2015-02-08 22:37:55 +00:00
if ( ( settings . parallel_replica_offset = = 0 ) & & unreplicated_reader & & values . count ( 0 ) )
2014-04-24 10:20:02 +00:00
{
2014-12-17 11:53:17 +00:00
res = unreplicated_reader - > read ( real_column_names , query ,
context , settings , processed_stage ,
2015-09-20 11:54:58 +00:00
max_block_size , threads , & part_index , 0 ) ;
2014-07-28 09:53:57 +00:00
for ( auto & virtual_column : virt_column_names )
{
if ( virtual_column = = " _replicated " )
{
for ( auto & stream : res )
2014-07-30 12:10:34 +00:00
stream = new AddingConstColumnBlockInputStream < UInt8 > ( stream , new DataTypeUInt8 , 0 , " _replicated " ) ;
2014-07-28 09:53:57 +00:00
}
}
}
2014-07-30 12:10:34 +00:00
if ( values . count ( 1 ) )
2014-07-28 09:53:57 +00:00
{
2015-09-20 11:54:58 +00:00
/** Настройка select_sequential_consistency имеет два смысла:
* 1. К и д а т ь и с к л ю ч е н и е , е с л и н а р е п л и к е е с т ь н е в с е к у с к и , к о т о р ы е б ы л и з а п и с а н ы н а к в о р у м о с т а л ь н ы х р е п л и к .
* 2. Н е ч и т а т ь к у с к и , к о т о р ы е е щ ё н е б ы л и з а п и с а н ы н а к в о р у м р е п л и к .
* Д л я э т о г о п р и х о д и т с я с и н х р о н н о с х о д и т ь в ZooKeeper .
*/
Int64 max_block_number_to_read = 0 ;
if ( settings . select_sequential_consistency )
{
auto zookeeper = getZooKeeper ( ) ;
String last_part ;
zookeeper - > tryGet ( zookeeper_path + " /quorum/last_part " , last_part ) ;
if ( ! last_part . empty ( ) & & ! data . getPartIfExists ( last_part ) ) /// TODO Отключение реплики при распределённых запросах.
throw Exception ( " Replica doesn't have part " + last_part + " which was successfully written to quorum of other replicas. "
" Send query to another replica or disable 'select_sequential_consistency' setting. " , ErrorCodes : : REPLICA_IS_NOT_IN_QUORUM ) ;
if ( last_part . empty ( ) ) /// Если ещё ни один кусок не был записан с кворумом.
{
String quorum_str ;
if ( zookeeper - > tryGet ( zookeeper_path + " /quorum/status " , quorum_str ) )
{
ReplicatedMergeTreeQuorumEntry quorum_entry ;
quorum_entry . fromString ( quorum_str ) ;
ActiveDataPartSet : : Part part_info ;
ActiveDataPartSet : : parsePartName ( quorum_entry . part_name , part_info ) ;
max_block_number_to_read = part_info . left - 1 ;
}
}
else
{
ActiveDataPartSet : : Part part_info ;
ActiveDataPartSet : : parsePartName ( last_part , part_info ) ;
max_block_number_to_read = part_info . right ;
}
}
auto res2 = reader . read (
real_column_names , query , context , settings , processed_stage , max_block_size , threads , & part_index , max_block_number_to_read ) ;
2014-07-28 09:53:57 +00:00
for ( auto & virtual_column : virt_column_names )
{
if ( virtual_column = = " _replicated " )
{
for ( auto & stream : res2 )
2014-07-30 12:10:34 +00:00
stream = new AddingConstColumnBlockInputStream < UInt8 > ( stream , new DataTypeUInt8 , 1 , " _replicated " ) ;
2014-07-28 09:53:57 +00:00
}
}
2014-07-30 12:10:34 +00:00
res . insert ( res . end ( ) , res2 . begin ( ) , res2 . end ( ) ) ;
2014-04-24 10:20:02 +00:00
}
return res ;
2014-03-22 14:44:44 +00:00
}
2014-10-18 17:37:55 +00:00
2015-09-10 20:43:42 +00:00
BlockOutputStreamPtr StorageReplicatedMergeTree : : write ( ASTPtr query , const Settings & settings )
2014-04-02 10:10:37 +00:00
{
2014-12-11 02:04:13 +00:00
if ( is_readonly )
throw Exception ( " Table is in readonly mode " , ErrorCodes : : TABLE_IS_READ_ONLY ) ;
2014-05-13 11:24:04 +00:00
2014-04-02 10:10:37 +00:00
String insert_id ;
2014-08-05 08:57:17 +00:00
if ( query )
if ( ASTInsertQuery * insert = typeid_cast < ASTInsertQuery * > ( & * query ) )
insert_id = insert - > insert_id ;
2014-04-02 10:10:37 +00:00
2015-09-10 20:43:42 +00:00
return new ReplicatedMergeTreeBlockOutputStream ( * this , insert_id , settings . insert_quorum ) ;
2014-04-02 10:10:37 +00:00
}
2014-03-22 14:44:44 +00:00
2014-10-18 17:37:55 +00:00
2015-04-14 14:58:59 +00:00
bool StorageReplicatedMergeTree : : optimize ( const Settings & settings )
2014-05-08 08:03:03 +00:00
{
2014-07-14 15:49:03 +00:00
/// Померджим какие-нибудь куски из директории unreplicated.
/// TODO: Мерджить реплицируемые куски тоже.
2014-05-08 08:03:03 +00:00
if ( ! unreplicated_data )
return false ;
2014-10-06 05:18:17 +00:00
std : : lock_guard < std : : mutex > lock ( unreplicated_mutex ) ;
2014-08-04 14:23:47 +00:00
2014-05-08 08:03:03 +00:00
unreplicated_data - > clearOldParts ( ) ;
MergeTreeData : : DataPartsVector parts ;
String merged_name ;
2015-04-14 13:44:38 +00:00
auto always_can_merge = [ ] ( const MergeTreeData : : DataPartPtr & a , const MergeTreeData : : DataPartPtr & b ) { return true ; } ;
2014-05-08 08:03:03 +00:00
if ( ! unreplicated_merger - > selectPartsToMerge ( parts , merged_name , 0 , true , true , false , always_can_merge ) )
return false ;
2014-09-10 11:34:26 +00:00
const auto & merge_entry = context . getMergeList ( ) . insert ( database_name , table_name , merged_name ) ;
2015-04-14 14:58:59 +00:00
unreplicated_merger - > mergeParts ( parts , merged_name , * merge_entry , settings . min_bytes_to_use_direct_io ) ;
2015-04-14 13:44:38 +00:00
2014-05-08 08:03:03 +00:00
return true ;
}
2014-10-18 17:37:55 +00:00
2014-08-07 09:23:55 +00:00
void StorageReplicatedMergeTree : : alter ( const AlterCommands & params ,
const String & database_name , const String & table_name , Context & context )
2014-07-16 08:58:59 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-12-17 18:37:23 +00:00
const MergeTreeMergeBlocker merge_blocker { merger } ;
const auto unreplicated_merge_blocker = unreplicated_merger ?
2015-02-10 21:10:58 +00:00
std : : make_unique < MergeTreeMergeBlocker > ( * unreplicated_merger ) : nullptr ;
2014-12-12 20:50:32 +00:00
2014-07-16 08:58:59 +00:00
LOG_DEBUG ( log , " Doing ALTER " ) ;
NamesAndTypesList new_columns ;
2014-10-16 13:37:01 +00:00
NamesAndTypesList new_materialized_columns ;
NamesAndTypesList new_alias_columns ;
ColumnDefaults new_column_defaults ;
2014-07-16 08:58:59 +00:00
String new_columns_str ;
int new_columns_version ;
zkutil : : Stat stat ;
{
auto table_lock = lockStructureForAlter ( ) ;
2014-12-11 02:04:13 +00:00
if ( is_readonly )
throw Exception ( " Can't ALTER readonly table " , ErrorCodes : : TABLE_IS_READ_ONLY ) ;
2014-08-12 09:27:00 +00:00
2014-07-16 08:58:59 +00:00
data . checkAlter ( params ) ;
2014-10-16 13:37:01 +00:00
new_columns = data . getColumnsListNonMaterialized ( ) ;
new_materialized_columns = data . materialized_columns ;
new_alias_columns = data . alias_columns ;
new_column_defaults = data . column_defaults ;
params . apply ( new_columns , new_materialized_columns , new_alias_columns , new_column_defaults ) ;
2014-07-16 08:58:59 +00:00
2014-11-10 16:16:43 +00:00
new_columns_str = ColumnsDescription < false > {
new_columns , new_materialized_columns ,
new_alias_columns , new_column_defaults
} . toString ( ) ;
2014-07-16 08:58:59 +00:00
/// Делаем ALTER.
zookeeper - > set ( zookeeper_path + " /columns " , new_columns_str , - 1 , & stat ) ;
new_columns_version = stat . version ;
}
LOG_DEBUG ( log , " Updated columns in ZooKeeper. Waiting for replicas to apply changes. " ) ;
/// Ждем, пока все реплики обновят данные.
/// Подпишемся на изменения столбцов, чтобы перестать ждать, если кто-то еще сделает ALTER.
if ( ! zookeeper - > exists ( zookeeper_path + " /columns " , & stat , alter_query_event ) )
throw Exception ( zookeeper_path + " /columns doesn't exist " , ErrorCodes : : NOT_FOUND_NODE ) ;
2014-09-26 00:49:50 +00:00
2014-07-16 08:58:59 +00:00
if ( stat . version ! = new_columns_version )
{
LOG_WARNING ( log , zookeeper_path + " /columns changed before this ALTER finished; "
" overlapping ALTER-s are fine but use caution with nontransitive changes " ) ;
return ;
}
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
for ( const String & replica : replicas )
{
LOG_DEBUG ( log , " Waiting for " < < replica < < " to apply changes " ) ;
while ( ! shutdown_called )
{
String replica_columns_str ;
/// Реплику могли успеть удалить.
if ( ! zookeeper - > tryGet ( zookeeper_path + " /replicas/ " + replica + " /columns " , replica_columns_str , & stat ) )
{
LOG_WARNING ( log , replica < < " was removed " ) ;
break ;
}
int replica_columns_version = stat . version ;
if ( replica_columns_str = = new_columns_str )
break ;
if ( ! zookeeper - > exists ( zookeeper_path + " /columns " , & stat ) )
throw Exception ( zookeeper_path + " /columns doesn't exist " , ErrorCodes : : NOT_FOUND_NODE ) ;
2014-09-26 00:49:50 +00:00
2014-07-16 08:58:59 +00:00
if ( stat . version ! = new_columns_version )
{
LOG_WARNING ( log , zookeeper_path + " /columns changed before ALTER finished; "
" overlapping ALTER-s are fine but use caution with nontransitive changes " ) ;
return ;
}
2014-07-17 10:44:17 +00:00
if ( ! zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica + " /columns " , & stat , alter_query_event ) )
2014-07-16 08:58:59 +00:00
{
LOG_WARNING ( log , replica < < " was removed " ) ;
break ;
}
if ( stat . version ! = replica_columns_version )
continue ;
alter_query_event - > wait ( ) ;
}
if ( shutdown_called )
break ;
}
LOG_DEBUG ( log , " ALTER finished " ) ;
}
2014-08-07 09:23:55 +00:00
/// Название воображаемого куска, покрывающего все возможные куски в указанном месяце с номерами в указанном диапазоне.
static String getFakePartNameForDrop ( const String & month_name , UInt64 left , UInt64 right )
{
/// Диапазон дат - весь месяц.
2015-07-07 23:11:30 +00:00
const auto & lut = DateLUT : : instance ( ) ;
2015-06-26 15:11:31 +00:00
time_t start_time = lut . YYYYMMDDToDate ( parse < UInt32 > ( month_name + " 01 " ) ) ;
2014-08-07 09:23:55 +00:00
DayNum_t left_date = lut . toDayNum ( start_time ) ;
DayNum_t right_date = DayNum_t ( static_cast < size_t > ( left_date ) + lut . daysInMonth ( start_time ) - 1 ) ;
/// Уровень - right-left+1: кусок не мог образоваться в результате такого или большего количества слияний.
return ActiveDataPartSet : : getPartName ( left_date , right_date , left , right , right - left + 1 ) ;
}
2014-10-18 17:37:55 +00:00
2015-06-22 18:23:33 +00:00
void StorageReplicatedMergeTree : : dropUnreplicatedPartition ( const Field & partition , const bool detach , const Settings & settings )
2014-08-07 09:23:55 +00:00
{
2015-04-21 13:10:08 +00:00
if ( ! unreplicated_data )
return ;
/// Просит завершить мерджи и не позволяет им начаться.
/// Это защищает от "оживания" данных за удалённую партицию после завершения мерджа.
const MergeTreeMergeBlocker merge_blocker { * unreplicated_merger } ;
auto structure_lock = lockStructure ( true ) ;
const DayNum_t month = MergeTreeData : : getMonthDayNum ( partition ) ;
size_t removed_parts = 0 ;
MergeTreeData : : DataParts parts = unreplicated_data - > getDataParts ( ) ;
for ( const auto & part : parts )
{
2015-08-17 21:09:36 +00:00
if ( part - > month ! = month )
2015-04-21 13:10:08 +00:00
continue ;
LOG_DEBUG ( log , " Removing unreplicated part " < < part - > name ) ;
+ + removed_parts ;
2015-06-22 18:23:33 +00:00
if ( detach )
unreplicated_data - > renameAndDetachPart ( part , " " ) ;
else
unreplicated_data - > replaceParts ( { part } , { } , false ) ;
2015-04-21 13:10:08 +00:00
}
2015-06-22 18:23:33 +00:00
LOG_INFO ( log , ( detach ? " Detached " : " Removed " ) < < removed_parts < < " unreplicated parts inside " < < apply_visitor ( FieldVisitorToString ( ) , partition ) < < " . " ) ;
2015-04-21 13:10:08 +00:00
}
2015-10-02 21:28:19 +00:00
void StorageReplicatedMergeTree : : dropPartition ( ASTPtr query , const Field & field , bool detach , bool unreplicated , const Settings & settings )
2015-04-21 13:10:08 +00:00
{
if ( unreplicated )
{
2015-06-22 18:23:33 +00:00
dropUnreplicatedPartition ( field , detach , settings ) ;
2015-04-21 13:10:08 +00:00
return ;
}
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-10-03 17:57:01 +00:00
String month_name = MergeTreeData : : getMonthName ( field ) ;
2014-08-07 09:23:55 +00:00
if ( ! is_leader_node )
2015-10-02 21:28:19 +00:00
{
/// Проксируем запрос в лидера.
auto live_replicas = zookeeper - > getChildren ( zookeeper_path + " /leader_election " ) ;
if ( live_replicas . empty ( ) )
throw Exception ( " No active replicas " , ErrorCodes : : NO_ACTIVE_REPLICAS ) ;
2015-12-03 04:03:20 +00:00
std : : sort ( live_replicas . begin ( ) , live_replicas . end ( ) ) ;
2015-10-02 21:28:19 +00:00
const auto leader = zookeeper - > get ( zookeeper_path + " /leader_election/ " + live_replicas . front ( ) ) ;
if ( leader = = replica_name )
throw Exception ( " Leader was suddenly changed or logical error. " , ErrorCodes : : LEADERSHIP_CHANGED ) ;
ReplicatedMergeTreeAddress leader_address ( zookeeper - > get ( zookeeper_path + " /replicas/ " + leader + " /host " ) ) ;
auto new_query = query - > clone ( ) ;
auto & alter = typeid_cast < ASTAlterQuery & > ( * new_query ) ;
alter . database = leader_address . database ;
alter . table = leader_address . table ;
/// NOTE Работает только если есть доступ от пользователя default без пароля. Можно исправить с помощью добавления параметра в конфиг сервера.
Connection connection (
leader_address . host ,
leader_address . queries_port ,
leader_address . database ,
" " , " " , " ClickHouse replica " ) ;
RemoteBlockInputStream stream ( connection , formattedAST ( new_query ) , & settings ) ;
NullBlockOutputStream output ;
copyData ( stream , output ) ;
return ;
}
2014-08-07 09:23:55 +00:00
/** Пропустим один номер в block_numbers для удаляемого месяца, и будем удалять только куски до этого номера.
* Э т о з а п р е т и т м е р д ж и у д а л я е м ы х к у с к о в с н о в ы м и в с т а в л я е м ы м и д а н н ы м и .
* И н в а р и а н т : в л о г е н е п о я в я т с я с л и я н и я у д а л я е м ы х к у с к о в с д р у г и м и к у с к а м и .
* NOTE : Е с л и п о н а д о б и т с я а н а л о г и ч н о п о д д е р ж а т ь з а п р о с DROP PART , д л я н е г о п р и д е т с я п р и д у м а т ь к а к о й - н и б у д ь н о в ы й м е х а н и з м ,
* ч т о б ы г а р а н т и р о в а т ь э т о т и н в а р и а н т .
*/
2015-08-17 21:09:36 +00:00
Int64 right ;
2014-08-07 09:23:55 +00:00
{
AbandonableLockInZooKeeper block_number_lock = allocateBlockNumber ( month_name ) ;
right = block_number_lock . getNumber ( ) ;
block_number_lock . unlock ( ) ;
}
/// Такого никогда не должно происходить.
if ( right = = 0 )
2015-09-24 05:47:17 +00:00
throw Exception ( " Logical error: just allocated block number is zero " , ErrorCodes : : LOGICAL_ERROR ) ;
2014-08-07 09:23:55 +00:00
- - right ;
String fake_part_name = getFakePartNameForDrop ( month_name , 0 , right ) ;
2016-01-10 04:43:30 +00:00
/** Запретим выбирать для слияния удаляемые куски.
2014-08-07 09:23:55 +00:00
* И н в а р и а н т : п о с л е п о я в л е н и я в л о г е з а п и с и DROP_RANGE , в л о г е н е п о я в я т с я с л и я н и я у д а л я е м ы х к у с к о в .
*/
{
2014-12-02 19:08:18 +00:00
std : : lock_guard < std : : mutex > merge_selecting_lock ( merge_selecting_mutex ) ;
2014-08-07 09:23:55 +00:00
2016-01-10 04:43:30 +00:00
queue . disableMergesInRange ( fake_part_name ) ;
2014-08-07 09:23:55 +00:00
}
2014-08-07 11:46:01 +00:00
/// Наконец, добившись нужных инвариантов, можно положить запись в лог.
2014-08-07 09:23:55 +00:00
LogEntry entry ;
entry . type = LogEntry : : DROP_RANGE ;
entry . source_replica = replica_name ;
entry . new_part_name = fake_part_name ;
entry . detach = detach ;
String log_znode_path = zookeeper - > create ( zookeeper_path + " /log/log- " , entry . toString ( ) , zkutil : : CreateMode : : PersistentSequential ) ;
2014-08-07 11:46:01 +00:00
entry . znode_name = log_znode_path . substr ( log_znode_path . find_last_of ( ' / ' ) + 1 ) ;
2015-09-27 14:22:23 +00:00
entry . create_time = time ( 0 ) ;
2014-08-07 09:23:55 +00:00
2014-10-18 19:14:09 +00:00
/// Если надо - дожидаемся выполнения операции на с е б е или на всех репликах.
if ( settings . replication_alter_partitions_sync ! = 0 )
{
if ( settings . replication_alter_partitions_sync = = 1 )
waitForReplicaToProcessLogEntry ( replica_name , entry ) ;
else
waitForAllReplicasToProcessLogEntry ( entry ) ;
}
2014-08-07 09:23:55 +00:00
}
2014-10-18 17:37:55 +00:00
2015-10-02 21:28:19 +00:00
void StorageReplicatedMergeTree : : attachPartition ( ASTPtr query , const Field & field , bool unreplicated , bool attach_part , const Settings & settings )
2014-08-07 11:46:01 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-10-03 17:57:01 +00:00
String partition ;
2014-08-08 08:28:13 +00:00
2014-10-03 17:57:01 +00:00
if ( attach_part )
2015-08-17 21:09:36 +00:00
partition = field . safeGet < String > ( ) ;
2014-10-03 17:57:01 +00:00
else
partition = MergeTreeData : : getMonthName ( field ) ;
2014-08-08 08:28:13 +00:00
String source_dir = ( unreplicated ? " unreplicated/ " : " detached/ " ) ;
/// Составим список кусков, которые нужно добавить.
Strings parts ;
if ( attach_part )
{
parts . push_back ( partition ) ;
}
else
{
LOG_DEBUG ( log , " Looking for parts for partition " < < partition < < " in " < < source_dir ) ;
ActiveDataPartSet active_parts ;
for ( Poco : : DirectoryIterator it = Poco : : DirectoryIterator ( full_path + source_dir ) ; it ! = Poco : : DirectoryIterator ( ) ; + + it )
{
String name = it . name ( ) ;
if ( ! ActiveDataPartSet : : isPartDirectory ( name ) )
continue ;
2014-10-15 19:59:12 +00:00
if ( 0 ! = name . compare ( 0 , partition . size ( ) , partition ) )
2014-08-08 08:28:13 +00:00
continue ;
LOG_DEBUG ( log , " Found part " < < name ) ;
active_parts . add ( name ) ;
}
LOG_DEBUG ( log , active_parts . size ( ) < < " of them are active " ) ;
parts = active_parts . getParts ( ) ;
}
/// Синхронно проверим, что добавляемые куски существуют и не испорчены хотя бы на этой реплике. Запишем checksums.txt, если е г о нет.
LOG_DEBUG ( log , " Checking parts " ) ;
for ( const String & part : parts )
{
LOG_DEBUG ( log , " Checking part " < < part ) ;
data . loadPartAndFixMetadata ( source_dir + part ) ;
}
/// Выделим добавляемым кускам максимальные свободные номера, меньшие RESERVED_BLOCK_NUMBERS.
/// NOTE: Проверка свободности номеров никак не синхронизируется. Выполнять несколько запросов ATTACH/DETACH/DROP одновременно нельзя.
2015-08-17 21:09:36 +00:00
Int64 min_used_number = RESERVED_BLOCK_NUMBERS ;
DayNum_t month = DateLUT : : instance ( ) . makeDayNum ( parse < UInt16 > ( partition . substr ( 0 , 4 ) ) , parse < UInt8 > ( partition . substr ( 4 , 2 ) ) , 0 ) ;
2014-08-08 08:28:13 +00:00
{
auto existing_parts = data . getDataParts ( ) ;
for ( const auto & part : existing_parts )
2015-08-17 21:09:36 +00:00
if ( part - > month = = month )
min_used_number = std : : min ( min_used_number , part - > left ) ;
2014-08-08 08:28:13 +00:00
}
/// Добавим записи в лог.
std : : reverse ( parts . begin ( ) , parts . end ( ) ) ;
std : : list < LogEntry > entries ;
zkutil : : Ops ops ;
for ( const String & part_name : parts )
{
ActiveDataPartSet : : Part part ;
ActiveDataPartSet : : parsePartName ( part_name , part ) ;
part . left = part . right = - - min_used_number ;
String new_part_name = ActiveDataPartSet : : getPartName ( part . left_date , part . right_date , part . left , part . right , part . level ) ;
LOG_INFO ( log , " Will attach " < < part_name < < " as " < < new_part_name ) ;
entries . emplace_back ( ) ;
LogEntry & entry = entries . back ( ) ;
entry . type = LogEntry : : ATTACH_PART ;
entry . source_replica = replica_name ;
entry . source_part_name = part_name ;
entry . new_part_name = new_part_name ;
entry . attach_unreplicated = unreplicated ;
2015-09-27 14:22:23 +00:00
entry . create_time = time ( 0 ) ;
2014-08-08 08:28:13 +00:00
ops . push_back ( new zkutil : : Op : : Create (
zookeeper_path + " /log/log- " , entry . toString ( ) , zookeeper - > getDefaultACL ( ) , zkutil : : CreateMode : : PersistentSequential ) ) ;
}
LOG_DEBUG ( log , " Adding attaches to log " ) ;
zookeeper - > multi ( ops ) ;
2014-10-15 20:28:43 +00:00
2014-10-18 19:14:09 +00:00
/// Если надо - дожидаемся выполнения операции на с е б е или на всех репликах.
if ( settings . replication_alter_partitions_sync ! = 0 )
2014-08-08 08:28:13 +00:00
{
2014-10-18 19:14:09 +00:00
size_t i = 0 ;
for ( LogEntry & entry : entries )
{
String log_znode_path = dynamic_cast < zkutil : : Op : : Create & > ( ops [ i ] ) . getPathCreated ( ) ;
entry . znode_name = log_znode_path . substr ( log_znode_path . find_last_of ( ' / ' ) + 1 ) ;
if ( settings . replication_alter_partitions_sync = = 1 )
waitForReplicaToProcessLogEntry ( replica_name , entry ) ;
else
waitForAllReplicasToProcessLogEntry ( entry ) ;
2014-08-08 08:28:13 +00:00
2014-10-18 19:14:09 +00:00
+ + i ;
}
2014-08-08 08:28:13 +00:00
}
2014-08-07 11:46:01 +00:00
}
2014-10-18 17:37:55 +00:00
2014-03-22 14:44:44 +00:00
void StorageReplicatedMergeTree : : drop ( )
{
2014-12-11 02:04:13 +00:00
if ( is_readonly )
throw Exception ( " Can't drop readonly replicated table (need to drop data in ZooKeeper as well) " , ErrorCodes::TABLE_IS_READ_ONLY) ;
2014-07-23 13:58:38 +00:00
2014-04-03 11:48:28 +00:00
shutdown ( ) ;
2015-12-09 04:41:46 +00:00
auto zookeeper = getZooKeeper ( ) ;
2015-12-13 12:02:10 +00:00
if ( zookeeper - > expired ( ) )
throw Exception ( " Table was not dropped because ZooKeeper session has been expired. " , ErrorCodes : : TABLE_WAS_NOT_DROPPED ) ;
2014-05-27 12:08:40 +00:00
LOG_INFO ( log , " Removing replica " < < replica_path ) ;
2014-03-22 14:44:44 +00:00
replica_is_active_node = nullptr ;
2014-07-07 09:51:42 +00:00
zookeeper - > tryRemoveRecursive ( replica_path ) ;
2014-07-07 11:39:06 +00:00
/// Проверяем, что zookeeper_path существует: е г о могла удалить другая реплика после выполнения предыдущей строки.
2014-07-07 09:51:42 +00:00
Strings replicas ;
if ( zookeeper - > tryGetChildren ( zookeeper_path + " /replicas " , replicas ) = = ZOK & & replicas . empty ( ) )
2014-05-27 12:08:40 +00:00
{
LOG_INFO ( log , " Removing table " < < zookeeper_path < < " (this might take several minutes) " ) ;
2014-07-07 09:51:42 +00:00
zookeeper - > tryRemoveRecursive ( zookeeper_path ) ;
2014-05-27 12:08:40 +00:00
}
2014-07-02 12:30:38 +00:00
data . dropAllData ( ) ;
2014-03-22 14:44:44 +00:00
}
2014-10-18 17:37:55 +00:00
2014-07-28 14:33:30 +00:00
void StorageReplicatedMergeTree : : rename ( const String & new_path_to_db , const String & new_database_name , const String & new_table_name )
{
std : : string new_full_path = new_path_to_db + escapeForFileName ( new_table_name ) + ' / ' ;
data . setPath ( new_full_path , true ) ;
if ( unreplicated_data )
unreplicated_data - > setPath ( new_full_path + " unreplicated/ " , false ) ;
database_name = new_database_name ;
table_name = new_table_name ;
full_path = new_full_path ;
/// TODO: Можно обновить названия логгеров.
}
2014-10-18 17:37:55 +00:00
2014-08-07 09:23:55 +00:00
AbandonableLockInZooKeeper StorageReplicatedMergeTree : : allocateBlockNumber ( const String & month_name )
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-07 09:23:55 +00:00
String month_path = zookeeper_path + " /block_numbers/ " + month_name ;
if ( ! zookeeper - > exists ( month_path ) )
{
/// Создадим в block_numbers ноду для месяца и пропустим в ней 200 значений инкремента.
/// Нужно, чтобы в будущем при необходимости можно было добавить данные в начало.
zkutil : : Ops ops ;
auto acl = zookeeper - > getDefaultACL ( ) ;
ops . push_back ( new zkutil : : Op : : Create ( month_path , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
2014-08-08 08:28:13 +00:00
for ( size_t i = 0 ; i < RESERVED_BLOCK_NUMBERS ; + + i )
2014-08-07 09:23:55 +00:00
{
ops . push_back ( new zkutil : : Op : : Create ( month_path + " /skip_increment " , " " , acl , zkutil : : CreateMode : : Persistent ) ) ;
ops . push_back ( new zkutil : : Op : : Remove ( month_path + " /skip_increment " , - 1 ) ) ;
}
/// Игнорируем ошибки - не получиться могло только если кто-то еще выполнил эту строчку раньше нас.
zookeeper - > tryMulti ( ops ) ;
}
return AbandonableLockInZooKeeper (
zookeeper_path + " /block_numbers/ " + month_name + " /block- " ,
zookeeper_path + " /temp " , * zookeeper ) ;
}
2014-10-18 17:37:55 +00:00
2014-08-08 08:28:13 +00:00
void StorageReplicatedMergeTree : : waitForAllReplicasToProcessLogEntry ( const LogEntry & entry )
2014-08-07 09:23:55 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-08-07 11:46:01 +00:00
LOG_DEBUG ( log , " Waiting for all replicas to process " < < entry . znode_name ) ;
2014-10-18 17:37:55 +00:00
Strings replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
for ( const String & replica : replicas )
waitForReplicaToProcessLogEntry ( replica , entry ) ;
LOG_DEBUG ( log , " Finished waiting for all replicas to process " < < entry . znode_name ) ;
}
void StorageReplicatedMergeTree : : waitForReplicaToProcessLogEntry ( const String & replica , const LogEntry & entry )
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2015-06-04 02:07:30 +00:00
String entry_str = entry . toString ( ) ;
String log_node_name ;
2014-08-07 09:23:55 +00:00
2015-06-04 02:07:30 +00:00
/** В эту функцию могут передать entry двух видов:
* 1. ( б о л е е ч а с т о ) И з д и р е к т о р и и log - о б щ е г о л о г а , о т к у д а р е п л и к и к о п и р у ю т з а п и с и в с в о ю queue .
* 2. И з д и р е к т о р и и queue о д н о й и з р е п л и к .
*
* П р о б л е м а в т о м , ч т о н о м е р а ( sequential н о д ) э л е м е н т о в о ч е р е д и в log и в queue н е с о в п а д а ю т .
* ( И в queue н е с о в п а д а ю т н о м е р а у о д н о г о и т о г о ж е э л е м е н т а л о г а д л я р а з н ы х р е п л и к . )
*
* П о э т о м у с л е д у е т р а с с м а т р и в а т ь э т и с л у ч а и п о - о т д е л ь н о с т и .
*/
2014-08-07 11:46:01 +00:00
2015-06-04 02:07:30 +00:00
/** Первое - нужно дождаться, пока реплика возьмёт к с е б е в queue элемент очереди из log,
* е с л и о н а е щ ё э т о г о н е с д е л а л а ( с м . ф у н к ц и ю pullLogsToQueue ) .
*
* Д л я э т о г о п р о в е р я е м е ё у з е л log_pointer - м а к с и м а л ь н ы й н о м е р в з я т о г о э л е м е н т а и з log п л ю с е д и н и ц а .
*/
if ( 0 = = entry . znode_name . compare ( 0 , strlen ( " log- " ) , " log- " ) )
2014-10-18 17:37:55 +00:00
{
2015-06-04 02:07:30 +00:00
/** В этом случае просто берём номер из имени ноды log-xxxxxxxxxx.
*/
2014-08-07 09:23:55 +00:00
2015-06-04 02:07:30 +00:00
UInt64 log_index = parse < UInt64 > ( entry . znode_name . substr ( entry . znode_name . size ( ) - 10 ) ) ;
log_node_name = entry . znode_name ;
2014-08-07 09:23:55 +00:00
2015-06-04 02:07:30 +00:00
LOG_DEBUG ( log , " Waiting for " < < replica < < " to pull " < < log_node_name < < " to queue " ) ;
/// Дождемся, пока запись попадет в очередь реплики.
while ( true )
{
zkutil : : EventPtr event = new Poco : : Event ;
String log_pointer = zookeeper - > get ( zookeeper_path + " /replicas/ " + replica + " /log_pointer " , nullptr , event ) ;
if ( ! log_pointer . empty ( ) & & parse < UInt64 > ( log_pointer ) > log_index )
break ;
event - > wait ( ) ;
}
2014-10-18 17:37:55 +00:00
}
2015-06-04 02:07:30 +00:00
else if ( 0 = = entry . znode_name . compare ( 0 , strlen ( " queue- " ) , " queue- " ) )
{
/** В этом случае номер log-ноды неизвестен. Нужно просмотреть все от log_pointer до конца,
* и щ а н о д у с т а к и м ж е с о д е р ж и м ы м . И е с л и м ы е ё н е н а й д ё м - з н а ч и т р е п л и к а у ж е в з я л а э т у з а п и с ь в с в о ю queue .
*/
String log_pointer = zookeeper - > get ( zookeeper_path + " /replicas/ " + replica + " /log_pointer " ) ;
2014-08-07 09:23:55 +00:00
2015-06-04 02:07:30 +00:00
Strings log_entries = zookeeper - > getChildren ( zookeeper_path + " /log " ) ;
UInt64 log_index = 0 ;
bool found = false ;
for ( const String & log_entry_name : log_entries )
{
log_index = parse < UInt64 > ( log_entry_name . substr ( log_entry_name . size ( ) - 10 ) ) ;
if ( ! log_pointer . empty ( ) & & log_index < parse < UInt64 > ( log_pointer ) )
continue ;
String log_entry_str ;
bool exists = zookeeper - > tryGet ( zookeeper_path + " /log/ " + log_entry_name , log_entry_str ) ;
if ( exists & & entry_str = = log_entry_str )
{
found = true ;
log_node_name = log_entry_name ;
break ;
}
}
if ( found )
{
LOG_DEBUG ( log , " Waiting for " < < replica < < " to pull " < < log_node_name < < " to queue " ) ;
/// Дождемся, пока запись попадет в очередь реплики.
while ( true )
{
zkutil : : EventPtr event = new Poco : : Event ;
String log_pointer = zookeeper - > get ( zookeeper_path + " /replicas/ " + replica + " /log_pointer " , nullptr , event ) ;
if ( ! log_pointer . empty ( ) & & parse < UInt64 > ( log_pointer ) > log_index )
break ;
event - > wait ( ) ;
}
}
}
else
throw Exception ( " Logical error: unexpected name of log node: " + entry . znode_name , ErrorCodes : : LOGICAL_ERROR ) ;
if ( ! log_node_name . empty ( ) )
LOG_DEBUG ( log , " Looking for node corresponding to " < < log_node_name < < " in " < < replica < < " queue " ) ;
else
LOG_DEBUG ( log , " Looking for corresponding node in " < < replica < < " queue " ) ;
/** Второе - найдем соответствующую запись в очереди указанной реплики (replica).
* Е ё н о м е р м о ж е т н е с о в п а д а т ь н и с log - у з л о м , н и с queue - у з л о м у т е к у щ е й р е п л и к и ( у н а с ) .
* П о э т о м у , и щ е м п у т ё м с р а в н е н и я с о д е р ж и м о г о .
*/
2014-08-07 11:46:01 +00:00
2014-10-18 17:37:55 +00:00
Strings queue_entries = zookeeper - > getChildren ( zookeeper_path + " /replicas/ " + replica + " /queue " ) ;
2015-06-04 02:07:30 +00:00
String queue_entry_to_wait_for ;
2014-08-07 09:23:55 +00:00
2014-10-18 17:37:55 +00:00
for ( const String & entry_name : queue_entries )
{
String queue_entry_str ;
bool exists = zookeeper - > tryGet ( zookeeper_path + " /replicas/ " + replica + " /queue/ " + entry_name , queue_entry_str ) ;
2015-06-04 02:07:30 +00:00
if ( exists & & queue_entry_str = = entry_str )
2014-08-07 09:23:55 +00:00
{
2015-06-04 02:07:30 +00:00
queue_entry_to_wait_for = entry_name ;
2014-10-18 17:37:55 +00:00
break ;
2014-08-07 09:23:55 +00:00
}
2014-10-18 17:37:55 +00:00
}
2014-08-07 09:23:55 +00:00
2014-10-18 17:37:55 +00:00
/// Пока искали запись, е е уже выполнили и удалили.
2015-06-04 02:07:30 +00:00
if ( queue_entry_to_wait_for . empty ( ) )
{
LOG_DEBUG ( log , " No corresponding node found. Assuming it has been already processed. " ) ;
2014-10-18 17:37:55 +00:00
return ;
2015-06-04 02:07:30 +00:00
}
LOG_DEBUG ( log , " Waiting for " < < queue_entry_to_wait_for < < " to disappear from " < < replica < < " queue " ) ;
2014-08-07 09:23:55 +00:00
2015-06-04 02:07:30 +00:00
/// Третье - дождемся, пока запись исчезнет из очереди реплики.
2015-09-11 02:13:59 +00:00
zookeeper - > waitForDisappear ( zookeeper_path + " /replicas/ " + replica + " /queue/ " + queue_entry_to_wait_for ) ;
2014-08-07 09:23:55 +00:00
}
2014-10-07 18:44:03 +00:00
void StorageReplicatedMergeTree : : getStatus ( Status & res , bool with_zk_fields )
2014-10-06 05:18:17 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-10-06 05:18:17 +00:00
res . is_leader = is_leader_node ;
2014-12-11 02:04:13 +00:00
res . is_readonly = is_readonly ;
2014-11-07 01:12:55 +00:00
res . is_session_expired = ! zookeeper | | zookeeper - > expired ( ) ;
2014-10-06 05:18:17 +00:00
2016-01-10 04:43:30 +00:00
res . queue = queue . getStatus ( ) ;
2014-10-06 05:18:17 +00:00
{
std : : lock_guard < std : : mutex > lock ( parts_to_check_mutex ) ;
res . parts_to_check = parts_to_check_set . size ( ) ;
}
res . zookeeper_path = zookeeper_path ;
res . replica_name = replica_name ;
res . replica_path = replica_path ;
res . columns_version = columns_version ;
2014-10-07 18:44:03 +00:00
if ( res . is_session_expired | | ! with_zk_fields )
2014-10-06 05:18:17 +00:00
{
res . log_max_index = 0 ;
res . log_pointer = 0 ;
res . total_replicas = 0 ;
res . active_replicas = 0 ;
}
else
{
auto log_entries = zookeeper - > getChildren ( zookeeper_path + " /log " ) ;
2014-10-07 18:44:03 +00:00
if ( log_entries . empty ( ) )
{
res . log_max_index = 0 ;
}
else
{
const String & last_log_entry = * std : : max_element ( log_entries . begin ( ) , log_entries . end ( ) ) ;
res . log_max_index = parse < UInt64 > ( last_log_entry . substr ( strlen ( " log- " ) ) ) ;
}
String log_pointer_str = zookeeper - > get ( replica_path + " /log_pointer " ) ;
res . log_pointer = log_pointer_str . empty ( ) ? 0 : parse < UInt64 > ( log_pointer_str ) ;
2014-10-06 05:18:17 +00:00
auto all_replicas = zookeeper - > getChildren ( zookeeper_path + " /replicas " ) ;
res . total_replicas = all_replicas . size ( ) ;
res . active_replicas = 0 ;
for ( const String & replica : all_replicas )
if ( zookeeper - > exists ( zookeeper_path + " /replicas/ " + replica + " /is_active " ) )
+ + res . active_replicas ;
}
}
2014-10-09 20:28:33 +00:00
2015-09-24 00:21:02 +00:00
void StorageReplicatedMergeTree : : getQueue ( LogEntriesData & res , String & replica_name_ )
{
replica_name_ = replica_name ;
2016-01-10 04:43:30 +00:00
queue . getEntries ( res ) ;
2015-09-24 00:21:02 +00:00
}
2015-11-05 20:08:18 +00:00
void StorageReplicatedMergeTree : : getReplicaDelays ( time_t & out_absolute_delay , time_t & out_relative_delay ) const
{
if ( ! restarting_thread )
throw Exception ( " Table was shutted down or is in readonly mode. " , ErrorCodes : : TABLE_IS_READ_ONLY ) ;
restarting_thread - > getReplicaDelays ( out_absolute_delay , out_relative_delay ) ;
}
2014-10-18 19:14:09 +00:00
void StorageReplicatedMergeTree : : fetchPartition ( const Field & partition , const String & from_ , const Settings & settings )
2014-10-09 20:28:33 +00:00
{
2014-12-12 20:50:32 +00:00
auto zookeeper = getZooKeeper ( ) ;
2014-10-10 00:46:37 +00:00
String partition_str = MergeTreeData : : getMonthName ( partition ) ;
2014-10-09 20:28:33 +00:00
String from = from_ ;
2014-10-09 23:14:06 +00:00
if ( from . back ( ) = = ' / ' )
from . resize ( from . size ( ) - 1 ) ;
2014-10-09 20:28:33 +00:00
2014-10-10 00:46:37 +00:00
LOG_INFO ( log , " Will fetch partition " < < partition_str < < " from shard " < < from_ ) ;
2014-10-13 17:28:59 +00:00
/** Проверим, что в директории detached (куда мы будем записывать скаченные куски) ещё нет такой партиции.
* Н е н а д ё ж н о ( е с т ь race condition ) - т а к а я п а р т и ц и я м о ж е т п о я в и т ь с я ч у т ь п о з ж е .
*/
Poco : : DirectoryIterator dir_end ;
for ( Poco : : DirectoryIterator dir_it { data . getFullPath ( ) + " detached/ " } ; dir_it ! = dir_end ; + + dir_it )
if ( 0 = = dir_it . name ( ) . compare ( 0 , partition_str . size ( ) , partition_str ) )
throw Exception ( " Detached partition " + partition_str + " is already exists. " , ErrorCodes : : PARTITION_ALREADY_EXISTS ) ;
2014-10-09 20:28:33 +00:00
/// Список реплик шарда-источника.
zkutil : : Strings replicas = zookeeper - > getChildren ( from + " /replicas " ) ;
/// Оставим только активные реплики.
zkutil : : Strings active_replicas ;
active_replicas . reserve ( replicas . size ( ) ) ;
for ( const String & replica : replicas )
if ( zookeeper - > exists ( from + " /replicas/ " + replica + " /is_active " ) )
active_replicas . push_back ( replica ) ;
if ( active_replicas . empty ( ) )
2014-10-13 17:28:59 +00:00
throw Exception ( " No active replicas for shard " + from , ErrorCodes : : NO_ACTIVE_REPLICAS ) ;
2014-10-09 20:28:33 +00:00
/** Надо выбрать лучшую (наиболее актуальную) реплику.
2014-10-10 00:46:37 +00:00
* Э т о р е п л и к а с м а к с и м а л ь н ы м log_pointer , з а т е м с м и н и м а л ь н ы м р а з м е р о м queue .
2014-10-09 20:28:33 +00:00
* NOTE Э т о н е с о в с е м л у ч ш и й к р и т е р и й . Д л я с к а ч и в а н и я с т а р ы х п а р т и ц и й э т о н е и м е е т с м ы с л а ,
* и б ы л о б ы н е п л о х о у м е т ь в ы б и р а т ь р е п л и к у , б л и ж а й ш у ю п о с е т и .
2014-10-10 00:46:37 +00:00
* NOTE Р а з у м е е т с я , з д е с ь е с т ь data race - ы . М о ж н о р е ш и т ь р е т р а я м и .
2014-10-09 20:28:33 +00:00
*/
2014-10-10 00:46:37 +00:00
Int64 max_log_pointer = - 1 ;
UInt64 min_queue_size = std : : numeric_limits < UInt64 > : : max ( ) ;
String best_replica ;
for ( const String & replica : active_replicas )
{
2014-10-13 17:28:59 +00:00
String current_replica_path = from + " /replicas/ " + replica ;
2014-10-10 00:46:37 +00:00
2014-10-13 17:28:59 +00:00
String log_pointer_str = zookeeper - > get ( current_replica_path + " /log_pointer " ) ;
2014-10-10 00:46:37 +00:00
Int64 log_pointer = log_pointer_str . empty ( ) ? 0 : parse < UInt64 > ( log_pointer_str ) ;
zkutil : : Stat stat ;
2014-10-13 17:28:59 +00:00
zookeeper - > get ( current_replica_path + " /queue " , & stat ) ;
2014-10-10 00:46:37 +00:00
size_t queue_size = stat . numChildren ;
if ( log_pointer > max_log_pointer
| | ( log_pointer = = max_log_pointer & & queue_size < min_queue_size ) )
{
max_log_pointer = log_pointer ;
min_queue_size = queue_size ;
best_replica = replica ;
}
}
if ( best_replica . empty ( ) )
throw Exception ( " Logical error: cannot choose best replica. " , ErrorCodes : : LOGICAL_ERROR ) ;
LOG_INFO ( log , " Found " < < replicas . size ( ) < < " replicas, " < < active_replicas . size ( ) < < " of them are active. "
< < " Selected " < < best_replica < < " to fetch from. " ) ;
2014-10-13 17:28:59 +00:00
String best_replica_path = from + " /replicas/ " + best_replica ;
2014-10-10 00:46:37 +00:00
/// Выясним, какие куски есть на лучшей реплике.
2014-10-09 20:28:33 +00:00
2014-10-13 17:28:59 +00:00
/** Пытаемся скачать эти куски.
* Ч а с т ь и з н и х м о г л а у д а л и т ь с я и з - з а м е р д ж а .
* В э т о м с л у ч а е , о б н о в л я е м и н ф о р м а ц и ю о д о с т у п н ы х к у с к а х и п р о б у е м с н о в а .
*/
unsigned try_no = 0 ;
Strings missing_parts ;
do
{
if ( try_no )
LOG_INFO ( log , " Some of parts ( " < < missing_parts . size ( ) < < " ) are missing. Will try to fetch covering parts. " ) ;
if ( try_no > = 5 )
throw Exception ( " Too much retries to fetch parts from " + best_replica_path , ErrorCodes : : TOO_MUCH_RETRIES_TO_FETCH_PARTS ) ;
Strings parts = zookeeper - > getChildren ( best_replica_path + " /parts " ) ;
ActiveDataPartSet active_parts_set ( parts ) ;
Strings parts_to_fetch ;
if ( missing_parts . empty ( ) )
{
parts_to_fetch = active_parts_set . getParts ( ) ;
/// Оставляем только куски нужной партиции.
Strings parts_to_fetch_partition ;
for ( const String & part : parts_to_fetch )
if ( 0 = = part . compare ( 0 , partition_str . size ( ) , partition_str ) )
parts_to_fetch_partition . push_back ( part ) ;
parts_to_fetch = std : : move ( parts_to_fetch_partition ) ;
2014-10-13 18:01:58 +00:00
if ( parts_to_fetch . empty ( ) )
throw Exception ( " Partition " + partition_str + " on " + best_replica_path + " doesn't exist " , ErrorCodes : : PARTITION_DOESNT_EXIST ) ;
2014-10-13 17:28:59 +00:00
}
else
{
for ( const String & missing_part : missing_parts )
{
String containing_part = active_parts_set . getContainingPart ( missing_part ) ;
if ( ! containing_part . empty ( ) )
parts_to_fetch . push_back ( containing_part ) ;
else
LOG_WARNING ( log , " Part " < < missing_part < < " on replica " < < best_replica_path < < " has been vanished. " ) ;
}
}
LOG_INFO ( log , " Parts to fetch: " < < parts_to_fetch . size ( ) ) ;
missing_parts . clear ( ) ;
for ( const String & part : parts_to_fetch )
{
try
{
2015-09-11 02:13:59 +00:00
fetchPart ( part , best_replica_path , true , 0 ) ;
2014-10-13 17:28:59 +00:00
}
catch ( const DB : : Exception & e )
{
if ( e . code ( ) ! = ErrorCodes : : RECEIVED_ERROR_FROM_REMOTE_IO_SERVER )
throw ;
LOG_INFO ( log , e . displayText ( ) ) ;
missing_parts . push_back ( part ) ;
}
}
2014-10-09 20:28:33 +00:00
2014-10-13 17:28:59 +00:00
+ + try_no ;
} while ( ! missing_parts . empty ( ) ) ;
2014-10-09 20:28:33 +00:00
}
2014-11-11 04:11:07 +00:00
void StorageReplicatedMergeTree : : freezePartition ( const Field & partition , const Settings & settings )
{
/// Префикс может быть произвольным. Н е обязательно месяц - можно указать лишь год.
String prefix = partition . getType ( ) = = Field : : Types : : UInt64
? toString ( partition . get < UInt64 > ( ) )
: partition . safeGet < String > ( ) ;
data . freezePartition ( prefix ) ;
if ( unreplicated_data )
unreplicated_data - > freezePartition ( prefix ) ;
}
2014-03-21 13:42:14 +00:00
}