/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.addthis.hydra.job.spawn;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import com.addthis.hydra.job.JobTask;
import com.addthis.hydra.job.mq.HostState;
import com.addthis.hydra.job.mq.JobKey;
import org.junit.Test;
import static org.junit.Assert.*;
public class SpawnQueueManagerTest {
@Test
public void hostSortingTest() {
TreeMap<Integer, LinkedList<SpawnQueueItem>> mappedQueues = new TreeMap<>();
mappedQueues.put(0, new LinkedList<>());
SpawnQueueManager spawnQueueManager = new SpawnQueueManager(mappedQueues);
HostState noSlots = makeHostState("h1", 0, 0);
HostState oneSlotHighMeanActive = makeHostState("h2", 1, .95);
HostState oneSlotLowMeanActive = makeHostState("h3", 1, .03);
HostState twoSlots = makeHostState("h4", 2, .99);
List<HostState> allHosts = Arrays.asList(noSlots, oneSlotHighMeanActive, oneSlotLowMeanActive, twoSlots);
spawnQueueManager.updateAllHostAvailSlots(allHosts);
// If host with two slots is available, should choose that host.
assertEquals("should choose host with two slots", twoSlots, spawnQueueManager.findBestHostToRunTask(allHosts, true));
// If multiple hosts with one slot are available, should choose one with lesser meanActiveTasks.
assertEquals("should choose less active host", oneSlotLowMeanActive, spawnQueueManager.findBestHostToRunTask(allHosts.subList(0, 3), true));
// If only available host has no slots, should return null
assertEquals("should return null", null, spawnQueueManager.findBestHostToRunTask(Arrays.asList(noSlots), true));
// Simulate a task kicking on the twoSlots host. Then we should choose the oneSlot host with the lowest meanActive value.
spawnQueueManager.markHostTaskActive(twoSlots.getHostUuid());
assertEquals("after kick, should return less active host", oneSlotLowMeanActive, spawnQueueManager.findBestHostToRunTask(allHosts, true));
}
@Test
public void queueTest() {
SpawnQueueManager spawnQueueManager = new SpawnQueueManager(new TreeMap<>());
JobKey key1 = new JobKey("job", 0);
spawnQueueManager.addTaskToQueue(0, key1, 0, false);
JobKey key2 = new JobKey("job", 1);
spawnQueueManager.addTaskToQueue(0, key2, 0, false);
JobKey headKey = new JobKey("job", 2);
spawnQueueManager.addTaskToQueue(0, headKey, 0, true);
JobKey highPriKey = new JobKey("job2", 10);
spawnQueueManager.addTaskToQueue(1, highPriKey, 0, false);
Iterator<JobKey> expected = Arrays.asList(highPriKey, headKey, key1, key2).iterator();
assertEquals("should get expected number of pri=1 tasks", 1, spawnQueueManager.getTaskQueuedCount(1));
assertEquals("should get expected number of pri=0 tasks", 3, spawnQueueManager.getTaskQueuedCount(0));
for (LinkedList<SpawnQueueItem> keyList : spawnQueueManager.getQueues()) {
for (SpawnQueueItem item : keyList) {
assertEquals("should get keys in expected order", item.getJobKey(), expected.next().getJobKey());
}
}
long maxTaskBytesToMigrate = SpawnQueueManager.getTaskMigrationMaxBytes();
long limitGrowthInterval = SpawnQueueManager.getTaskMigrationLimitGrowthInterval();
// Check that small tasks can migrate soon after being put on the queue, but large tasks have to wait.
assertTrue("should allow small task to migrate immediately", spawnQueueManager.checkSizeAgeForMigration(0, 0));
assertTrue("should not allow large task to migrate, even after waiting", !spawnQueueManager.checkSizeAgeForMigration(2 * maxTaskBytesToMigrate, 2 * limitGrowthInterval));
assertTrue("should not allow medium task to migrate immediately", !spawnQueueManager.checkSizeAgeForMigration(maxTaskBytesToMigrate / 2, 0));
assertTrue("should allow medium task after waiting", spawnQueueManager.checkSizeAgeForMigration(maxTaskBytesToMigrate / 2, limitGrowthInterval));
for (String hostName : Arrays.asList("a", "b", "c", "d")) {
spawnQueueManager.incrementHostAvailableSlots(hostName);
}
// Simulate a migration from host a to host b. Make sure that neither a nor b can perform a migration again for a time interval
spawnQueueManager.markMigrationBetweenHosts("a", "b");
JobTask task = new JobTask("a", 0, 0);
task.setByteCount(1);
assertTrue("should not allow migration from same host immediately", !spawnQueueManager.shouldMigrateTaskToHost(task, "c"));
JobTask task2 = new JobTask("c", 0, 0);
task2.setByteCount(1);
assertTrue("should not allow migration to same host immediately", !spawnQueueManager.shouldMigrateTaskToHost(task2, "b"));
JobTask task3 = new JobTask("d", 0, 0);
task3.setByteCount(1);
assertTrue("should allow migration between distinct hosts", spawnQueueManager.shouldMigrateTaskToHost(task3, "c"));
}
private HostState makeHostState(String uuid, int availSlots, double meanActiveTasks) {
HostState hostState = new HostState(uuid);
hostState.setAvailableTaskSlots(availSlots);
hostState.setMeanActiveTasks(meanActiveTasks);
return hostState;
}
}