@@ -384,3 +384,81 @@ async def create_fail(rfs: Union[List[int], int], failed_dc: int, rf: int, rack_
384
384
async with asyncio .TaskGroup () as tg :
385
385
for task in [* valid_keyspaces , * invalid_keyspaces ]:
386
386
_ = tg .create_task (task )
387
+
388
+ async def test_startup_with_keyspaces_violating_rf_rack_valid_keyspaces (manager : ManagerClient ):
389
+ """
390
+ This test verifies that starting a Scylla node fails when there's an RF-rack-invalid keyspace.
391
+ We aim to simulate the behavior of a node when upgrading to 2025.*.
392
+
393
+ For more context, see: scylladb/scylladb#23300.
394
+ """
395
+
396
+ cfg_false = {"rf_rack_valid_keyspaces" : "false" }
397
+
398
+ s1 = await manager .server_add (config = cfg_false , property_file = {"dc" : "dc1" , "rack" : "r1" })
399
+ _ = await manager .server_add (config = cfg_false , property_file = {"dc" : "dc1" , "rack" : "r2" })
400
+ _ = await manager .server_add (config = cfg_false , property_file = {"dc" : "dc1" , "rack" : "r3" })
401
+ _ = await manager .server_add (config = cfg_false , property_file = {"dc" : "dc2" , "rack" : "r4" })
402
+ # Note: This rack should behave as if it never existed.
403
+ _ = await manager .server_add (config = {"join_ring" : "false" , "rf_rack_valid_keyspaces" : "false" }, property_file = {"dc" : "dc1" , "rack" : "rzerotoken" })
404
+
405
+ # Current situation:
406
+ # DC1: {r1, r2, r3}, DC2: {r4}
407
+
408
+ cql = manager .get_cql ()
409
+
410
+ async def create_keyspace (rfs : List [int ], tablets : bool ) -> str :
411
+ dcs = ", " .join ([f"'dc{ i + 1 } ': { rf } " for i , rf in enumerate (rfs )])
412
+ name = unique_name ()
413
+ tablets = str (tablets ).lower ()
414
+ await cql .run_async (f"CREATE KEYSPACE { name } WITH REPLICATION = {{'class': 'NetworkTopologyStrategy', { dcs } }} "
415
+ f"AND tablets = {{'enabled': { tablets } }}" )
416
+ logger .info (f"Created keyspace { name } with { rfs } and tablets={ tablets } " )
417
+ return name
418
+
419
+ valid_keyspaces = [
420
+ # For each DC: RF \in {0, 1, #racks}.
421
+ ([0 , 0 ], True ),
422
+ ([1 , 0 ], True ),
423
+ ([3 , 0 ], True ),
424
+ ([1 , 1 ], True ),
425
+ ([3 , 1 ], True ),
426
+ # Reminder: Keyspaces not using tablets are all valid.
427
+ ([0 , 0 ], False ),
428
+ ([1 , 0 ], False ),
429
+ ([2 , 0 ], False ),
430
+ ([3 , 0 ], False ),
431
+ ([4 , 0 ], False ),
432
+ ([0 , 1 ], False ),
433
+ ([1 , 1 ], False ),
434
+ ([2 , 1 ], False ),
435
+ ([3 , 1 ], False ),
436
+ ([4 , 1 ], False ),
437
+ ([0 , 2 ], False ),
438
+ ([1 , 2 ], False ),
439
+ ([2 , 2 ], False ),
440
+ ([3 , 2 ], False ),
441
+ ([4 , 2 ], False )
442
+ ]
443
+
444
+ # Populate RF-rack-valid keyspaces.
445
+ async with asyncio .TaskGroup () as tg :
446
+ for rfs , tablets in valid_keyspaces :
447
+ _ = tg .create_task (create_keyspace (rfs , tablets ))
448
+
449
+ await manager .server_stop_gracefully (s1 .server_id )
450
+ await manager .server_update_config (s1 .server_id , "rf_rack_valid_keyspaces" , "true" )
451
+
452
+ async def try_fail (rfs : List [int ], dc : str , rf : int , rack_count : int ):
453
+ ks = await create_keyspace (rfs , True )
454
+ err = r"The option `rf_rack_valid_keyspaces` is enabled. It requires that all keyspaces are RF-rack-valid. " \
455
+ f"That condition is violated: keyspace '{ ks } ' doesn't satisfy it for DC '{ dc } ': RF={ rf } vs. rack count={ rack_count } ."
456
+ _ = await manager .server_start (s1 .server_id , expected_error = err )
457
+ await cql .run_async (f"DROP KEYSPACE { ks } " )
458
+
459
+ # Test RF-rack-invalid keyspaces.
460
+ await try_fail ([2 , 0 ], "dc1" , 2 , 3 )
461
+ await try_fail ([3 , 2 ], "dc2" , 2 , 1 )
462
+ await try_fail ([4 , 1 ], "dc1" , 4 , 3 )
463
+
464
+ _ = await manager .server_start (s1 .server_id )
0 commit comments