11import time
22import unittest
3- import pytest
4- import os
3+
54from cassandra .cluster import Cluster
65from cassandra .policies import ConstantReconnectionPolicy , RoundRobinPolicy , TokenAwarePolicy
76
87from tests .integration import PROTOCOL_VERSION , use_cluster
98from tests .unit .test_host_connection_pool import LOGGER
109
10+ CCM_CLUSTER = None
11+
1112def setup_module ():
12- use_cluster ('tablets' , [3 ], start = True )
13+ global CCM_CLUSTER
14+
15+ CCM_CLUSTER = use_cluster ('tablets' , [3 ], start = True )
1316
1417class TestTabletsIntegration (unittest .TestCase ):
1518 @classmethod
@@ -18,7 +21,7 @@ def setup_class(cls):
1821 load_balancing_policy = TokenAwarePolicy (RoundRobinPolicy ()),
1922 reconnection_policy = ConstantReconnectionPolicy (1 ))
2023 cls .session = cls .cluster .connect ()
21- cls .create_ks_and_cf (cls )
24+ cls .create_ks_and_cf (cls . session )
2225 cls .create_data (cls .session )
2326
2427 @classmethod
@@ -47,6 +50,10 @@ def verify_same_host_in_tracing(self, results):
4750 self .assertEqual (len (host_set ), 1 )
4851 self .assertIn ('locally' , "\n " .join ([event .activity for event in events ]))
4952
53+ def get_tablet_record (self , query ):
54+ metadata = self .session .cluster .metadata
55+ return metadata ._tablets .get_tablet_for_key (query .keyspace , query .table , metadata .token_map .token_class .from_key (query .routing_key ))
56+
5057 def verify_same_shard_in_tracing (self , results ):
5158 traces = results .get_query_trace ()
5259 events = traces .events
@@ -69,24 +76,25 @@ def verify_same_shard_in_tracing(self, results):
6976 self .assertEqual (len (shard_set ), 1 )
7077 self .assertIn ('locally' , "\n " .join ([event .activity for event in events ]))
7178
72- def create_ks_and_cf (self ):
73- self .session .execute (
79+ @classmethod
80+ def create_ks_and_cf (cls , session ):
81+ session .execute (
7482 """
7583 DROP KEYSPACE IF EXISTS test1
7684 """
7785 )
78- self . session .execute (
86+ session .execute (
7987 """
8088 CREATE KEYSPACE test1
8189 WITH replication = {
8290 'class': 'NetworkTopologyStrategy',
83- 'replication_factor': 1
91+ 'replication_factor': 2
8492 } AND tablets = {
8593 'initial': 8
8694 }
8795 """ )
8896
89- self . session .execute (
97+ session .execute (
9098 """
9199 CREATE TABLE test1.table1 (pk int, ck int, v int, PRIMARY KEY (pk, ck));
92100 """ )
@@ -109,6 +117,8 @@ def query_data_shard_select(self, session, verify_in_tracing=True):
109117 """ )
110118
111119 bound = prepared .bind ([(2 )])
120+ assert self .get_tablet_record (bound ) is not None
121+
112122 results = session .execute (bound , trace = True )
113123 self .assertEqual (results , [(2 , 2 , 0 )])
114124 if verify_in_tracing :
@@ -121,6 +131,8 @@ def query_data_host_select(self, session, verify_in_tracing=True):
121131 """ )
122132
123133 bound = prepared .bind ([(2 )])
134+ assert self .get_tablet_record (bound ) is not None
135+
124136 results = session .execute (bound , trace = True )
125137 self .assertEqual (results , [(2 , 2 , 0 )])
126138 if verify_in_tracing :
@@ -133,6 +145,8 @@ def query_data_shard_insert(self, session, verify_in_tracing=True):
133145 """ )
134146
135147 bound = prepared .bind ([(51 ), (1 ), (2 )])
148+ assert self .get_tablet_record (bound ) is not None
149+
136150 results = session .execute (bound , trace = True )
137151 if verify_in_tracing :
138152 self .verify_same_shard_in_tracing (results )
@@ -144,6 +158,8 @@ def query_data_host_insert(self, session, verify_in_tracing=True):
144158 """ )
145159
146160 bound = prepared .bind ([(52 ), (1 ), (2 )])
161+ assert self .get_tablet_record (bound ) is not None
162+
147163 results = session .execute (bound , trace = True )
148164 if verify_in_tracing :
149165 self .verify_same_host_in_tracing (results )
@@ -155,3 +171,61 @@ def test_tablets(self):
155171 def test_tablets_shard_awareness (self ):
156172 self .query_data_shard_select (self .session )
157173 self .query_data_shard_insert (self .session )
174+
175+ def test_tablets_invalidation (self ):
176+ global CCM_CLUSTER
177+
178+ def recreate_and_wait (_ ):
179+ # Drop and recreate ks and table to trigger tablets invalidation
180+ self .create_ks_and_cf (self .cluster .connect ())
181+ time .sleep (3 )
182+
183+ def recreate_while_reconnecting (_ ):
184+ # Kill control connection
185+ conn = self .session .cluster .control_connection ._connection
186+ self .session .cluster .control_connection ._connection = None
187+ conn .close ()
188+
189+ # Drop and recreate ks and table to trigger tablets invalidation
190+ self .create_ks_and_cf (self .cluster .connect ())
191+
192+ # Start control connection
193+ self .session .cluster .control_connection ._reconnect ()
194+
195+ def decommission_non_cc_node (rec ):
196+ # Drop and recreate ks and table to trigger tablets invalidation
197+ for node in CCM_CLUSTER .nodes .values ():
198+ if self .cluster .control_connection ._connection .endpoint .address == node .network_interfaces ["storage" ][0 ]:
199+ # Ignore node that control connection is connected to
200+ continue
201+ for replica in rec .replicas :
202+ if str (replica [0 ]) == str (node .node_hostid ):
203+ node .decommission ()
204+ break
205+ else :
206+ continue
207+ break
208+ else :
209+ assert False , "failed to find node to decommission"
210+ time .sleep (10 )
211+
212+ self .run_tablets_invalidation_test (recreate_and_wait )
213+ self .run_tablets_invalidation_test (recreate_while_reconnecting )
214+ self .run_tablets_invalidation_test (decommission_non_cc_node )
215+
216+
217+ def run_tablets_invalidation_test (self , invalidate ):
218+ # Make sure driver holds tablet info
219+ bound = self .session .prepare (
220+ """
221+ SELECT pk, ck, v FROM test1.table1 WHERE pk = ?
222+ """ ).bind ([(2 )])
223+ self .session .execute (bound )
224+
225+ rec = self .get_tablet_record (bound )
226+ assert rec is not None
227+
228+ invalidate (rec )
229+
230+ # Check if tablets information was purged
231+ assert self .get_tablet_record (bound ) is None
0 commit comments