/**
* Copyright 2011 LiveRamp
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.liveramp.hank.ring_group_conductor;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import com.liveramp.hank.coordinator.Domain;
import com.liveramp.hank.coordinator.DomainAndVersion;
import com.liveramp.hank.coordinator.DomainGroup;
import com.liveramp.hank.coordinator.Host;
import com.liveramp.hank.coordinator.HostCommand;
import com.liveramp.hank.coordinator.HostDomain;
import com.liveramp.hank.coordinator.HostDomainPartition;
import com.liveramp.hank.coordinator.HostState;
import com.liveramp.hank.coordinator.PartitionServerAddress;
import com.liveramp.hank.coordinator.Ring;
import com.liveramp.hank.coordinator.mock.MockDomain;
import com.liveramp.hank.coordinator.mock.MockDomainGroup;
import com.liveramp.hank.partition_assigner.ModPartitionAssigner;
import com.liveramp.hank.partition_assigner.PartitionAssigner;
import com.liveramp.hank.test.BaseTestCase;
import com.liveramp.hank.test.coordinator.MockHost;
import com.liveramp.hank.test.coordinator.MockRing;
import com.liveramp.hank.test.coordinator.MockRingGroup;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TestRingGroupUpdateTransitionFunctionImpl extends BaseTestCase {
private class MockRingLocal extends MockRing {
public MockRingLocal(int number,
Set<Host> hosts) {
super(hosts, null, number);
}
}
private class MockRingGroupLocal extends MockRingGroup {
public MockRingGroupLocal(Ring... rings) {
super(domainGroup, "myRingGroup", new LinkedHashSet<Ring>(Arrays.asList(rings)));
}
}
private class MockHostLocal extends MockHost {
public MockHostLocal(PartitionServerAddress address) {
super(address);
}
public void setCurrentVersion(Set<DomainAndVersion> currentVersions) throws IOException {
for (HostDomain hostDomain : getAssignedDomains()) {
Integer version = null;
for (DomainAndVersion domainVersion : currentVersions) {
if (domainVersion.getDomain().equals(hostDomain.getDomain())) {
version = domainVersion.getVersionNumber();
break;
}
}
if (version != null) {
for (HostDomainPartition partition : hostDomain.getPartitions()) {
partition.setCurrentDomainVersion(version);
}
}
}
}
}
private static Map<Domain, Integer> versionsMap1 = new HashMap<Domain, Integer>();
private static Map<Domain, Integer> versionsMap2 = new HashMap<Domain, Integer>();
private static Map<Domain, Integer> versionsMap3 = new HashMap<Domain, Integer>();
private static Set<DomainAndVersion> v1 = new HashSet<DomainAndVersion>();
private static Set<DomainAndVersion> v2 = new HashSet<DomainAndVersion>();
private static Set<DomainAndVersion> v3 = new HashSet<DomainAndVersion>();
private static Domain domain1 = new MockDomain("domain1", 1, 6, null, null, null, null);
private static Domain domain2 = new MockDomain("domain2", 2, 6, null, null, null, null);
private static DomainGroup domainGroup = new MockDomainGroup("myDomainGroup");
private MockRingLocal r0;
private MockRingLocal r1;
private MockRingLocal r2;
private MockHostLocal r0h0 = null;
private MockHostLocal r0h1 = null;
private MockHostLocal r1h0 = null;
private MockHostLocal r1h1 = null;
private MockHostLocal r2h0 = null;
private MockHostLocal r2h1 = null;
private MockRingGroupLocal rg = null;
private PartitionAssigner partitionAssigner;
private RingGroupUpdateTransitionFunctionImpl testTransitionFunction = null;
@Before
public void setUp() throws Exception {
r0h0 = new MockHostLocal(new PartitionServerAddress("localhost", 1));
r0h1 = new MockHostLocal(new PartitionServerAddress("localhost", 2));
r1h0 = new MockHostLocal(new PartitionServerAddress("localhost", 3));
r1h1 = new MockHostLocal(new PartitionServerAddress("localhost", 4));
r2h0 = new MockHostLocal(new PartitionServerAddress("localhost", 5));
r2h1 = new MockHostLocal(new PartitionServerAddress("localhost", 6));
Set<Host> r0Hosts = new HashSet<Host>();
r0Hosts.add(r0h0);
r0Hosts.add(r0h1);
Set<Host> r1Hosts = new HashSet<Host>();
r1Hosts.add(r1h0);
r1Hosts.add(r1h1);
Set<Host> r2Hosts = new HashSet<Host>();
r2Hosts.add(r2h0);
r2Hosts.add(r2h1);
r0 = new MockRingLocal(0, r0Hosts);
r1 = new MockRingLocal(1, r1Hosts);
r2 = new MockRingLocal(2, r2Hosts);
rg = new MockRingGroupLocal(r0, r1, r2);
partitionAssigner = new ModPartitionAssigner();
testTransitionFunction = new RingGroupUpdateTransitionFunctionImpl(partitionAssigner, 0, 2, 0, null);
// V1
versionsMap1.put(domain1, 1);
v1.add(new DomainAndVersion(domain1, 1));
// V2
versionsMap2.put(domain1, 2);
v2.add(new DomainAndVersion(domain1, 2));
// V3
versionsMap3.put(domain1, 2);
versionsMap3.put(domain2, 1);
v3.add(new DomainAndVersion(domain1, 2));
v3.add(new DomainAndVersion(domain2, 1));
}
private void setUpRing(MockRingLocal ring,
Set<DomainAndVersion> currentVersions,
Set<DomainAndVersion> assignedVersions,
HostState hostState) throws IOException {
if (assignedVersions != null) {
partitionAssigner.prepare(ring, assignedVersions, rg.getRingGroupConductorMode());
}
for (Host host : ring.getHosts()) {
if (assignedVersions != null) {
partitionAssigner.assign(host);
}
host.setState(hostState);
if (currentVersions != null) {
((MockHostLocal)host).setCurrentVersion(currentVersions);
}
}
}
private boolean isAssigned(Ring ring, Host host, Set<DomainAndVersion> domainVersions) throws IOException {
partitionAssigner.prepare(ring, domainVersions, rg.getRingGroupConductorMode());
return partitionAssigner.isAssigned(host);
}
@Test
public void testIsFullyServing() throws IOException {
RingGroupUpdateTransitionFunctionImpl transitionFunction = new RingGroupUpdateTransitionFunctionImpl(null, 1, 2, 0, null);
setUpRing(r0, v1, v1, HostState.IDLE);
assertFalse(transitionFunction.isFullyServing(r0h0, true));
assertFalse(transitionFunction.isFullyServing(r0h1, true));
r0h0.setState(HostState.SERVING);
assertFalse(transitionFunction.isFullyServing(r0h0, true));
assertFalse(transitionFunction.isFullyServing(r0h1, true));
r0h1.setState(HostState.SERVING);
assertTrue(transitionFunction.isFullyServing(r0h0, true));
assertFalse(transitionFunction.isFullyServing(r0h1, true));
assertTrue(transitionFunction.isFullyServing(r0h1, true));
r0h0.enqueueCommand(HostCommand.GO_TO_IDLE);
assertFalse(transitionFunction.isFullyServing(r0h0, true));
r0h0.setCurrentCommand(HostCommand.GO_TO_IDLE);
r0h0.clearCommandQueue();
assertFalse(transitionFunction.isFullyServing(r0h0, true));
}
@Test
public void testNothingToDo() throws IOException {
domainGroup.setDomainVersions(versionsMap1);
setUpRing(r0, v1, v1, HostState.SERVING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// No commands should have been issued
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testKickstartAllRings() throws IOException {
domainGroup.setDomainVersions(versionsMap1);
setUpRing(r0, null, null, HostState.IDLE);
setUpRing(r1, null, null, HostState.IDLE);
setUpRing(r2, null, null, HostState.IDLE);
// Nothing should be assigned
assertFalse(isAssigned(r0, r0h0, v1));
assertFalse(isAssigned(r0, r0h1, v1));
assertFalse(isAssigned(r1, r1h0, v1));
assertFalse(isAssigned(r1, r1h1, v1));
assertFalse(isAssigned(r2, r2h0, v1));
assertFalse(isAssigned(r2, r2h1, v1));
testTransitionFunction.manageTransitions(rg);
// All rings should have been assigned
assertTrue(isAssigned(r0, r0h0, v1));
assertTrue(isAssigned(r0, r0h1, v1));
assertTrue(isAssigned(r1, r1h0, v1));
assertTrue(isAssigned(r1, r1h1, v1));
assertTrue(isAssigned(r2, r2h0, v1));
assertTrue(isAssigned(r2, r2h1, v1));
// No commands should have been issued
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
testTransitionFunction.manageTransitions(rg);
// All hosts should have received execute update
assertEquals(HostCommand.EXECUTE_UPDATE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r0h1.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r1h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r1h1.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r2h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testTakesDownFirstRingForAssignmentWhenStartingUpdate() throws IOException {
domainGroup.setDomainVersions(versionsMap3);
setUpRing(r0, v2, v2, HostState.SERVING);
setUpRing(r1, v2, v2, HostState.SERVING);
setUpRing(r2, v2, v2, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// All serving hosts in r0 should have received go to idle
assertEquals(HostCommand.GO_TO_IDLE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to other rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
// Nothing should be assigned
assertFalse(isAssigned(r0, r0h0, v3));
assertFalse(isAssigned(r0, r0h1, v3));
assertFalse(isAssigned(r1, r1h0, v3));
assertFalse(isAssigned(r1, r1h1, v3));
assertFalse(isAssigned(r2, r2h0, v3));
assertFalse(isAssigned(r2, r2h1, v3));
// Make hosts idle
r0h0.nextCommand();
r0h1.nextCommand();
r0h0.setState(HostState.IDLE);
r0h1.setState(HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
// Ring one should have been assigned
assertTrue(isAssigned(r0, r0h0, v3));
assertTrue(isAssigned(r0, r0h1, v3));
assertFalse(isAssigned(r1, r1h0, v3));
assertFalse(isAssigned(r1, r1h1, v3));
assertFalse(isAssigned(r2, r2h0, v3));
assertFalse(isAssigned(r2, r2h1, v3));
}
@Test
public void testTakesDownFirstRingForUpdateWhenStartingUpdate() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v1, v2, HostState.SERVING);
setUpRing(r1, v1, v2, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// All serving hosts in r0 should have received go to idle
assertEquals(HostCommand.GO_TO_IDLE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to other rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
// Make hosts idle
r0h0.nextCommand();
r0h1.nextCommand();
r0h0.setState(HostState.IDLE);
r0h1.setState(HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
// All idle hosts in r0 should have received execute update
assertEquals(HostCommand.EXECUTE_UPDATE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to other rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testAssignWhenOneHostIsServing() throws IOException {
domainGroup.setDomainVersions(versionsMap3);
setUpRing(r0, v1, v1, HostState.IDLE);
r0h1.setState(HostState.SERVING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// v2 should have been assigned to r0h0 but not r0h1
assertTrue(isAssigned(r0, r0h0, v3));
assertFalse(isAssigned(r0, r0h1, v3));
// No commands should have been issued to rings, except to r0h1
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
// Make host idle
r0h1.nextCommand();
r0h1.setState(HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
// v2 should have been assigned to r0h1
assertTrue(isAssigned(r0, r0h0, v3));
assertTrue(isAssigned(r0, r0h1, v3));
// r0h0 should be updating
assertEquals(HostCommand.EXECUTE_UPDATE, r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testAssignWhenOneHostIsUpdating() throws IOException {
domainGroup.setDomainVersions(versionsMap3);
setUpRing(r0, v1, v1, HostState.IDLE);
r0h1.setState(HostState.UPDATING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// v2 should have been assigned to r0h0 but not r0h1
assertTrue(isAssigned(r0, r0h0, v3));
assertFalse(isAssigned(r0, r0h1, v3));
// No commands should have been issued to rings
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testAssignIdleRing() throws IOException {
domainGroup.setDomainVersions(versionsMap3);
setUpRing(r0, v1, v1, HostState.IDLE);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
assertTrue(isAssigned(r0, r0h0, v3));
assertTrue(isAssigned(r0, r0h1, v3));
assertFalse(isAssigned(r1, r1h0, v3));
assertFalse(isAssigned(r1, r1h1, v3));
assertFalse(isAssigned(r2, r2h0, v3));
assertFalse(isAssigned(r2, r2h1, v3));
// No commands should have been issued to rings
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testTakeDownModifiedRing() throws IOException {
domainGroup.setDomainVersions(versionsMap1);
setUpRing(r0, v1, null, HostState.SERVING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// Hosts of r0 should have received go to idle
assertEquals(HostCommand.GO_TO_IDLE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to other rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testAssignModifiedRing() throws IOException {
domainGroup.setDomainVersions(versionsMap1);
setUpRing(r0, v1, null, HostState.IDLE);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// v1 should have been assigned to r0
assertTrue(isAssigned(r0, r0h0, v1));
assertTrue(isAssigned(r0, r0h1, v1));
// No commands should have been issued to rings
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testReassignProactivelyWhenHostsAreOffline() throws IOException {
domainGroup.setDomainVersions(versionsMap1);
setUpRing(r0, v1, v1, HostState.SERVING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
assertTrue(isAssigned(r0, r0h0, v1));
assertTrue(isAssigned(r0, r0h1, v1));
r0h0.setState(HostState.OFFLINE);
assertTrue(isAssigned(r0, r0h0, v1));
assertTrue(isAssigned(r0, r0h1, v1));
// Switch to proactive
rg.setRingGroupConductorMode(RingGroupConductorMode.PROACTIVE);
assertFalse(isAssigned(r0, r0h0, v1));
assertFalse(isAssigned(r0, r0h1, v1));
testTransitionFunction.manageTransitions(rg);
// r0h1 should be going idle for assignment
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
r0h1.setState(HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
assertFalse(isAssigned(r0, r0h0, v1));
assertTrue(isAssigned(r0, r0h1, v1));
testTransitionFunction.manageTransitions(rg);
// r0h1 should be executing update
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r0h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testExecuteUpdateWhenAssignedAndIdle() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v1, v2, HostState.IDLE);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// Hosts of r0 should have received execute update
assertEquals(HostCommand.EXECUTE_UPDATE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.EXECUTE_UPDATE, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testProactivelyServeDateWhenHostUpdated() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v1, v2, HostState.UPDATING);
setUpRing(r1, v1, v2, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
r0h0.setCurrentVersion(v2);
r0h0.setState(HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
// r0h0 should have received serve data
assertEquals(HostCommand.SERVE_DATA, r0h0.getAndClearLastEnqueuedCommand());
// No commands should have been issued to rings
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testServeDataWhenUpdated() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v2, v2, HostState.IDLE);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v1, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// Hosts of r0 should have received serve data
assertEquals(HostCommand.SERVE_DATA, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.SERVE_DATA, r0h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to rings
assertNull(r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testTakeDownSecondRingWhenFirstIsUpdated() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v2, v2, HostState.SERVING);
setUpRing(r1, v1, v2, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// Hosts of r1 should have received go to idle
assertEquals(HostCommand.GO_TO_IDLE, r1h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r1h1.getAndClearLastEnqueuedCommand());
// No commands should have been issued to rings
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testServeDataWhenNotEnoughRingsAreFullyServing() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
setUpRing(r0, v1, v2, HostState.SERVING);
setUpRing(r1, v1, v1, HostState.IDLE);
setUpRing(r2, v1, v2, HostState.IDLE);
testTransitionFunction.manageTransitions(rg);
// No commands should have been issued to r0
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
// Other hosts should have received serve data
assertEquals(HostCommand.SERVE_DATA, r1h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.SERVE_DATA, r1h1.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.SERVE_DATA, r2h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.SERVE_DATA, r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testUpdateMultipleRingsWhenEnoughReplicasAreServing() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
// r0: h0 SERVING, h1 UPDATING
// r1: h0 SERVING, h1 SERVING
// r2: h0 SERVING, h1 SERVING
setUpRing(r0, v1, v2, HostState.SERVING);
// Make r0h0 up to date
partitionAssigner.prepare(r0, v2, rg.getRingGroupConductorMode());
partitionAssigner.assign(r0h0);
r0h0.setCurrentVersion(v2);
// r0h1 is still updating
r0h1.setState(HostState.UPDATING);
setUpRing(r1, v1, v1, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
testTransitionFunction.manageTransitions(rg);
// No commands should have been issued to offline hosts and up to date hosts
assertNull(r0h0.getAndClearLastEnqueuedCommand());
assertNull(r0h1.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r1h0.getAndClearLastEnqueuedCommand());
assertNull(r1h1.getAndClearLastEnqueuedCommand());
assertNull(r2h0.getAndClearLastEnqueuedCommand());
assertNull(r2h1.getAndClearLastEnqueuedCommand());
}
// 2 - 2 : doesn't update both in either AZ
@Test
public void testSingleUpdateSameBucket() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
r0h0.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r0h1.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r1h0.setEnvironmentFlags(Collections.singletonMap("AZ", "b"));
r1h1.setEnvironmentFlags(Collections.singletonMap("AZ", "b"));
r2h0.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r2h1.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
// all rings need to be updated
setUpRing(r0, v1, v2, HostState.SERVING);
setUpRing(r1, v1, v2, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
// require at least 1 replica up per AZ.
testTransitionFunction = new RingGroupUpdateTransitionFunctionImpl(partitionAssigner, 0, 1, 1, "AZ");
testTransitionFunction.manageTransitions(rg);
// r0 can go idle
assertEquals(HostCommand.GO_TO_IDLE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
// r1 can't
assertEquals(null, r1h0.getAndClearLastEnqueuedCommand());
assertEquals(null, r1h1.getAndClearLastEnqueuedCommand());
// r2 can't
assertEquals(null, r2h0.getAndClearLastEnqueuedCommand());
assertEquals(null, r2h1.getAndClearLastEnqueuedCommand());
}
@Test
public void testUpdateWithNoMin() throws IOException {
domainGroup.setDomainVersions(versionsMap2);
r0h0.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r0h1.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r1h0.setEnvironmentFlags(Collections.singletonMap("AZ", "b"));
r1h1.setEnvironmentFlags(Collections.singletonMap("AZ", "b"));
r2h0.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
r2h1.setEnvironmentFlags(Collections.singletonMap("AZ", "a"));
// all rings need to be updated
setUpRing(r0, v1, v2, HostState.SERVING);
setUpRing(r1, v1, v2, HostState.SERVING);
setUpRing(r2, v1, v2, HostState.SERVING);
// no minimum number of replicas per zone
testTransitionFunction = new RingGroupUpdateTransitionFunctionImpl(partitionAssigner, 0, 1, 0, "AZ");
testTransitionFunction.manageTransitions(rg);
// r0 can go idle
assertEquals(HostCommand.GO_TO_IDLE, r0h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r0h1.getAndClearLastEnqueuedCommand());
// r1 can as well, since 0 min
assertEquals(HostCommand.GO_TO_IDLE, r1h0.getAndClearLastEnqueuedCommand());
assertEquals(HostCommand.GO_TO_IDLE, r1h1.getAndClearLastEnqueuedCommand());
// r2 can't, limited by global 1 min replica.
assertEquals(null, r2h0.getAndClearLastEnqueuedCommand());
assertEquals(null, r2h1.getAndClearLastEnqueuedCommand());
}
}