/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.ambari.server.agent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.apache.ambari.server.agent.AgentCommand.AgentCommandType;
import org.easymock.EasyMock;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestActionQueue {
private static Logger LOG = LoggerFactory.getLogger(TestActionQueue.class);
private static int threadCount = 100;
static class ActionQueueOperation implements Runnable {
enum OpType {
ENQUEUE,
DEQUEUE,
DEQUEUEALL,
CHECKPENDING,
UPDATEHOSTLIST
}
private volatile boolean shouldRun = true;
private long [] opCounts;
private ActionQueue actionQueue;
private OpType operation;
private String[] hosts;
public ActionQueueOperation(ActionQueue aq, String [] hosts, OpType op) {
actionQueue = aq;
operation = op;
this.hosts = hosts;
opCounts = new long [hosts.length];
for (int i = 0; i < hosts.length; i++) {
opCounts[i] = 0;
}
}
public long [] getOpCounts() {
return opCounts;
}
public void stop() {
shouldRun = false;
}
@Override
public void run() {
try {
switch (operation) {
case ENQUEUE:
enqueueOp();
break;
case DEQUEUE:
dequeueOp();
break;
case DEQUEUEALL:
dequeueAllOp();
break;
case CHECKPENDING:
checkPending();
break;
case UPDATEHOSTLIST:
updateHostList();
break;
}
} catch (Exception ex) {
LOG.error("Failure", ex);
throw new RuntimeException("Failure", ex);
}
}
private void checkPending() throws InterruptedException {
while (shouldRun) {
int index = 0;
for (String host: hosts) {
actionQueue.hasPendingTask(host);
opCounts[index]++;
index++;
}
Thread.sleep(1);
}
}
private void updateHostList() throws InterruptedException {
HashSet<String> hostsWithTasks = new HashSet<>();
while (shouldRun) {
for (String host: hosts) {
hostsWithTasks.add(host);
if (hostsWithTasks.size() % 2 == 0) {
actionQueue.updateListOfHostsWithPendingTask(hostsWithTasks);
} else {
actionQueue.updateListOfHostsWithPendingTask(null);
}
opCounts[0]++;
}
Thread.sleep(1);
}
}
private void enqueueOp() throws InterruptedException {
while (shouldRun) {
int index = 0;
for (String host: hosts) {
actionQueue.enqueue(host, new StatusCommand());
opCounts[index]++;
index++;
}
Thread.sleep(1);
}
}
private void dequeueOp() throws InterruptedException {
while (shouldRun) {
int index = 0;
for (String host: hosts) {
AgentCommand cmd = actionQueue.dequeue(host);
if (cmd != null) {
opCounts[index]++;
}
index++;
}
Thread.sleep(1);
}
}
private void dequeueAllOp() throws InterruptedException {
while (shouldRun) {
int index = 0;
for (String host : hosts) {
List<AgentCommand> cmds = actionQueue.dequeueAll(host);
if (cmds != null && !cmds.isEmpty()) {
opCounts[index] += cmds.size();
}
index++;
}
Thread.sleep(1);
}
}
}
@Test
public void testConcurrentOperations() throws InterruptedException {
ActionQueue aq = new ActionQueue();
String[] hosts = new String[] { "h0", "h1", "h2", "h3", "h4", "h5", "h6",
"h7", "h8", "h9" };
ActionQueueOperation[] enqueOperators = new ActionQueueOperation[threadCount];
ActionQueueOperation[] dequeOperators = new ActionQueueOperation[threadCount];
ActionQueueOperation[] dequeAllOperators = new ActionQueueOperation[threadCount];
List<Thread> producers = new ArrayList<>();
List<Thread> consumers = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
dequeOperators[i] = new ActionQueueOperation(aq, hosts,
ActionQueueOperation.OpType.DEQUEUE);
Thread t = new Thread(dequeOperators[i]);
consumers.add(t);
t.start();
}
for (int i = 0; i < threadCount; i++) {
enqueOperators[i] = new ActionQueueOperation(aq, hosts,
ActionQueueOperation.OpType.ENQUEUE);
Thread t = new Thread(enqueOperators[i]);
producers.add(t);
t.start();
}
for (int i = 0; i < threadCount; i++) {
dequeAllOperators[i] = new ActionQueueOperation(aq, hosts,
ActionQueueOperation.OpType.DEQUEUEALL);
Thread t = new Thread(dequeAllOperators[i]);
consumers.add(t);
t.start();
}
// Run for some time
Thread.sleep(100);
// Stop the enqueue
for (int i = 0; i < threadCount; i++) {
enqueOperators[i].stop();
}
for (Thread producer : producers) {
producer.join();
}
// Give time to get everything dequeued
boolean allDequeued = false;
while (!allDequeued) {
Thread.sleep(10);
allDequeued = true;
for (String host: hosts) {
if (aq.size(host) > 0) {
allDequeued = false;
break;
}
}
}
// Stop all threads
for (int i = 0; i < threadCount; i++) {
dequeOperators[i].stop();
dequeAllOperators[i].stop();
}
for (Thread consumer : consumers) {
consumer.join();
}
for (int h = 0; h<hosts.length; h++) {
long opsEnqueued = 0;
long opsDequeued = 0;
for (int i = 0; i < threadCount; i++) {
opsEnqueued += enqueOperators[i].getOpCounts()[h];
opsDequeued += dequeOperators[i].getOpCounts()[h];
opsDequeued += dequeAllOperators[i].getOpCounts()[h];
}
assertTrue(opsEnqueued != 0); //Prevent degenerate case of all zeros.
assertEquals(0, aq.size(hosts[h])); //Everything should be dequeued
LOG.info("Host: " + hosts[h] + ", opsEnqueued: " + opsEnqueued
+ ", opsDequeued: " + opsDequeued);
assertEquals(opsDequeued, opsEnqueued);
}
}
@Test
public void testConcurrentHostCheck() throws InterruptedException {
ActionQueue aq = new ActionQueue();
String[] hosts = new String[] { "h0", "h1", "h2", "h3", "h4" };
ActionQueueOperation[] hostCheckers = new ActionQueueOperation[threadCount];
ActionQueueOperation[] hostUpdaters = new ActionQueueOperation[threadCount];
List<Thread> producers = new ArrayList<>();
List<Thread> consumers = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
hostCheckers[i] = new ActionQueueOperation(aq, hosts,
ActionQueueOperation.OpType.CHECKPENDING);
Thread t = new Thread(hostCheckers[i]);
consumers.add(t);
t.start();
}
for (int i = 0; i < threadCount; i++) {
hostUpdaters[i] = new ActionQueueOperation(aq, hosts,
ActionQueueOperation.OpType.UPDATEHOSTLIST);
Thread t = new Thread(hostUpdaters[i]);
producers.add(t);
t.start();
}
// Run for some time
Thread.sleep(100);
for (int i = 0; i < threadCount; i++) {
hostUpdaters[i].stop();
}
for (Thread producer : producers) {
producer.join();
}
for (int i = 0; i < threadCount; i++) {
hostCheckers[i].stop();
}
for (Thread consumer : consumers) {
consumer.join();
}
int totalChecks = 0;
int totalUpdates = 0;
for (int i = 0; i < threadCount; i++) {
totalChecks += hostUpdaters[i].getOpCounts()[0];
for (int h = 0; h<hosts.length; h++) {
totalUpdates += hostCheckers[i].getOpCounts()[h];
}
}
LOG.info("Report: totalChecks: " + totalChecks + ", totalUpdates: " + totalUpdates);
HashSet<String> hostsWithPendingtasks = new HashSet<>();
aq.updateListOfHostsWithPendingTask(hostsWithPendingtasks);
hostsWithPendingtasks.add("h1");
aq.updateListOfHostsWithPendingTask(hostsWithPendingtasks);
assertTrue(aq.hasPendingTask("h1"));
assertFalse(aq.hasPendingTask("h2"));
hostsWithPendingtasks.add("h1");
hostsWithPendingtasks.add("h2");
aq.updateListOfHostsWithPendingTask(hostsWithPendingtasks);
assertTrue(aq.hasPendingTask("h1"));
assertTrue(aq.hasPendingTask("h2"));
hostsWithPendingtasks.clear();
hostsWithPendingtasks.add("h2");
aq.updateListOfHostsWithPendingTask(hostsWithPendingtasks);
assertFalse(aq.hasPendingTask("h1"));
assertTrue(aq.hasPendingTask("h2"));
}
/**
* @throws Exception
*/
@Test
public void testDequeueCommandType() throws Exception {
ActionQueue queue = new ActionQueue();
String c6401 = "c6401.ambari.apache.org";
String c6402 = "c6402.ambari.apache.org";
queue.enqueue(c6401,
EasyMock.createMockBuilder(ExecutionCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(StatusCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(AlertDefinitionCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(StatusCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(AlertDefinitionCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(StatusCommand.class).createNiceMock());
queue.enqueue(c6401,
EasyMock.createMockBuilder(AlertDefinitionCommand.class).createNiceMock());
queue.enqueue(c6402,
EasyMock.createMockBuilder(ExecutionCommand.class).createNiceMock());
queue.enqueue(c6402,
EasyMock.createMockBuilder(StatusCommand.class).createNiceMock());
queue.enqueue(c6402,
EasyMock.createMockBuilder(AlertDefinitionCommand.class).createNiceMock());
assertEquals(7, queue.size(c6401));
List<AgentCommand> commands = queue.dequeue(c6401,
AgentCommandType.ALERT_DEFINITION_COMMAND);
assertNotNull(commands);
assertEquals(3, commands.size());
assertEquals(4, queue.size(c6401));
assertEquals(3, queue.size(c6402));
}
}