def test_node_busy(self): """ Verify that once TCP buffer is full, queries continue to get re-routed to other nodes """ start_and_prime_singledc() profile = ExecutionProfile(load_balancing_policy=RoundRobinPolicy()) cluster = Cluster( protocol_version=PROTOCOL_VERSION, compression=False, execution_profiles={EXEC_PROFILE_DEFAULT: profile}, ) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) query = session.prepare("INSERT INTO table1 (id) VALUES (?)") prime_request(PauseReads(dc_id=0, node_id=0)) blocked_profile = ExecutionProfile( load_balancing_policy=WhiteListRoundRobinPolicy(["127.0.0.1"])) cluster.add_execution_profile('blocked_profile', blocked_profile) # Fill our blocked node's tcp buffer until we get a busy exception self._fill_buffers(session, query, expected_blocked=1, execution_profile='blocked_profile') # Now that our send buffer is completely full on one node, # verify queries get re-routed to other nodes and queries complete successfully for i in range(1000): session.execute(query, [str(i)])
def test_max_in_flight(self): """ Verify we don't exceed max_in_flight when borrowing connections or sending heartbeats """ Connection.max_in_flight = 50 start_and_prime_singledc() profile = ExecutionProfile(request_timeout=1, load_balancing_policy=WhiteListRoundRobinPolicy(['127.0.0.1'])) cluster = Cluster( protocol_version=PROTOCOL_VERSION, compression=False, execution_profiles={EXEC_PROFILE_DEFAULT: profile}, idle_heartbeat_interval=.1, idle_heartbeat_timeout=.1, ) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) query = session.prepare("INSERT INTO table1 (id) VALUES (?)") prime_request(PauseReads()) futures = [] # + 50 because simulacron doesn't immediately block all queries for i in range(Connection.max_in_flight + 50): futures.append(session.execute_async(query, ['a'])) prime_request(ResumeReads()) for future in futures: # We're veryfing we don't get an assertion error from Connection.get_request_id, # so skip any valid errors try: future.result() except OperationTimedOut: pass except NoHostAvailable: pass
def test_paused_connections(self): """ Verify all requests come back as expected if node resumes within query timeout """ start_and_prime_singledc() profile = ExecutionProfile(request_timeout=500, load_balancing_policy=RoundRobinPolicy()) cluster = Cluster( protocol_version=PROTOCOL_VERSION, compression=False, execution_profiles={EXEC_PROFILE_DEFAULT: profile}, ) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) query = session.prepare("INSERT INTO table1 (id) VALUES (?)") prime_request(PauseReads()) futures = self._fill_buffers(session, query) # Make sure we actually have some stuck in-flight requests for in_flight in [ pool._connection.in_flight for pool in session.get_pools() ]: self.assertGreater(in_flight, 100) time.sleep(.5) for in_flight in [ pool._connection.in_flight for pool in session.get_pools() ]: self.assertGreater(in_flight, 100) prime_request(ResumeReads()) for future in futures: try: future.result() except NoHostAvailable as e: # We shouldn't have any timeouts here, but all of the queries beyond what can fit # in the tcp buffer will have returned with a ConnectionBusy exception self.assertIn("ConnectionBusy", str(e)) # Verify that we can continue sending queries without any problems for host in session.cluster.metadata.all_hosts(): session.execute(query, ["a"], host=host)
def _prime_testtable_query(): queries = [ 'SELECT "", " " FROM testks.testtable', 'SELECT "", " " FROM testks.testtable LIMIT 10000' # cqlengine ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [{ "": "testval", " ": "testval1" }], 'column_types': { "": "ascii", " ": "ascii" }, 'ignore_on_prepare': False } for query in queries: prime_request(PrimeQuery(query, then=then))
def test_driver_recovers_nework_isolation(self): start_and_prime_singledc() idle_heartbeat_timeout = 3 idle_heartbeat_interval = 1 listener = TrackDownListener() cluster = Cluster(['127.0.0.1'], load_balancing_policy=RoundRobinPolicy(), idle_heartbeat_timeout=idle_heartbeat_timeout, idle_heartbeat_interval=idle_heartbeat_interval, executor_threads=16) session = cluster.connect(wait_for_all_pools=True) cluster.register_listener(listener) prime_request(PrimeOptions(then=NO_THEN)) prime_request(RejectConnections(RejectType.REJECT_STARTUP)) time.sleep((idle_heartbeat_timeout + idle_heartbeat_interval) * 2) for host in cluster.metadata.all_hosts(): self.assertIn(host, listener.hosts_marked_down) self.assertRaises(NoHostAvailable, session.execute, "SELECT * from system.local") clear_queries() prime_request(AcceptConnections()) time.sleep(idle_heartbeat_timeout + idle_heartbeat_interval + 2) self.assertIsNotNone(session.execute("SELECT * from system.local"))
def test_driver_recovers_nework_isolation(self): start_and_prime_singledc() idle_heartbeat_timeout = 3 idle_heartbeat_interval = 1 listener = TrackDownListener() cluster = Cluster(['127.0.0.1'], load_balancing_policy=RoundRobinPolicy(), idle_heartbeat_timeout=idle_heartbeat_timeout, idle_heartbeat_interval=idle_heartbeat_interval, executor_threads=16) session = cluster.connect(wait_for_all_pools=True) cluster.register_listener(listener) prime_request(PrimeOptions(then=NO_THEN)) prime_request(RejectConnections(RejectType.REJECT_STARTUP)) time.sleep((idle_heartbeat_timeout + idle_heartbeat_interval) * 2) for host in cluster.metadata.all_hosts(): self.assertIn(host, listener.hosts_marked_down) self.assertRaises(NoHostAvailable, session.execute, "SELECT * from system.local") clear_queries() prime_request(AcceptConnections()) time.sleep(idle_heartbeat_timeout + idle_heartbeat_interval + 2) self.assertIsNotNone(session.execute("SELECT * from system.local"))
def _prime_testtable_query(): queries = [ 'SELECT "", " " FROM testks.testtable', 'SELECT "", " " FROM testks.testtable LIMIT 10000' # cqlengine ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [ { "": "testval", " ": "testval1" } ], 'column_types': { "": "ascii", " ": "ascii" }, 'ignore_on_prepare': False } for query in queries: prime_request(PrimeQuery(query, then=then))
def test_queued_requests_timeout(self): """ Verify that queued requests timeout as expected """ start_and_prime_singledc() profile = ExecutionProfile(request_timeout=.1, load_balancing_policy=RoundRobinPolicy()) cluster = Cluster( protocol_version=PROTOCOL_VERSION, compression=False, execution_profiles={EXEC_PROFILE_DEFAULT: profile}, ) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) query = session.prepare("INSERT INTO table1 (id) VALUES (?)") prime_request(PauseReads()) futures = [] for i in range(1000): future = session.execute_async(query, [str(i)]) future.add_callbacks(callback=self.callback_success, errback=self.callback_error) futures.append(future) successes = 0 for future in futures: try: future.result() successes += 1 except OperationTimedOut: pass # Simulacron will respond to a couple queries before cutting off reads, so we'll just verify # that only "a few" successes happened here self.assertLess(successes, 50) self.assertLess(self.callback_successes, 50) self.assertEqual(self.callback_errors, len(futures) - self.callback_successes)
def test_cluster_busy(self): """ Verify that once TCP buffer is full we get busy exceptions rather than timeouts """ start_and_prime_singledc() profile = ExecutionProfile(load_balancing_policy=RoundRobinPolicy()) cluster = Cluster( protocol_version=PROTOCOL_VERSION, compression=False, execution_profiles={EXEC_PROFILE_DEFAULT: profile}, ) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) query = session.prepare("INSERT INTO table1 (id) VALUES (?)") prime_request(PauseReads()) # These requests will get stuck in the TCP buffer and we have no choice but to let them time out self._fill_buffers(session, query, expected_blocked=3) # Now that our send buffer is completely full, verify we immediately get busy exceptions rather than timing out for i in range(1000): with self.assertRaises(NoHostAvailable) as e: session.execute(query, [str(i)]) self.assertIn("ConnectionBusy", str(e.exception))
def test_heart_beat_timeout(self): """ Test to ensure the hosts are marked as down after a OTO is received. Also to ensure this happens within the expected timeout @since 3.10 @jira_ticket PYTHON-762 @expected_result all the hosts have been marked as down at some point @test_category metadata """ number_of_dcs = 3 nodes_per_dc = 20 query_to_prime = "INSERT INTO test3rf.test (k, v) VALUES (0, 1);" idle_heartbeat_timeout = 5 idle_heartbeat_interval = 1 start_and_prime_cluster_defaults(number_of_dcs, nodes_per_dc) listener = TrackDownListener() executor = ThreadTracker(max_workers=8) # We need to disable compression since it's not supported in simulacron cluster = Cluster( compression=False, idle_heartbeat_interval=idle_heartbeat_interval, idle_heartbeat_timeout=idle_heartbeat_timeout, executor_threads=8, execution_profiles={ EXEC_PROFILE_DEFAULT: ExecutionProfile(load_balancing_policy=RoundRobinPolicy()) }) self.addCleanup(cluster.shutdown) cluster.scheduler.shutdown() cluster.executor = executor cluster.scheduler = _Scheduler(executor) session = cluster.connect(wait_for_all_pools=True) cluster.register_listener(listener) log = logging.getLogger() log.setLevel('CRITICAL') self.addCleanup(log.setLevel, "DEBUG") prime_query(query_to_prime, then=NO_THEN) futures = [] for _ in range(number_of_dcs * nodes_per_dc): future = session.execute_async(query_to_prime) futures.append(future) for f in futures: f._event.wait() self.assertIsInstance(f._final_exception, OperationTimedOut) prime_request(PrimeOptions(then=NO_THEN)) # We allow from some extra time for all the hosts to be to on_down # The callbacks should start happening after idle_heartbeat_timeout + idle_heartbeat_interval time.sleep((idle_heartbeat_timeout + idle_heartbeat_interval) * 2.5) for host in cluster.metadata.all_hosts(): self.assertIn(host, listener.hosts_marked_down) # In this case HostConnection._replace shouldn't be called self.assertNotIn("_replace", executor.called_functions)
def test_retry_after_defunct(self): """ We test cluster._retry is called if an the connection is defunct in the middle of a query Finally we verify the driver recovers correctly in the event of a network partition @since 3.12 @expected_result the driver is able to query even if a host is marked as down in the middle of the query, it will go to the next one if the timeout hasn't expired @test_category connection """ number_of_dcs = 3 nodes_per_dc = 2 query_to_prime = "INSERT INTO test3rf.test (k, v) VALUES (0, 1);" idle_heartbeat_timeout = 1 idle_heartbeat_interval = 5 simulacron_cluster = start_and_prime_cluster_defaults( number_of_dcs, nodes_per_dc) dc_ids = sorted(simulacron_cluster.data_center_ids) last_host = dc_ids.pop() prime_query(query_to_prime, cluster_name="{}/{}".format( simulacron_cluster.cluster_name, last_host)) roundrobin_lbp = OrderedRoundRobinPolicy() cluster = Cluster( compression=False, idle_heartbeat_interval=idle_heartbeat_interval, idle_heartbeat_timeout=idle_heartbeat_timeout, execution_profiles={ EXEC_PROFILE_DEFAULT: ExecutionProfile(load_balancing_policy=roundrobin_lbp) }) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) # This simulates we only have access to one DC for dc_id in dc_ids: datacenter_path = "{}/{}".format(simulacron_cluster.cluster_name, dc_id) prime_query(query_to_prime, then=NO_THEN, cluster_name=datacenter_path) prime_request( PrimeOptions(then=NO_THEN, cluster_name=datacenter_path)) # Only the last datacenter will respond, therefore the first host won't # We want to make sure the returned hosts are 127.0.0.1, 127.0.0.2, ... 127.0.0.8 roundrobin_lbp._position = 0 # After 3 + 1 seconds the connection should be marked and down and another host retried response_future = session.execute_async( query_to_prime, timeout=4 * idle_heartbeat_interval + idle_heartbeat_timeout) response_future.result() self.assertGreater(len(response_future.attempted_hosts), 1) # No error should be raised here since the hosts have been marked # as down and there's still 1 DC available for _ in range(10): session.execute(query_to_prime) # Might take some time to close the previous connections and reconnect time.sleep(10) assert_quiescent_pool_state(self, cluster) clear_queries() time.sleep(10) assert_quiescent_pool_state(self, cluster)
def test_heartbeat_defunct_deadlock(self): """ Ensure that there is no deadlock when request is in-flight and heartbeat defuncts connection @since 3.16 @jira_ticket PYTHON-1044 @expected_result an OperationTimeout is raised and no deadlock occurs @test_category connection """ start_and_prime_singledc() # This is all about timing. We will need the QUERY response future to time out and the heartbeat to defunct # at the same moment. The latter will schedule a QUERY retry to another node in case the pool is not # already shut down. If and only if the response future timeout falls in between the retry scheduling and # its execution the deadlock occurs. The odds are low, so we need to help fate a bit: # 1) Make one heartbeat messages be sent to every node # 2) Our QUERY goes always to the same host # 3) This host needs to defunct first # 4) Open a small time window for the response future timeout, i.e. block executor threads for retry # execution and last connection to defunct query_to_prime = "SELECT * from testkesypace.testtable" query_host = "127.0.0.2" heartbeat_interval = 1 heartbeat_timeout = 1 lag = 0.05 never = 9999 class PatchedRoundRobinPolicy(RoundRobinPolicy): # Send always to same host def make_query_plan(self, working_keyspace=None, query=None): if query and query.query_string == query_to_prime: return filter(lambda h: h == query_host, self._live_hosts) else: return super(PatchedRoundRobinPolicy, self).make_query_plan() class PatchedCluster(Cluster): # Make sure that QUERY connection will timeout first def get_connection_holders(self): holders = super(PatchedCluster, self).get_connection_holders() return sorted( holders, reverse=True, key=lambda v: int(v._connection.host == query_host)) # Block executor thread like closing a dead socket could do def connection_factory(self, *args, **kwargs): conn = super(PatchedCluster, self).connection_factory(*args, **kwargs) conn.defunct = late(seconds=2 * lag)(conn.defunct) return conn cluster = PatchedCluster( protocol_version=PROTOCOL_VERSION, compression=False, idle_heartbeat_interval=heartbeat_interval, idle_heartbeat_timeout=heartbeat_timeout, load_balancing_policy=PatchedRoundRobinPolicy()) session = cluster.connect() self.addCleanup(cluster.shutdown) prime_query(query_to_prime, then={"delay_in_ms": never}) # Make heartbeat due time.sleep(heartbeat_interval) future = session.execute_async(query_to_prime, timeout=heartbeat_interval + heartbeat_timeout + 3 * lag) # Delay thread execution like kernel could do future._retry_task = late(seconds=4 * lag)(future._retry_task) prime_request( PrimeOptions(then={ "result": "no_result", "delay_in_ms": never })) prime_request(RejectConnections("unbind")) self.assertRaisesRegexp(OperationTimedOut, "Connection defunct by heartbeat", future.result)
def test_heart_beat_timeout(self): """ Test to ensure the hosts are marked as down after a OTO is received. Also to ensure this happens within the expected timeout @since 3.10 @jira_ticket PYTHON-762 @expected_result all the hosts have been marked as down at some point @test_category metadata """ number_of_dcs = 3 nodes_per_dc = 20 query_to_prime = "INSERT INTO test3rf.test (k, v) VALUES (0, 1);" idle_heartbeat_timeout = 5 idle_heartbeat_interval = 1 start_and_prime_cluster_defaults(number_of_dcs, nodes_per_dc) listener = TrackDownListener() executor = ThreadTracker(max_workers=8) # We need to disable compression since it's not supported in simulacron cluster = Cluster(compression=False, idle_heartbeat_interval=idle_heartbeat_interval, idle_heartbeat_timeout=idle_heartbeat_timeout, executor_threads=8, execution_profiles={ EXEC_PROFILE_DEFAULT: ExecutionProfile(load_balancing_policy=RoundRobinPolicy())}) self.addCleanup(cluster.shutdown) cluster.scheduler.shutdown() cluster.executor = executor cluster.scheduler = _Scheduler(executor) session = cluster.connect(wait_for_all_pools=True) cluster.register_listener(listener) log = logging.getLogger() log.setLevel('CRITICAL') self.addCleanup(log.setLevel, "DEBUG") prime_query(query_to_prime, then=NO_THEN) futures = [] for _ in range(number_of_dcs * nodes_per_dc): future = session.execute_async(query_to_prime) futures.append(future) for f in futures: f._event.wait() self.assertIsInstance(f._final_exception, OperationTimedOut) prime_request(PrimeOptions(then=NO_THEN)) # We allow from some extra time for all the hosts to be to on_down # The callbacks should start happening after idle_heartbeat_timeout + idle_heartbeat_interval time.sleep((idle_heartbeat_timeout + idle_heartbeat_interval) * 2.5) for host in cluster.metadata.all_hosts(): self.assertIn(host, listener.hosts_marked_down) # In this case HostConnection._replace shouldn't be called self.assertNotIn("_replace", executor.called_functions)
def test_retry_after_defunct(self): """ We test cluster._retry is called if an the connection is defunct in the middle of a query Finally we verify the driver recovers correctly in the event of a network partition @since 3.12 @expected_result the driver is able to query even if a host is marked as down in the middle of the query, it will go to the next one if the timeout hasn't expired @test_category connection """ number_of_dcs = 3 nodes_per_dc = 2 query_to_prime = "INSERT INTO test3rf.test (k, v) VALUES (0, 1);" idle_heartbeat_timeout = 1 idle_heartbeat_interval = 5 simulacron_cluster = start_and_prime_cluster_defaults(number_of_dcs, nodes_per_dc) dc_ids = sorted(simulacron_cluster.data_center_ids) last_host = dc_ids.pop() prime_query(query_to_prime, cluster_name="{}/{}".format(simulacron_cluster.cluster_name, last_host)) roundrobin_lbp = OrderedRoundRobinPolicy() cluster = Cluster(compression=False, idle_heartbeat_interval=idle_heartbeat_interval, idle_heartbeat_timeout=idle_heartbeat_timeout, execution_profiles={ EXEC_PROFILE_DEFAULT: ExecutionProfile(load_balancing_policy=roundrobin_lbp)}) session = cluster.connect(wait_for_all_pools=True) self.addCleanup(cluster.shutdown) # This simulates we only have access to one DC for dc_id in dc_ids: datacenter_path = "{}/{}".format(simulacron_cluster.cluster_name, dc_id) prime_query(query_to_prime, then=NO_THEN, cluster_name=datacenter_path) prime_request(PrimeOptions(then=NO_THEN, cluster_name=datacenter_path)) # Only the last datacenter will respond, therefore the first host won't # We want to make sure the returned hosts are 127.0.0.1, 127.0.0.2, ... 127.0.0.8 roundrobin_lbp._position = 0 # After 3 + 1 seconds the connection should be marked and down and another host retried response_future = session.execute_async(query_to_prime, timeout=4 * idle_heartbeat_interval + idle_heartbeat_timeout) response_future.result() self.assertGreater(len(response_future.attempted_hosts), 1) # No error should be raised here since the hosts have been marked # as down and there's still 1 DC available for _ in range(10): session.execute(query_to_prime) # Might take some time to close the previous connections and reconnect time.sleep(10) assert_quiescent_pool_state(self, cluster) clear_queries() time.sleep(10) assert_quiescent_pool_state(self, cluster)
def test_empty_columns_in_system_schema(self): queries = [ "SELECT * FROM system_schema.tables", "SELECT * FROM system.schema.tables", "SELECT * FROM system.schema_columnfamilies" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [{ "compression": dict(), "compaction": dict(), "bloom_filter_fp_chance": 0.1, "caching": { "keys": "ALL", "rows_per_partition": "NONE" }, "comment": "comment", "gc_grace_seconds": 60000, "keyspace_name": "testks", "table_name": "testtable", "columnfamily_name": "testtable", # C* 2.2 "flags": ["compound"], "comparator": "none" # C* 2.2 }], 'column_types': { "compression": "map<ascii, ascii>", "compaction": "map<ascii, ascii>", "bloom_filter_fp_chance": "double", "caching": "map<ascii, ascii>", "comment": "ascii", "gc_grace_seconds": "int", "keyspace_name": "ascii", "table_name": "ascii", "columnfamily_name": "ascii", "flags": "set<ascii>", "comparator": "ascii" }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) queries = [ "SELECT * FROM system_schema.keyspaces", "SELECT * FROM system.schema_keyspaces" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [{ "strategy_class": "SimpleStrategy", # C* 2.2 "strategy_options": '{}', # C* 2.2 "replication": { 'strategy': 'SimpleStrategy', 'replication_factor': 1 }, "durable_writes": True, "keyspace_name": "testks" }], 'column_types': { "strategy_class": "ascii", "strategy_options": "ascii", "replication": "map<ascii, ascii>", "keyspace_name": "ascii", "durable_writes": "boolean" }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) queries = [ "SELECT * FROM system_schema.columns", "SELECT * FROM system.schema.columns", "SELECT * FROM system.schema_columns" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [ { "table_name": 'testtable', "columnfamily_name": 'testtable', # C* 2.2 "column_name": "", "keyspace_name": "testks", "kind": "partition_key", "clustering_order": "none", "position": 0, "type": "text", "column_name_bytes": 0x12, "validator": "none" # C* 2.2 }, { "table_name": 'testtable', "columnfamily_name": 'testtable', # C* 2.2 "column_name": " ", "keyspace_name": "testks", "kind": "regular", "clustering_order": "none", "position": -1, "type": "text", "column_name_bytes": 0x13, "validator": "none" # C* 2.2 } ], 'column_types': { "table_name": "ascii", "columnfamily_name": "ascii", "column_name": "ascii", "keyspace_name": "ascii", "clustering_order": "ascii", "column_name_bytes": "blob", "kind": "ascii", "position": "int", "type": "ascii", "validator": "ascii" # C* 2.2 }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) self.cluster = Cluster(protocol_version=PROTOCOL_VERSION, compression=False) self.session = self.cluster.connect(wait_for_all_pools=True) table_metadata = self.cluster.metadata.keyspaces['testks'].tables[ 'testtable'] self.assertEqual(len(table_metadata.columns), 2) self.assertIn('', table_metadata.columns) self.assertIn(' ', table_metadata.columns)
def test_empty_columns_in_system_schema(self): queries = [ "SELECT * FROM system_schema.tables", "SELECT * FROM system.schema.tables", "SELECT * FROM system.schema_columnfamilies" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [ { "compression": dict(), "compaction": dict(), "bloom_filter_fp_chance": 0.1, "caching": {"keys": "ALL", "rows_per_partition": "NONE"}, "comment": "comment", "gc_grace_seconds": 60000, "dclocal_read_repair_chance": 0.1, "read_repair_chance": 0.1, "keyspace_name": "testks", "table_name": "testtable", "columnfamily_name": "testtable", # C* 2.2 "flags": ["compound"], "comparator": "none" # C* 2.2 } ], 'column_types': { "compression": "map<ascii, ascii>", "compaction": "map<ascii, ascii>", "bloom_filter_fp_chance": "double", "caching": "map<ascii, ascii>", "comment": "ascii", "gc_grace_seconds": "int", "dclocal_read_repair_chance": "double", "read_repair_chance": "double", "keyspace_name": "ascii", "table_name": "ascii", "columnfamily_name": "ascii", "flags": "set<ascii>", "comparator": "ascii" }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) queries = [ "SELECT * FROM system_schema.keyspaces", "SELECT * FROM system.schema_keyspaces" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [ { "strategy_class": "SimpleStrategy", # C* 2.2 "strategy_options": '{}', # C* 2.2 "replication": {'strategy': 'SimpleStrategy', 'replication_factor': 1}, "durable_writes": True, "keyspace_name": "testks" } ], 'column_types': { "strategy_class": "ascii", "strategy_options": "ascii", "replication": "map<ascii, ascii>", "keyspace_name": "ascii", "durable_writes": "boolean" }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) queries = [ "SELECT * FROM system_schema.columns", "SELECT * FROM system.schema.columns", "SELECT * FROM system.schema_columns" ] then = { 'result': 'success', 'delay_in_ms': 0, 'rows': [ { "table_name": 'testtable', "columnfamily_name": 'testtable', # C* 2.2 "column_name": "", "keyspace_name": "testks", "kind": "partition_key", "clustering_order": "none", "position": 0, "type": "text", "column_name_bytes": 0x12, "validator": "none" # C* 2.2 }, { "table_name": 'testtable', "columnfamily_name": 'testtable', # C* 2.2 "column_name": " ", "keyspace_name": "testks", "kind": "regular", "clustering_order": "none", "position": -1, "type": "text", "column_name_bytes": 0x13, "validator": "none" # C* 2.2 } ], 'column_types': { "table_name": "ascii", "columnfamily_name": "ascii", "column_name": "ascii", "keyspace_name": "ascii", "clustering_order": "ascii", "column_name_bytes": "blob", "kind": "ascii", "position": "int", "type": "ascii", "validator": "ascii" # C* 2.2 }, 'ignore_on_prepare': False } for query in queries: query = PrimeQuery(query, then=then) prime_request(query) self.cluster = Cluster(protocol_version=PROTOCOL_VERSION) self.session = self.cluster.connect(wait_for_all_pools=True) table_metadata = self.cluster.metadata.keyspaces['testks'].tables['testtable'] self.assertEqual(len(table_metadata.columns), 2) self.assertIn('', table_metadata.columns) self.assertIn(' ', table_metadata.columns)