@@ -1179,10 +1179,8 @@ def test_json():
11791179
11801180
11811181def test_string_primary_key (monkeypatch ):
1182- monkeypatch .setattr (DbReplicatorInitial , 'INITIAL_REPLICATION_BATCH_SIZE' , 1 )
1183-
11841182 cfg = config .Settings ()
1185- cfg .load (CONFIG_FILE )
1183+ cfg .load ('tests_config_string_primary_key.yaml' )
11861184
11871185 mysql = mysql_api .MySQLApi (
11881186 database = None ,
@@ -1217,9 +1215,9 @@ def test_string_primary_key(monkeypatch):
12171215 commit = True ,
12181216 )
12191217
1220- binlog_replicator_runner = BinlogReplicatorRunner ()
1218+ binlog_replicator_runner = BinlogReplicatorRunner (cfg_file = 'tests_config_string_primary_key.yaml' )
12211219 binlog_replicator_runner .run ()
1222- db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME )
1220+ db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME , cfg_file = 'tests_config_string_primary_key.yaml' )
12231221 db_replicator_runner .run ()
12241222
12251223 assert_wait (lambda : TEST_DB_NAME in ch .get_databases ())
@@ -1241,10 +1239,8 @@ def test_string_primary_key(monkeypatch):
12411239
12421240
12431241def test_if_exists_if_not_exists (monkeypatch ):
1244- monkeypatch .setattr (DbReplicatorInitial , 'INITIAL_REPLICATION_BATCH_SIZE' , 1 )
1245-
12461242 cfg = config .Settings ()
1247- cfg .load (CONFIG_FILE )
1243+ cfg .load ('tests_config_string_primary_key.yaml' )
12481244
12491245 mysql = mysql_api .MySQLApi (
12501246 database = None ,
@@ -1258,9 +1254,9 @@ def test_if_exists_if_not_exists(monkeypatch):
12581254
12591255 prepare_env (cfg , mysql , ch )
12601256
1261- binlog_replicator_runner = BinlogReplicatorRunner ()
1257+ binlog_replicator_runner = BinlogReplicatorRunner (cfg_file = 'tests_config_string_primary_key.yaml' )
12621258 binlog_replicator_runner .run ()
1263- db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME )
1259+ db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME , cfg_file = 'tests_config_string_primary_key.yaml' )
12641260 db_replicator_runner .run ()
12651261
12661262 assert_wait (lambda : TEST_DB_NAME in ch .get_databases ())
@@ -1282,10 +1278,8 @@ def test_if_exists_if_not_exists(monkeypatch):
12821278
12831279
12841280def test_percona_migration (monkeypatch ):
1285- monkeypatch .setattr (DbReplicatorInitial , 'INITIAL_REPLICATION_BATCH_SIZE' , 1 )
1286-
12871281 cfg = config .Settings ()
1288- cfg .load (CONFIG_FILE )
1282+ cfg .load ('tests_config_string_primary_key.yaml' )
12891283
12901284 mysql = mysql_api .MySQLApi (
12911285 database = None ,
@@ -1310,9 +1304,9 @@ def test_percona_migration(monkeypatch):
13101304 commit = True ,
13111305 )
13121306
1313- binlog_replicator_runner = BinlogReplicatorRunner ()
1307+ binlog_replicator_runner = BinlogReplicatorRunner (cfg_file = 'tests_config_string_primary_key.yaml' )
13141308 binlog_replicator_runner .run ()
1315- db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME )
1309+ db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME , cfg_file = 'tests_config_string_primary_key.yaml' )
13161310 db_replicator_runner .run ()
13171311
13181312 assert_wait (lambda : TEST_DB_NAME in ch .get_databases ())
@@ -1360,10 +1354,8 @@ def test_percona_migration(monkeypatch):
13601354
13611355
13621356def test_add_column_first_after_and_drop_column (monkeypatch ):
1363- monkeypatch .setattr (DbReplicatorInitial , 'INITIAL_REPLICATION_BATCH_SIZE' , 1 )
1364-
13651357 cfg = config .Settings ()
1366- cfg .load (CONFIG_FILE )
1358+ cfg .load ('tests_config_string_primary_key.yaml' )
13671359
13681360 mysql = mysql_api .MySQLApi (
13691361 database = None ,
@@ -1388,9 +1380,9 @@ def test_add_column_first_after_and_drop_column(monkeypatch):
13881380 commit = True ,
13891381 )
13901382
1391- binlog_replicator_runner = BinlogReplicatorRunner ()
1383+ binlog_replicator_runner = BinlogReplicatorRunner (cfg_file = 'tests_config_string_primary_key.yaml' )
13921384 binlog_replicator_runner .run ()
1393- db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME )
1385+ db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME , cfg_file = 'tests_config_string_primary_key.yaml' )
13941386 db_replicator_runner .run ()
13951387
13961388 assert_wait (lambda : TEST_DB_NAME in ch .get_databases ())
@@ -2912,3 +2904,124 @@ def test_timezone_conversion():
29122904 finally :
29132905 # Clean up temporary config file
29142906 os .unlink (temp_config_file )
2907+
2908+ def test_resume_initial_replication_with_ignore_deletes ():
2909+ """
2910+ Test that resuming initial replication works correctly with ignore_deletes=True.
2911+
2912+ This reproduces the bug from https://github.com/bakwc/mysql_ch_replicator/issues/172
2913+ where resuming initial replication would fail with "Database sirocco_tmp does not exist"
2914+ when ignore_deletes=True because the code would try to use the _tmp database instead
2915+ of the target database directly.
2916+ """
2917+ # Create a temporary config file with ignore_deletes=True
2918+ with tempfile .NamedTemporaryFile (mode = 'w' , suffix = '.yaml' , delete = False ) as temp_config_file :
2919+ config_file = temp_config_file .name
2920+
2921+ # Read the original config
2922+ with open (CONFIG_FILE , 'r' ) as original_config :
2923+ config_data = yaml .safe_load (original_config )
2924+
2925+ # Add ignore_deletes=True
2926+ config_data ['ignore_deletes' ] = True
2927+
2928+ # Set initial_replication_batch_size to 1 for testing
2929+ config_data ['initial_replication_batch_size' ] = 1
2930+
2931+ # Write to the temp file
2932+ yaml .dump (config_data , temp_config_file )
2933+
2934+ try :
2935+ cfg = config .Settings ()
2936+ cfg .load (config_file )
2937+
2938+ # Verify the ignore_deletes option was set
2939+ assert cfg .ignore_deletes is True
2940+
2941+ mysql = mysql_api .MySQLApi (
2942+ database = None ,
2943+ mysql_settings = cfg .mysql ,
2944+ )
2945+
2946+ ch = clickhouse_api .ClickhouseApi (
2947+ database = TEST_DB_NAME ,
2948+ clickhouse_settings = cfg .clickhouse ,
2949+ )
2950+
2951+ prepare_env (cfg , mysql , ch )
2952+
2953+ # Create a table with many records to ensure initial replication takes time
2954+ mysql .execute (f'''
2955+ CREATE TABLE `{ TEST_TABLE_NAME } ` (
2956+ id int NOT NULL AUTO_INCREMENT,
2957+ name varchar(255),
2958+ data varchar(1000),
2959+ PRIMARY KEY (id)
2960+ )
2961+ ''' )
2962+
2963+ # Insert many records to make initial replication take longer
2964+ for i in range (100 ):
2965+ mysql .execute (
2966+ f"INSERT INTO `{ TEST_TABLE_NAME } ` (name, data) VALUES ('test_{ i } ', 'data_{ i } ');" ,
2967+ commit = True
2968+ )
2969+
2970+ # Start binlog replicator
2971+ binlog_replicator_runner = BinlogReplicatorRunner (cfg_file = config_file )
2972+ binlog_replicator_runner .run ()
2973+
2974+ # Start db replicator for initial replication with test flag to exit early
2975+ db_replicator_runner = DbReplicatorRunner (TEST_DB_NAME , cfg_file = config_file ,
2976+ additional_arguments = '--initial-replication-test-fail-records 30' )
2977+ db_replicator_runner .run ()
2978+
2979+ # Wait for initial replication to start
2980+ assert_wait (lambda : TEST_DB_NAME in ch .get_databases ())
2981+ ch .execute_command (f'USE `{ TEST_DB_NAME } `' )
2982+ assert_wait (lambda : TEST_TABLE_NAME in ch .get_tables ())
2983+
2984+ # Wait for some records to be replicated but not all (should hit the 30 record limit)
2985+ assert_wait (lambda : len (ch .select (TEST_TABLE_NAME )) > 0 )
2986+
2987+ # The db replicator should have stopped automatically due to the test flag
2988+ # But we still call stop() to ensure proper cleanup
2989+ db_replicator_runner .stop ()
2990+
2991+ # Verify the state is still PERFORMING_INITIAL_REPLICATION
2992+ state_path = os .path .join (cfg .binlog_replicator .data_dir , TEST_DB_NAME , 'state.pckl' )
2993+ state = DbReplicatorState (state_path )
2994+ assert state .status .value == 2 # PERFORMING_INITIAL_REPLICATION
2995+
2996+ # Add more records while replication is stopped
2997+ for i in range (100 , 150 ):
2998+ mysql .execute (
2999+ f"INSERT INTO `{ TEST_TABLE_NAME } ` (name, data) VALUES ('test_{ i } ', 'data_{ i } ');" ,
3000+ commit = True
3001+ )
3002+
3003+ # Verify that sirocco_tmp database does NOT exist (it should use sirocco directly)
3004+ assert f"{ TEST_DB_NAME } _tmp" not in ch .get_databases (), "Temporary database should not exist with ignore_deletes=True"
3005+
3006+ # Resume initial replication - this should NOT fail with "Database sirocco_tmp does not exist"
3007+ db_replicator_runner_2 = DbReplicatorRunner (TEST_DB_NAME , cfg_file = config_file )
3008+ db_replicator_runner_2 .run ()
3009+
3010+ # Wait for all records to be replicated (100 original + 50 extra = 150)
3011+ assert_wait (lambda : len (ch .select (TEST_TABLE_NAME )) == 150 , max_wait_time = 30 )
3012+
3013+ # Verify the replication completed successfully
3014+ records = ch .select (TEST_TABLE_NAME )
3015+ assert len (records ) == 150 , f"Expected 150 records, got { len (records )} "
3016+
3017+ # Verify we can continue with realtime replication
3018+ mysql .execute (f"INSERT INTO `{ TEST_TABLE_NAME } ` (name, data) VALUES ('realtime_test', 'realtime_data');" , commit = True )
3019+ assert_wait (lambda : len (ch .select (TEST_TABLE_NAME )) == 151 )
3020+
3021+ # Clean up
3022+ db_replicator_runner_2 .stop ()
3023+ binlog_replicator_runner .stop ()
3024+
3025+ finally :
3026+ # Clean up temp config file
3027+ os .unlink (config_file )
0 commit comments