/**
* Copyright 2008 the original author or authors.
*
* 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 net.sf.katta.master;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import net.sf.katta.operation.OperationId;
import net.sf.katta.operation.master.MasterOperation;
import net.sf.katta.operation.master.MasterOperation.ExecutionInstruction;
import net.sf.katta.protocol.InteractionProtocol;
import net.sf.katta.protocol.MasterQueue;
import net.sf.katta.testutil.Mocks;
import net.sf.katta.testutil.TestUtil;
import net.sf.katta.testutil.mockito.SleepingAnswer;
import org.I0Itec.zkclient.exception.ZkInterruptedException;
import org.junit.Test;
import org.mockito.InOrder;
@SuppressWarnings("unchecked")
public class OperatorThreadTest {
protected static final List EMPTY_LIST = Collections.EMPTY_LIST;
private final InteractionProtocol _protocol = mock(InteractionProtocol.class);
private final MasterQueue _queue = mock(MasterQueue.class);
protected final MasterContext _context = new MasterContext(_protocol, Mocks.mockMaster(),
new DefaultDistributionPolicy(), _queue);
@Test(timeout = 10000)
public void testSafeMode() throws Exception {
final MasterOperation operation = mockOperation(ExecutionInstruction.EXECUTE);
when(_queue.peek()).thenReturn(operation).thenAnswer(new SleepingAnswer());
when(_protocol.getLiveNodes()).thenReturn(EMPTY_LIST);
long safeModeMaxTime = 200;
OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
// no nodes connected
Thread.sleep(safeModeMaxTime + 200);
assertTrue(operatorThread.isAlive());
assertTrue(operatorThread.isInSafeMode());
// connect nodes
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
// check safe mode & operation execution
Thread.sleep(safeModeMaxTime + 200);
assertTrue(operatorThread.isAlive());
assertFalse(operatorThread.isInSafeMode());
verify(operation, times(1)).execute(_context, EMPTY_LIST);
operatorThread.interrupt();
}
@Test(timeout = 10000)
public void testGracefulShutdownWhileInSafeMode() throws Exception {
when(_queue.peek()).thenAnswer(new SleepingAnswer());
long safeModeMaxTime = 2000;
OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
assertTrue(operatorThread.isAlive());
assertTrue(operatorThread.isInSafeMode());
operatorThread.interrupt();
operatorThread.join();
}
@Test(timeout = 10000)
public void testGracefulShutdownWhileWaitingForOperations() throws Exception {
when(_queue.peek()).thenAnswer(new SleepingAnswer());
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
long safeModeMaxTime = 200;
OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
waitUntilLeaveSafeMode(operatorThread);
assertTrue(operatorThread.isAlive());
assertFalse(operatorThread.isInSafeMode());
operatorThread.interrupt();
operatorThread.join();
}
@Test(timeout = 10000)
public void testOperationExecution() throws Exception {
final MasterOperation masterOperation1 = mockOperation(ExecutionInstruction.EXECUTE);
final MasterOperation masterOperation2 = mockOperation(ExecutionInstruction.EXECUTE);
final MasterOperation masterOperation3 = mockOperation(ExecutionInstruction.EXECUTE);
when(_queue.peek()).thenReturn(masterOperation1).thenReturn(masterOperation2).thenReturn(masterOperation3)
.thenAnswer(new SleepingAnswer());
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
long safeModeMaxTime = 200;
OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
waitUntilLeaveSafeMode(operatorThread);
// Thread.sleep(safeModeMaxTime + 100);
InOrder inOrder = inOrder(masterOperation1, masterOperation2, masterOperation3);
inOrder.verify(masterOperation1, times(1)).execute(_context, EMPTY_LIST);
inOrder.verify(masterOperation2, times(1)).execute(_context, EMPTY_LIST);
inOrder.verify(masterOperation3, times(1)).execute(_context, EMPTY_LIST);
operatorThread.interrupt();
operatorThread.join();
}
@Test(timeout = 10000)
public void testOperationWatchdog() throws Exception {
String nodeName = "node1";
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList(nodeName));
List<OperationId> operationIds = new ArrayList<OperationId>();
operationIds.add(new OperationId(nodeName, "e1"));
final MasterOperation leaderOperation = mock(MasterOperation.class);
when(leaderOperation.execute(_context, EMPTY_LIST)).thenReturn(operationIds);
setupExecutionInstruction(leaderOperation, ExecutionInstruction.EXECUTE);
when(_queue.peek()).thenReturn(leaderOperation).thenAnswer(new SleepingAnswer());
when(_protocol.isNodeOperationQueued(operationIds.get(0))).thenReturn(false);
OperationWatchdog watchdog = mock(OperationWatchdog.class);
when(_queue.moveOperationToWatching(leaderOperation, operationIds)).thenReturn(watchdog);
// start the operator
long safeModeMaxTime = 200;
final OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
waitUntilLeaveSafeMode(operatorThread);
verify(watchdog).start(_context);
operatorThread.interrupt();
operatorThread.join();
}
private void waitUntilLeaveSafeMode(final OperatorThread operatorThread) throws Exception {
TestUtil.waitUntil(false, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return operatorThread.isInSafeMode();
}
}, TimeUnit.SECONDS, 30);
}
@Test(timeout = 10000)
public void testOperationLocks_CancelLockedOperation() throws Exception {
final MasterOperation leaderOperation1 = mock(MasterOperation.class);
final MasterOperation leaderOperation2 = mock(MasterOperation.class);
ExecutionInstruction lockInstruction = MasterOperation.ExecutionInstruction.CANCEL;
runLockSituation(leaderOperation1, leaderOperation2, lockInstruction);
verify(leaderOperation2, times(0)).execute(_context, EMPTY_LIST);
}
@Test(timeout = 10000)
public void testOperationLocks_SuspendLockedTask() throws Exception {
final MasterOperation leaderOperation1 = mock(MasterOperation.class);
final MasterOperation leaderOperation2 = mock(MasterOperation.class);
ExecutionInstruction lockInstruction = MasterOperation.ExecutionInstruction.ADD_TO_QUEUE_TAIL;
runLockSituation(leaderOperation1, leaderOperation2, lockInstruction);
verify(leaderOperation2, times(0)).execute(_context, EMPTY_LIST);
verify(_queue, times(1)).add(leaderOperation2);
}
@Test(timeout = 10000)
public void testRecreateWatchdogs() throws Exception {
when(_queue.peek()).thenAnswer(new SleepingAnswer());
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
OperationWatchdog watchdog1 = mock(OperationWatchdog.class);
OperationWatchdog watchdog2 = mock(OperationWatchdog.class);
when(watchdog1.isDone()).thenReturn(true);
when(watchdog2.isDone()).thenReturn(false);
when(_queue.getWatchdogs()).thenReturn(Arrays.asList(watchdog1, watchdog2));
OperatorThread operatorThread = new OperatorThread(_context, 100);
operatorThread.start();
waitUntilLeaveSafeMode(operatorThread);
Thread.sleep(200);
verify(_queue).getWatchdogs();
verify(watchdog1).isDone();
verify(watchdog2).isDone();
verify(_queue, times(1)).removeWatchdog(watchdog1);
verify(_queue, times(0)).removeWatchdog(watchdog2);
assertTrue(operatorThread.getOperationRegistry().getRunningOperations().contains(watchdog2.getOperation()));
}
@Test(timeout = 10000)
public void testInterruptedException_Queue() throws Exception {
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
when(_queue.peek()).thenThrow(new InterruptedException());
OperatorThread operatorThread = new OperatorThread(_context, 50);
operatorThread.start();
operatorThread.join();
}
@Test(timeout = 10000)
public void testInterruptedException_Queue_Zk() throws Exception {
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
ZkInterruptedException zkInterruptedException = mock(ZkInterruptedException.class);
when(_queue.peek()).thenThrow(zkInterruptedException);
OperatorThread operatorThread = new OperatorThread(_context, 50);
operatorThread.start();
operatorThread.join();
}
@Test(timeout = 10000)
public void testInterruptedException_Operation() throws Exception {
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
final MasterOperation masterOperation = mockOperation(ExecutionInstruction.EXECUTE);
when(masterOperation.execute(_context, EMPTY_LIST)).thenThrow(new InterruptedException());
when(_queue.peek()).thenReturn(masterOperation);
OperatorThread operatorThread = new OperatorThread(_context, 50);
operatorThread.start();
operatorThread.join();
}
@Test(timeout = 1000000)
public void testInterruptedException_Operation_Zk() throws Exception {
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
ZkInterruptedException zkInterruptedException = mock(ZkInterruptedException.class);
final MasterOperation masterOperation = mockOperation(ExecutionInstruction.EXECUTE);
when(masterOperation.execute(_context, EMPTY_LIST)).thenThrow(zkInterruptedException);
when(_queue.peek()).thenReturn(masterOperation);
OperatorThread operatorThread = new OperatorThread(_context, 50);
operatorThread.start();
operatorThread.join();
}
@Test(timeout = 10000000)
public void testDontStopOnOOM() throws Exception {
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList("node1"));
when(_queue.peek()).thenThrow(new OutOfMemoryError("test exception")).thenAnswer(new SleepingAnswer());
OperatorThread operatorThread = new OperatorThread(_context, 50);
operatorThread.start();
operatorThread.join(500);
assertEquals(true, operatorThread.isAlive());
operatorThread.interrupt();
verify(_queue, atLeast(2)).peek();
}
private void runLockSituation(final MasterOperation leaderOperation1, final MasterOperation leaderOperation2,
ExecutionInstruction instruction) throws Exception, InterruptedException {
String nodeName = "node1";
when(_protocol.getLiveNodes()).thenReturn(Arrays.asList(nodeName));
List<OperationId> operationIds = new ArrayList<OperationId>();
operationIds.add(new OperationId(nodeName, "e1"));
when(_protocol.isNodeOperationQueued(operationIds.get(0))).thenReturn(false);
OperationWatchdog watchdog = mock(OperationWatchdog.class);
when(_queue.moveOperationToWatching(leaderOperation1, operationIds)).thenReturn(watchdog);
setupExecutionInstruction(leaderOperation1, ExecutionInstruction.EXECUTE);
setupExecutionInstruction(leaderOperation2, instruction);
when(leaderOperation1.execute(_context, EMPTY_LIST)).thenReturn(operationIds);
when(_queue.peek()).thenReturn(leaderOperation1).thenReturn(leaderOperation2).thenAnswer(new SleepingAnswer());
// start the operator
long safeModeMaxTime = 200;
OperatorThread operatorThread = new OperatorThread(_context, safeModeMaxTime);
operatorThread.start();
// let operation1 be executed
waitUntilLeaveSafeMode(operatorThread);
// Thread.sleep(safeModeMaxTime + 100);
verify(leaderOperation1, times(1)).execute(_context, EMPTY_LIST);
operatorThread.interrupt();
operatorThread.join();
}
private MasterOperation mockOperation(ExecutionInstruction instruction) throws Exception {
MasterOperation masterOperation = mock(MasterOperation.class);
when(masterOperation.getExecutionInstruction((List<MasterOperation>) notNull())).thenReturn(instruction);
return masterOperation;
}
private void setupExecutionInstruction(final MasterOperation leaderOperation, ExecutionInstruction instruction)
throws Exception {
when(leaderOperation.getExecutionInstruction((List<MasterOperation>) notNull())).thenReturn(instruction);
}
}