package com.netflix.eureka.resources; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.MyDataCenterInstanceConfig; import com.netflix.discovery.DefaultEurekaClientConfig; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.util.InstanceInfoGenerator; import com.netflix.eureka.DefaultEurekaServerConfig; import com.netflix.eureka.EurekaServerContext; import com.netflix.eureka.cluster.PeerEurekaNode; import com.netflix.eureka.cluster.PeerEurekaNodes; import com.netflix.eureka.registry.PeerAwareInstanceRegistry; import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import java.util.Collections; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; /** * A pseudo mock test to test concurrent scenarios to do with registration and replication of cluster setups * with 1+ eureka servers * * @author David Liu */ public class ReplicationConcurrencyTest { private String id; private String appName; private InstanceInfo instance1; private InstanceInfo instance2; private MockServer server1; private MockServer server2; private InstanceInfo server1Sees; private InstanceInfo server2Sees; @Before public void setUp() throws Exception { InstanceInfo seed = InstanceInfoGenerator.takeOne(); id = seed.getId(); appName = seed.getAppName(); // set up test instances instance1 = InstanceInfo.Builder.newBuilder() .setInstanceId(id) .setAppName(appName) .setHostName(seed.getHostName()) .setIPAddr(seed.getIPAddr()) .setDataCenterInfo(seed.getDataCenterInfo()) .setStatus(InstanceInfo.InstanceStatus.STARTING) .setLastDirtyTimestamp(11111l) .build(); instance2 = new InstanceInfo.Builder(seed) .setInstanceId(id) .setAppName(appName) .setHostName(seed.getHostName()) .setIPAddr(seed.getIPAddr()) .setDataCenterInfo(seed.getDataCenterInfo()) .setStatus(InstanceInfo.InstanceStatus.UP) .setLastDirtyTimestamp(22222l) .build(); assertThat(instance1.getStatus(), not(equalTo(instance2.getStatus()))); // set up server1 with no replication anywhere PeerEurekaNodes server1Peers = Mockito.mock(PeerEurekaNodes.class); Mockito.when(server1Peers.getPeerEurekaNodes()).thenReturn(Collections.<PeerEurekaNode>emptyList()); server1 = new MockServer(appName, server1Peers); // set up server2 PeerEurekaNodes server2Peers = Mockito.mock(PeerEurekaNodes.class); Mockito.when(server2Peers.getPeerEurekaNodes()).thenReturn(Collections.<PeerEurekaNode>emptyList()); server2 = new MockServer(appName, server2Peers); // register with server1 server1.applicationResource.addInstance(instance1, "false"/* isReplication */); // STARTING server1Sees = server1.registry.getInstanceByAppAndId(appName, id); assertThat(server1Sees, equalTo(instance1)); // update (via a register) with server2 server2.applicationResource.addInstance(instance2, "false"/* isReplication */); // UP server2Sees = server2.registry.getInstanceByAppAndId(appName, id); assertThat(server2Sees, equalTo(instance2)); // make sure data in server 1 is "older" assertThat(server2Sees.getLastDirtyTimestamp() > server1Sees.getLastDirtyTimestamp(), is(true)); } /** * this test tests a scenario where multiple registration and update requests for a single client is sent to * different eureka servers before replication can occur between them */ @Test public void testReplicationWithRegistrationAndUpdateOnDifferentServers() throws Exception { // now simulate server1 (delayed) replication to server2. // without batching this is done by server1 making a REST call to the register endpoint of server2 with // replication=true server2.applicationResource.addInstance(instance1, "true"); // verify that server2's "newer" info is (or is not) overridden // server2 should still see instance2 even though server1 tried to replicate across server1 InstanceInfo newServer2Sees = server2.registry.getInstanceByAppAndId(appName, id); assertThat(newServer2Sees.getStatus(), equalTo(instance2.getStatus())); // now let server2 replicate to server1 server1.applicationResource.addInstance(newServer2Sees, "true"); // verify that server1 now have the updated info from server2 InstanceInfo newServer1Sees = server1.registry.getInstanceByAppAndId(appName, id); assertThat(newServer1Sees.getStatus(), equalTo(instance2.getStatus())); } private static class MockServer { public final ApplicationResource applicationResource; public final PeerReplicationResource replicationResource; public final PeerAwareInstanceRegistry registry; public MockServer(String appName, PeerEurekaNodes peerEurekaNodes) throws Exception { ApplicationInfoManager infoManager = new ApplicationInfoManager(new MyDataCenterInstanceConfig()); DefaultEurekaServerConfig serverConfig = Mockito.spy(new DefaultEurekaServerConfig()); DefaultEurekaClientConfig clientConfig = new DefaultEurekaClientConfig(); ServerCodecs serverCodecs = new DefaultServerCodecs(serverConfig); EurekaClient eurekaClient = Mockito.mock(EurekaClient.class); Mockito.doReturn("true").when(serverConfig).getExperimental("registry.registration.ignoreIfDirtyTimestampIsOlder"); this.registry = new PeerAwareInstanceRegistryImpl(serverConfig, clientConfig, serverCodecs, eurekaClient); this.registry.init(peerEurekaNodes); this.applicationResource = new ApplicationResource(appName, serverConfig, registry); EurekaServerContext serverContext = Mockito.mock(EurekaServerContext.class); Mockito.when(serverContext.getServerConfig()).thenReturn(serverConfig); Mockito.when(serverContext.getRegistry()).thenReturn(registry); this.replicationResource = new PeerReplicationResource(serverContext); } } }