2024-03-09 00:36:06 +00:00
import os
import re
2024-09-30 18:26:13 +00:00
import shutil
2024-03-09 00:36:06 +00:00
import threading
2024-09-30 18:26:13 +00:00
import time
2024-03-09 00:36:06 +00:00
from random import randint
2024-09-30 18:26:13 +00:00
import pytest
2024-03-09 00:36:06 +00:00
from helpers . cluster import ClickHouseCluster
from helpers . network import PartitionManager
2024-09-30 18:26:13 +00:00
from helpers . test_tools import assert_eq_with_retry , assert_logs_contain
2024-03-09 00:36:06 +00:00
test_recover_staled_replica_run = 1
cluster = ClickHouseCluster ( __file__ )
node1 = cluster . add_instance (
" node1 " ,
main_configs = [ " configs/config.xml " ] ,
user_configs = [ " configs/users.xml " ] ,
with_zookeeper = True ,
2024-10-10 17:39:01 +00:00
keeper_required_feature_flags = [ " multi_read " , " create_if_not_exists " ] ,
2024-03-09 00:36:06 +00:00
macros = { " shard " : " shard1 " , " replica " : " 1 " } ,
2024-10-08 03:32:13 +00:00
stay_alive = True ,
2024-03-09 00:36:06 +00:00
)
node2 = cluster . add_instance (
" node2 " ,
main_configs = [ " configs/config.xml " ] ,
user_configs = [ " configs/users.xml " ] ,
with_zookeeper = True ,
2024-10-10 17:39:01 +00:00
keeper_required_feature_flags = [ " multi_read " , " create_if_not_exists " ] ,
2024-03-09 00:36:06 +00:00
macros = { " shard " : " shard1 " , " replica " : " 2 " } ,
)
nodes = [ node1 , node2 ]
@pytest.fixture ( scope = " module " )
def started_cluster ( ) :
try :
cluster . start ( )
yield cluster
finally :
cluster . shutdown ( )
def test_refreshable_mv_in_replicated_db ( started_cluster ) :
for node in nodes :
node . query (
" create database re engine = Replicated( ' /test/re ' , ' shard1 ' , ' {replica} ' ); "
)
# Table engine check.
2024-06-07 01:07:32 +00:00
assert " BAD_ARGUMENTS " in node1 . query_and_get_error (
" create materialized view re.a refresh every 1 second (x Int64) engine Memory as select 1 as x "
)
2024-03-09 00:36:06 +00:00
# Basic refreshing.
2024-06-07 01:07:32 +00:00
node1 . query (
" create materialized view re.a refresh every 1 second (x Int64) engine ReplicatedMergeTree order by x as select number*10 as x from numbers(2) "
)
2024-03-09 00:36:06 +00:00
node1 . query ( " system sync database replica re " )
for node in nodes :
node . query ( " system wait view re.a " )
assert node . query ( " select * from re.a order by all " ) == " 0 \n 10 \n "
2024-06-07 01:07:32 +00:00
assert (
node . query (
2024-08-29 18:35:30 +00:00
" select database, view, last_success_time != 0, last_refresh_time != 0, last_refresh_replica in ( ' 1 ' , ' 2 ' ), exception from system.view_refreshes "
2024-06-07 01:07:32 +00:00
)
2024-08-29 18:35:30 +00:00
== " re \t a \t 1 \t 1 \t 1 \t \n "
2024-06-07 01:07:32 +00:00
)
2024-03-09 00:36:06 +00:00
# Append mode, with and without coordination.
for coordinated in [ True , False ] :
name = " append " if coordinated else " append_uncoordinated "
refresh_settings = " " if coordinated else " settings all_replicas = 1 "
2024-06-07 01:07:32 +00:00
node2 . query (
f " create materialized view re. { name } refresh every 1 year { refresh_settings } append (x Int64) engine ReplicatedMergeTree order by x as select rand() as x "
)
2024-03-09 00:36:06 +00:00
# Stop the clocks.
for node in nodes :
2024-06-07 01:07:32 +00:00
node . query (
f " system test view re. { name } set fake time ' 2040-01-01 00:00:01 ' "
)
2024-03-09 00:36:06 +00:00
# Wait for quiescence.
for node in nodes :
2024-10-08 04:56:51 +00:00
# Wait twice to make sure we wait for a refresh that started after we adjusted the clock.
# Otherwise another refresh may start right after (because clock moved far forward).
2024-10-08 05:05:41 +00:00
node . query (
f " system wait view re. { name } ; system refresh view re. { name } ; system wait view re. { name } ; "
)
2024-03-09 00:36:06 +00:00
rows_before = int ( nodes [ randint ( 0 , 1 ) ] . query ( f " select count() from re. { name } " ) )
# Advance the clocks.
for node in nodes :
2024-06-07 01:07:32 +00:00
node . query (
f " system test view re. { name } set fake time ' 2041-01-01 00:00:01 ' "
)
2024-03-09 00:36:06 +00:00
# Wait for refresh.
for node in nodes :
2024-06-07 01:07:32 +00:00
assert_eq_with_retry (
node ,
f " select status, last_success_time from system.view_refreshes where view = ' { name } ' " ,
" Scheduled \t 2041-01-01 00:00:01 " ,
)
2024-03-09 00:36:06 +00:00
node . query ( f " system wait view re. { name } " )
# Check results.
2024-10-16 02:20:22 +00:00
node = nodes [ randint ( 0 , 1 ) ]
node . query ( f " system sync replica re. { name } " )
rows_after = int ( node . query ( f " select count() from re. { name } " ) )
2024-03-09 00:36:06 +00:00
expected = 1 if coordinated else 2
assert rows_after - rows_before == expected
# Uncoordinated append to unreplicated table.
2024-06-07 01:07:32 +00:00
node1 . query (
" create materialized view re.unreplicated_uncoordinated refresh every 1 second settings all_replicas = 1 append (x String) engine Memory as select 1 as x "
)
2024-03-09 00:36:06 +00:00
node2 . query ( " system sync database replica re " )
for node in nodes :
node . query ( " system wait view re.unreplicated_uncoordinated " )
2024-06-07 01:07:32 +00:00
assert (
node . query ( " select distinct x from re.unreplicated_uncoordinated " ) == " 1 \n "
)
2024-03-09 00:36:06 +00:00
# Rename.
2024-06-07 01:07:32 +00:00
node2 . query (
" create materialized view re.c refresh every 1 year (x Int64) engine ReplicatedMergeTree order by x empty as select rand() as x "
)
2024-03-09 00:36:06 +00:00
node1 . query ( " system sync database replica re " )
node1 . query ( " rename table re.c to re.d " )
2024-06-07 01:07:32 +00:00
node1 . query (
" alter table re.d modify query select number + sleepEachRow(1) as x from numbers(5) settings max_block_size = 1 "
)
2024-03-09 00:36:06 +00:00
# Rename while refreshing.
node1 . query ( " system refresh view re.d " )
2024-06-07 01:07:32 +00:00
assert_eq_with_retry (
node2 ,
" select status from system.view_refreshes where view = ' d ' " ,
" RunningOnAnotherReplica " ,
)
2024-03-09 00:36:06 +00:00
node2 . query ( " rename table re.d to re.e " )
node1 . query ( " system wait view re.e " )
assert node1 . query ( " select * from re.e order by x " ) == " 0 \n 1 \n 2 \n 3 \n 4 \n "
# A view that will be stuck refreshing until dropped.
2024-06-07 01:07:32 +00:00
node1 . query (
" create materialized view re.f refresh every 1 second (x Int64) engine ReplicatedMergeTree order by x as select sleepEachRow(1) as x from numbers(1000000) settings max_block_size = 1 "
)
assert_eq_with_retry (
node2 ,
" select status in ( ' Running ' , ' RunningOnAnotherReplica ' ) from system.view_refreshes where view = ' f ' " ,
" 1 " ,
)
2024-03-09 00:36:06 +00:00
# Locate coordination znodes.
2024-06-07 01:07:32 +00:00
znode_exists = (
lambda uuid : nodes [ randint ( 0 , 1 ) ] . query (
f " select count() from system.zookeeper where path = ' /clickhouse/tables/ { uuid } ' and name = ' shard1 ' "
)
== " 1 \n "
)
2024-03-09 00:36:06 +00:00
tables = [ ]
2024-06-07 01:07:32 +00:00
for row in node1 . query (
" select table, uuid from system.tables where database = ' re ' "
) . split ( " \n " ) [ : - 1 ] :
2024-03-09 00:36:06 +00:00
name , uuid = row . split ( " \t " )
print ( f " found table { name } { uuid } " )
2024-09-03 02:38:07 +00:00
if name . startswith ( " . " ) or name . startswith ( " _tmp_replace_ " ) :
2024-03-09 00:36:06 +00:00
continue
coordinated = not name . endswith ( " uncoordinated " )
tables . append ( ( name , uuid , coordinated ) )
assert coordinated == znode_exists ( uuid )
2024-06-07 01:07:32 +00:00
assert sorted ( [ name for ( name , _ , _ ) in tables ] ) == [
" a " ,
" append " ,
" append_uncoordinated " ,
" e " ,
" f " ,
" unreplicated_uncoordinated " ,
]
2024-03-09 00:36:06 +00:00
# Drop all tables and check that coordination znodes were deleted.
2024-06-07 01:07:32 +00:00
for name , uuid , coordinated in tables :
2024-03-09 00:36:06 +00:00
maybe_sync = " sync " if randint ( 0 , 1 ) == 0 else " "
nodes [ randint ( 0 , 1 ) ] . query ( f " drop table re. { name } { maybe_sync } " )
# TODO: After https://github.com/ClickHouse/ClickHouse/issues/61065 is done (for MVs, not ReplicatedMergeTree), check the parent znode instead.
assert not znode_exists ( uuid )
# A little stress test dropping MV while it's refreshing, hoping to hit various cases where the
# drop happens while creating/exchanging/dropping the inner table.
for i in range ( 20 ) :
maybe_empty = " empty " if randint ( 0 , 2 ) == 0 else " "
2024-06-07 01:07:32 +00:00
nodes [ randint ( 0 , 1 ) ] . query (
f " create materialized view re.g refresh every 1 second (x Int64) engine ReplicatedMergeTree order by x { maybe_empty } as select 1 as x "
)
2024-03-09 00:36:06 +00:00
r = randint ( 0 , 5 )
if r == 0 :
pass
elif r == 1 :
time . sleep ( randint ( 0 , 100 ) / 1000 )
else :
time . sleep ( randint ( 900 , 1100 ) / 1000 )
nodes [ randint ( 0 , 1 ) ] . query ( " drop table re.g " )
# Check that inner and temp tables were dropped.
for node in nodes :
assert node . query ( " show tables from re " ) == " "
node1 . query ( " drop database re sync " )
node2 . query ( " drop database re sync " )
2024-10-08 03:32:13 +00:00
2024-10-08 04:09:40 +00:00
2024-10-08 03:32:13 +00:00
def test_refreshable_mv_in_system_db ( started_cluster ) :
node1 . query (
" create materialized view system.a refresh every 1 second (x Int64) engine Memory as select number+1 as x from numbers(2); "
" system refresh view system.a; "
)
2024-10-08 07:42:40 +00:00
2024-10-08 03:32:13 +00:00
node1 . restart_clickhouse ( )
node1 . query ( " system refresh view system.a " )
2024-10-08 04:09:40 +00:00
assert node1 . query ( " select count(), sum(x) from system.a " ) == " 2 \t 3 \n "
2024-10-08 07:42:40 +00:00
node1 . query ( " drop table system.a " )