3434import net .bytebuddy .dynamic .loading .ClassLoadingStrategy ;
3535import net .bytebuddy .implementation .MethodDelegation ;
3636import net .bytebuddy .implementation .bind .annotation .SuperCall ;
37+ import org .apache .cassandra .config .DatabaseDescriptor ;
3738import org .apache .cassandra .cql3 .QueryProcessor ;
3839import org .apache .cassandra .distributed .Cluster ;
40+ import org .apache .cassandra .distributed .Constants ;
3941import org .apache .cassandra .distributed .api .ConsistencyLevel ;
42+ import org .apache .cassandra .distributed .api .IInstanceConfig ;
43+ import org .apache .cassandra .distributed .api .IInvokableInstance ;
4044import org .apache .cassandra .distributed .api .NodeToolResult ;
45+ import org .apache .cassandra .distributed .api .TokenSupplier ;
4146import org .apache .cassandra .distributed .shared .ClusterUtils ;
4247import org .apache .cassandra .distributed .test .TestBaseImpl ;
4348import org .apache .cassandra .gms .Gossiper ;
49+ import org .apache .cassandra .io .util .File ;
4450import org .apache .cassandra .service .StorageService ;
4551import org .apache .cassandra .streaming .StreamSession ;
4652import org .apache .cassandra .tcm .ClusterMetadata ;
5561import static net .bytebuddy .matcher .ElementMatchers .named ;
5662import static org .apache .cassandra .distributed .api .Feature .GOSSIP ;
5763import static org .apache .cassandra .distributed .api .Feature .NETWORK ;
64+ import static org .apache .cassandra .distributed .shared .NetworkTopology .dcAndRack ;
65+ import static org .apache .cassandra .distributed .shared .NetworkTopology .networkTopology ;
5866import static org .apache .cassandra .distributed .test .ring .BootstrapTest .populate ;
5967import static org .junit .Assert .assertEquals ;
6068import static org .junit .Assert .assertTrue ;
@@ -75,6 +83,47 @@ public void testResumableDecom() throws IOException
7583 }
7684 }
7785
86+ @ Test
87+ public void testAddressReuseAfterDecommission () throws IOException , ExecutionException , InterruptedException
88+ {
89+ // Initially, all nodes should be in dc1/rack1. Node 3 will be decommissioned and a new node added re-using
90+ // node 3's address. When the new node registers, it should be in dc2/rack2.
91+ // For now, this requires the accord service to disabled. See CASSANDRA-21026
92+ try (Cluster cluster = builder ().withNodes (3 )
93+ .withTokenSupplier (TokenSupplier .evenlyDistributedTokens (4 ))
94+ .withConfig (config -> config .with (NETWORK , GOSSIP )
95+ .set ("accord.enabled" , false ))
96+ .withNodeIdTopology (networkTopology (3 , (id ) -> dcAndRack ("dc1" , "rack1" )))
97+ .start ())
98+ {
99+ assertEquals ("dc1/rack1" , cluster .get (1 ).callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
100+ assertEquals ("dc1/rack1" , cluster .get (2 ).callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
101+ assertEquals ("dc1/rack1" , cluster .get (3 ).callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
102+
103+ IInvokableInstance toRemove = cluster .get (3 );
104+ toRemove .nodetoolResult ("decommission" , "--force" ).asserts ().success ();
105+ toRemove .shutdown ().get ();
106+ ClusterUtils .getDirectories (toRemove ).forEach (File ::tryDeleteRecursive );
107+ cluster .unsafeRemoveNode (toRemove );
108+
109+ // Now add a new node, using the same address as the one we just removed. This new node should register
110+ // itself in dc2/rack2 and not inherit the location of its predecessor.
111+ // Note: because we have removed the original node3 from the cluster completely, which is necessary because
112+ // the cluster will complain about an id clash otherwise, this new node will also be "node3". However, it is
113+ // completely distinct from the original one.
114+ cluster .unsafeUpdateNodeIdTopology (toRemove .config ().num (), dcAndRack ("dc2" , "rack2" ));
115+ IInstanceConfig config = cluster .newInstanceConfig ()
116+ .set ("auto_bootstrap" , true )
117+ .set (Constants .KEY_DTEST_FULL_STARTUP , true );
118+ IInvokableInstance newInstance = cluster .bootstrap (config );
119+ newInstance .startup ();
120+
121+ assertEquals ("dc1/rack1" , cluster .get (1 ).callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
122+ assertEquals ("dc1/rack1" , cluster .get (2 ).callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
123+ assertEquals ("dc2/rack2" , newInstance .callOnInstance (() -> DatabaseDescriptor .getLocator ().local ().toString ()));
124+ }
125+ }
126+
78127 public static class BB
79128 {
80129 static void install (ClassLoader cl , int nodeNumber )
0 commit comments