@@ -529,7 +529,7 @@ def delete_random_rows(self):
529
529
def delete_random_rows_lightweight (self ):
530
530
"""
531
531
Lightweight delete a few rows at random.
532
- Not supported with projections!
532
+ Not supported with projections.
533
533
"""
534
534
table_name = get_random_table_name ()
535
535
node = get_random_node_for_table (table_name = table_name )
@@ -744,7 +744,9 @@ def drop_random_index(self):
744
744
node = get_random_node_for_table (table_name = table_name )
745
745
746
746
with And ("waiting for any other mutations on that index to finish" ):
747
- wait_for_mutations_to_finish (node = node , command_like = index_name , timeout = 300 )
747
+ wait_for_mutations_to_finish (
748
+ node = node , command_like = index_name , timeout = 300
749
+ )
748
750
749
751
with And ("dropping the index" ):
750
752
r = node .query (
@@ -793,84 +795,127 @@ def remove_random_ttl(self):
793
795
794
796
795
797
@TestStep (Then )
796
- def check_tables_have_same_columns (self , tables ):
798
+ def check_tables_have_same_columns (self , tables , return_outliers = False ):
797
799
"""
798
800
Asserts that the given tables have the same columns.
799
801
Smartly selects a node for each given replicated table.
800
802
Does not check that all replicas of a table agree.
803
+
804
+ Args:
805
+ tables: List of table names to check.
806
+ return_outliers: If True, return the set of columns that are not present in all tables.
801
807
"""
802
- with When ("I get the columns for each table" ):
803
- table_columns = {}
804
- for table_name in tables :
805
- node = get_random_node_for_table (table_name = table_name )
806
- table_columns [table_name ] = set (
807
- get_column_names (node = node , table_name = table_name )
808
- )
808
+ try :
809
+ with When ("I get the columns for each table" ):
810
+ table_columns = {}
811
+ for table_name in tables :
812
+ node = get_random_node_for_table (table_name = table_name )
813
+ table_columns [table_name ] = set (
814
+ get_column_names (node = node , table_name = table_name )
815
+ )
809
816
810
- with Then ("all tables should have the same columns" ):
811
- for table1 , table2 in combinations (tables , 2 ):
812
- with By (f"checking { table1 } and { table2 } " , flags = TE ):
813
- assert table_columns [table1 ] == table_columns [table2 ], error ()
817
+ with Then ("all tables should have the same columns" ):
818
+ for table1 , table2 in combinations (tables , 2 ):
819
+ with By (f"checking { table1 } and { table2 } " , flags = TE ):
820
+ assert table_columns [table1 ] == table_columns [table2 ], error ()
821
+ finally :
822
+ if return_outliers :
823
+ return set .union (* table_columns .values ()) - set .intersection (
824
+ * table_columns .values ()
825
+ )
826
+ return set ()
814
827
815
828
816
829
@TestStep (Then )
817
830
def check_tables_have_same_projections (
818
- self , tables , check_present : list = None , check_absent : list = None
831
+ self ,
832
+ tables ,
833
+ check_present : list = None ,
834
+ check_absent : list = None ,
835
+ return_outliers = False ,
819
836
):
820
837
"""
821
838
Asserts that the given tables have the same projections.
822
839
Smartly selects a node for each given replicated table.
823
840
Does not check that all replicas of a table agree.
824
- """
825
- with When ("I get the projections for each table" ):
826
- table_projections = {}
827
- for table_name in tables :
828
- node = get_random_node_for_table (table_name = table_name )
829
- wait_for_mutations_to_finish (node = node , command_like = "PROJECTION" )
830
- table_projections [table_name ] = set (
831
- get_projections (node = node , table_name = table_name )
832
- )
833
841
834
- with Then ("all tables should have the same projections" ):
835
- for table1 , table2 in combinations (tables , 2 ):
836
- with By (f"checking { table1 } and { table2 } " , flags = TE ):
837
- assert table_projections [table1 ] == table_projections [table2 ], error ()
838
-
839
- if check_present is not None :
840
- with And (f"I check that { check_present } exist" ):
841
- for projection_name in check_present :
842
- assert projection_name in table_projections [tables [0 ]], error ()
842
+ Args:
843
+ tables: List of table names to check.
844
+ check_present: List of projection names that should be present in all tables.
845
+ check_absent: List of projection names that should not be present in any tables.
846
+ return_outliers: If True, return the set of projections that are not present in all tables.
847
+ """
848
+ try :
849
+ with When ("I get the projections for each table" ):
850
+ table_projections = {}
851
+ for table_name in tables :
852
+ node = get_random_node_for_table (table_name = table_name )
853
+ wait_for_mutations_to_finish (node = node , command_like = "PROJECTION" )
854
+ table_projections [table_name ] = set (
855
+ get_projections (node = node , table_name = table_name )
856
+ )
843
857
844
- if check_absent is not None :
845
- with And (f"I check that { check_absent } do not exist" ):
846
- for projection_name in check_absent :
847
- assert projection_name not in table_projections [tables [0 ]], error ()
858
+ with Then ("all tables should have the same projections" ):
859
+ for table1 , table2 in combinations (tables , 2 ):
860
+ with By (f"checking { table1 } and { table2 } " , flags = TE ):
861
+ assert (
862
+ table_projections [table1 ] == table_projections [table2 ]
863
+ ), error ()
864
+
865
+ if check_present is not None :
866
+ with And (f"I check that { check_present } exist" ):
867
+ for projection_name in check_present :
868
+ assert projection_name in table_projections [tables [0 ]], error ()
869
+
870
+ if check_absent is not None :
871
+ with And (f"I check that { check_absent } do not exist" ):
872
+ for projection_name in check_absent :
873
+ assert projection_name not in table_projections [tables [0 ]], error ()
874
+ finally :
875
+ if return_outliers :
876
+ return set .union (* table_projections .values ()) - set .intersection (
877
+ * table_projections .values ()
878
+ )
879
+ return set ()
848
880
849
881
850
882
@TestStep (Then )
851
- def check_tables_have_same_indexes (self , tables ):
883
+ def check_tables_have_same_indexes (self , tables , return_outliers = False ):
852
884
"""
853
885
Asserts that the given tables have the same indexes.
854
886
Smartly selects a node for each given replicated table.
855
887
Does not check that all replicas of a table agree.
888
+
889
+ Args:
890
+ tables: List of table names to check.
891
+ return_outliers: If True, return the set of indexes that are not present in all tables.
856
892
"""
857
- with When ("I get the indexes for each table" ):
858
- table_indexes = {}
859
- for table_name in tables :
860
- node = get_random_node_for_table (table_name = table_name )
861
- table_indexes [table_name ] = set (
862
- get_indexes (node = node , table_name = table_name )
863
- )
893
+ try :
894
+ with When ("I get the indexes for each table" ):
895
+ table_indexes = {}
896
+ for table_name in tables :
897
+ node = get_random_node_for_table (table_name = table_name )
898
+ table_indexes [table_name ] = set (
899
+ get_indexes (node = node , table_name = table_name )
900
+ )
864
901
865
- with Then ("all tables should have the same indexes" ):
866
- for table1 , table2 in combinations (tables , 2 ):
867
- with By (f"checking { table1 } and { table2 } " , flags = TE ):
868
- assert table_indexes [table1 ] == table_indexes [table2 ], error ()
902
+ with Then ("all tables should have the same indexes" ):
903
+ for table1 , table2 in combinations (tables , 2 ):
904
+ with By (f"checking { table1 } and { table2 } " , flags = TE ):
905
+ assert table_indexes [table1 ] == table_indexes [table2 ], error ()
906
+ finally :
907
+ if return_outliers :
908
+ return set .union (* table_indexes .values ()) - set .intersection (
909
+ * table_indexes .values ()
910
+ )
911
+ return set ()
869
912
870
913
871
914
@TestStep (Then )
872
915
@Retry (timeout = step_retry_timeout , delay = step_retry_delay )
873
- def check_consistency (self , tables = None , sync_timeout = None ):
916
+ def check_consistency (
917
+ self , tables = None , sync_timeout = None , restore_consistent_structure = False
918
+ ):
874
919
"""
875
920
Check that the given tables hold the same amount of data on all nodes where they exist.
876
921
Also check that column names match, subsequent part move tests require matching columns.
@@ -933,10 +978,37 @@ def check_consistency(self, tables=None, sync_timeout=None):
933
978
# The below check also asserts that all tables have the same structure
934
979
# Part move actions require matching structure
935
980
936
- with Then ("check that table structures are in sync" ):
937
- check_tables_have_same_columns (tables = tables )
938
- check_tables_have_same_projections (tables = tables )
939
- check_tables_have_same_indexes (tables = tables )
981
+ try :
982
+ with Then ("check that table structures are in sync" , flags = TE ):
983
+ outlier_columns = check_tables_have_same_columns (
984
+ tables = tables , return_outliers = restore_consistent_structure
985
+ )
986
+ outlier_projections = check_tables_have_same_projections (
987
+ tables = tables , return_outliers = restore_consistent_structure
988
+ )
989
+ outlier_indexes = check_tables_have_same_indexes (
990
+ tables = tables , return_outliers = restore_consistent_structure
991
+ )
992
+ finally :
993
+ if restore_consistent_structure and any (
994
+ [outlier_columns , outlier_projections , outlier_indexes ]
995
+ ):
996
+ with Finally ("I clean up any inconsistencies between tables" ):
997
+ for table_name in tables :
998
+ node = get_random_node_for_table (table_name = table_name )
999
+
1000
+ for projection in outlier_projections :
1001
+ node .query (
1002
+ f"ALTER TABLE { table_name } DROP PROJECTION IF EXISTS { projection } "
1003
+ )
1004
+ for index in outlier_indexes :
1005
+ node .query (
1006
+ f"ALTER TABLE { table_name } DROP INDEX IF EXISTS { index } "
1007
+ )
1008
+ for column in outlier_columns :
1009
+ node .query (
1010
+ f"ALTER TABLE { table_name } DROP COLUMN IF EXISTS { column } "
1011
+ )
940
1012
941
1013
942
1014
@TestStep
0 commit comments