package voldemort.scheduled; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; import org.junit.After; import org.junit.Before; import org.junit.Test; import voldemort.ServerTestUtils; import voldemort.TestUtils; import voldemort.client.protocol.admin.AdminClient; import voldemort.client.protocol.admin.AdminClientConfig; import voldemort.cluster.Cluster; import voldemort.serialization.SlopSerializer; import voldemort.server.VoldemortServer; import voldemort.store.routed.NodeValue; import voldemort.store.slop.Slop; import voldemort.utils.ByteArray; import voldemort.versioning.VectorClock; import voldemort.versioning.Versioned; import com.google.common.collect.Lists; public class SlopPurgeTest { private Cluster cluster; private VoldemortServer[] servers; private AdminClient adminClient; private SlopSerializer slopSerializer; private VoldemortServer purgedServer; private static int PURGE_SERVER_ID = 0; @Before public void setUp() throws Exception { cluster = ServerTestUtils.getLocalZonedCluster(6, 3, new int[] { 0, 0, 1, 1, 2, 2 }, new int[][] { { 0 }, { 2 }, { 4 }, { 1 }, { 3 }, { 5 } }); servers = new VoldemortServer[cluster.getNodes().size()]; slopSerializer = new SlopSerializer(); Properties serverProperties = new Properties(); // Schedule the slop pusher far far far out in the future, so it won't // run during the test serverProperties.setProperty("slop.frequency.ms", "" + (Integer.MAX_VALUE)); // Also no auto purging so we are sure that the only thing deleting the // slops is the purge job serverProperties.setProperty("auto.purge.dead.slops", "false"); cluster = ServerTestUtils.startVoldemortCluster(servers, null, null, "test/common/voldemort/config/three-stores-with-zones.xml", serverProperties, cluster); for(VoldemortServer server: servers) { if(server.getIdentityNode().getId() == PURGE_SERVER_ID) { purgedServer = server; break; } } Properties adminProperties = new Properties(); adminProperties.setProperty("max_connections", "2"); adminClient = new AdminClient(servers[0].getMetadataStore().getCluster(), new AdminClientConfig(adminProperties)); } private int numSlopsInServer(List<Versioned<Slop>> slops) { int count = 0; for(Versioned<Slop> slop: slops) { List<Versioned<byte[]>> slopEntry = adminClient.storeOps.getNodeKey("slop", PURGE_SERVER_ID, slop.getValue() .makeKey()); if(slopEntry.size() > 0) { count++; } } return count; } private void populateSlops(List<Versioned<Slop>> slops) { for(Versioned<Slop> slop: slops) { VectorClock clock = TestUtils.getClock(1); NodeValue<ByteArray, byte[]> nodeValue = new NodeValue<ByteArray, byte[]>(PURGE_SERVER_ID, slop.getValue() .makeKey(), new Versioned<byte[]>(slopSerializer.toBytes(slop.getValue()), clock)); adminClient.storeOps.putNodeKeyValue("slop", nodeValue); } } @Test public void testPurgeByNodeIds() { // these will perish List<Versioned<Slop>> node2Slops = ServerTestUtils.createRandomSlops(2, 10, false, "astore"); List<Versioned<Slop>> node4Slops = ServerTestUtils.createRandomSlops(4, 10, false, "cstore"); // these will be around List<Versioned<Slop>> node1Slops = ServerTestUtils.createRandomSlops(1, 10, false, "bstore"); List<Versioned<Slop>> node3Slops = ServerTestUtils.createRandomSlops(3, 10, false, "bstore"); List<Versioned<Slop>> slops = new ArrayList<Versioned<Slop>>(); slops.addAll(node2Slops); slops.addAll(node3Slops); slops.addAll(node1Slops); slops.addAll(node4Slops); Collections.shuffle(slops); // Populate the slops. populateSlops(slops); // Kick off the slop purge job adminClient.storeMntOps.slopPurgeJob(PURGE_SERVER_ID, Lists.newArrayList(2, 4), -1, null); ServerTestUtils.waitForAsyncOperationOnServer(purgedServer, "Purge", 5000); assertEquals("All slops from node 2 should be gone", 0, numSlopsInServer(node2Slops)); assertEquals("All slops from node 4 should be gone", 0, numSlopsInServer(node4Slops)); assertEquals("All slops from node 1 should be still there", node1Slops.size(), numSlopsInServer(node1Slops)); assertEquals("All slops from node 3 should be still there", node3Slops.size(), numSlopsInServer(node3Slops)); } @Test public void testPurgeByZone() { // these will perish, since nodes 2,3 are in zone 1. List<Versioned<Slop>> node2Slops = ServerTestUtils.createRandomSlops(2, 10, false, "astore"); List<Versioned<Slop>> node3Slops = ServerTestUtils.createRandomSlops(3, 10, false, "cstore"); // these will be around List<Versioned<Slop>> node1Slops = ServerTestUtils.createRandomSlops(1, 10, false, "bstore"); List<Versioned<Slop>> node5Slops = ServerTestUtils.createRandomSlops(5, 10, false, "astore"); List<Versioned<Slop>> slops = new ArrayList<Versioned<Slop>>(); slops.addAll(node2Slops); slops.addAll(node3Slops); slops.addAll(node1Slops); slops.addAll(node5Slops); Collections.shuffle(slops); // Populate the slops. populateSlops(slops); // Kick off the slop purge job adminClient.storeMntOps.slopPurgeJob(PURGE_SERVER_ID, null, 1, null); ServerTestUtils.waitForAsyncOperationOnServer(purgedServer, "Purge", 5000); assertEquals("All slops from node 2 should be gone", 0, numSlopsInServer(node2Slops)); assertEquals("All slops from node 3 should be gone", 0, numSlopsInServer(node3Slops)); assertEquals("All slops from node 1 should be still there", node1Slops.size(), numSlopsInServer(node1Slops)); assertEquals("All slops from node 5 should be still there", node5Slops.size(), numSlopsInServer(node5Slops)); } @Test public void testPurgeByStore() { // these will perish, since they belong to "astore" and "bstore" List<Versioned<Slop>> node2Slops = ServerTestUtils.createRandomSlops(2, 10, false, "astore"); List<Versioned<Slop>> node5Slops = ServerTestUtils.createRandomSlops(5, 10, false, "bstore"); // these will be around List<Versioned<Slop>> node1Slops = ServerTestUtils.createRandomSlops(1, 10, false, "cstore"); List<Versioned<Slop>> slops = new ArrayList<Versioned<Slop>>(); slops.addAll(node2Slops); slops.addAll(node1Slops); slops.addAll(node5Slops); Collections.shuffle(slops); // Populate the slops. populateSlops(slops); // Kick off the slop purge job adminClient.storeMntOps.slopPurgeJob(PURGE_SERVER_ID, null, -1, Lists.newArrayList("astore", "bstore")); ServerTestUtils.waitForAsyncOperationOnServer(purgedServer, "Purge", 5000); assertEquals("All slops from node 2 should be gone", 0, numSlopsInServer(node2Slops)); assertEquals("All slops from node 5 should be gone", 0, numSlopsInServer(node5Slops)); assertEquals("All slops from node 1 should be still there", node1Slops.size(), numSlopsInServer(node1Slops)); } @Test public void testPurgeMixed() { // these will perish, since // 1) Node 2 is in zone 1 List<Versioned<Slop>> node2Slops = ServerTestUtils.createRandomSlops(2, 10, false, "astore"); // 2) Slops are for store "cstore" List<Versioned<Slop>> node3Slops = ServerTestUtils.createRandomSlops(3, 10, false, "cstore"); // 3) Slop is for node 1 List<Versioned<Slop>> node1Slops = ServerTestUtils.createRandomSlops(1, 10, false, "bstore"); // these will be around List<Versioned<Slop>> node5Slops = ServerTestUtils.createRandomSlops(5, 10, false, "astore"); List<Versioned<Slop>> slops = new ArrayList<Versioned<Slop>>(); slops.addAll(node2Slops); slops.addAll(node3Slops); slops.addAll(node1Slops); slops.addAll(node5Slops); Collections.shuffle(slops); // Populate the slops. populateSlops(slops); // Kick off the slop purge job adminClient.storeMntOps.slopPurgeJob(PURGE_SERVER_ID, Lists.newArrayList(1), 1, Lists.newArrayList("cstore")); ServerTestUtils.waitForAsyncOperationOnServer(purgedServer, "Purge", 5000); assertEquals("All slops from node 2 should be gone", 0, numSlopsInServer(node2Slops)); assertEquals("All slops from node 3 should be gone", 0, numSlopsInServer(node3Slops)); assertEquals("All slops from node 1 should be gone", 0, numSlopsInServer(node1Slops)); assertEquals("All slops from node 5 should be still there", node5Slops.size(), numSlopsInServer(node5Slops)); } @After public void tearDown() throws IOException { for(VoldemortServer server: servers) { ServerTestUtils.stopVoldemortServer(server); } adminClient.close(); } }