/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb.iv2; import com.google_voltpatches.common.collect.Lists; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.voltdb.CommandLog.CompletionChecks; import org.voltdb.SnapshotCompletionMonitor; import org.voltdb.StoredProcedureInvocation; import org.voltdb.messaging.Iv2InitiateTaskMessage; import java.util.List; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class TestDurabilityListener { private SpScheduler m_sched; private TransactionTaskQueue m_taskQueue; private SimpleListener m_listener; private SpDurabilityListener dut; class SimpleListener implements SpScheduler.DurableUniqueIdListener { public long m_spUniqueId = Long.MIN_VALUE; public long m_mpUniqueId = Long.MIN_VALUE; public boolean m_notified = false; @Override public void lastUniqueIdsMadeDurable(long spUniqueId, long mpUniqueId) { m_spUniqueId = spUniqueId; m_mpUniqueId = mpUniqueId; m_notified = true; } public void resetNotified() { m_notified = false; } } @Before public void setup() { m_sched = mock(SpScheduler.class); m_taskQueue = mock(TransactionTaskQueue.class); m_listener = new SimpleListener(); dut = new SpDurabilityListener(m_sched, m_taskQueue); dut.setUniqueIdListener(m_listener); } @Test public void testSp() { for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); final List<Long> spUniqIds = logSp(0, 1, 2); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertEquals(spUniqIds.get(2).longValue(), m_listener.m_spUniqueId); assertEquals(Long.MIN_VALUE, m_listener.m_mpUniqueId); } } @Test public void testSpThenEverywhere() { for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); final List<Long> spUniqIds = logSp(10); final List<Long> mpUniqIds = logEverywhere(5); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertEquals(spUniqIds.get(0).longValue(), m_listener.m_spUniqueId); assertEquals(mpUniqIds.get(0).longValue(), m_listener.m_mpUniqueId); } } @Test public void testMp() { for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); final List<Long> mpUniqIds = logMp(0, 1, 2); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertEquals(Long.MIN_VALUE, m_listener.m_spUniqueId); assertEquals(mpUniqIds.get(2).longValue(), m_listener.m_mpUniqueId); } } @Test public void testInitializeID() { for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); dut.initializeLastDurableUniqueId(UniqueIdGenerator.makeIdFromComponents(5, 0, 0)); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertEquals(UniqueIdGenerator.makeIdFromComponents(5, 0, 0), m_listener.m_spUniqueId); } } @Test public void testNoDuplicateNotifications() { for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); final List<Long> spUniqIds = logSp(0, 1, 2); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertEquals(spUniqIds.get(2).longValue(), m_listener.m_spUniqueId); assertEquals(Long.MIN_VALUE, m_listener.m_mpUniqueId); assertTrue(m_listener.m_notified); // No new txns before this sync, should not have notified the listener m_listener.resetNotified(); dut.startNewTaskList(dut.getNumberOfTasks()).processChecks(); assertFalse(m_listener.m_notified); } } @Test public void testProcessDurabilityChecks() { SiteTaskerQueue stq = mock(SiteTaskerQueue.class); SnapshotCompletionMonitor scm = mock(SnapshotCompletionMonitor.class); SpScheduler sched = spy(new SpScheduler(1, stq, scm)); sched.setLock(new Object()); TransactionTaskQueue taskQueue = mock(TransactionTaskQueue.class); SimpleListener listener = new SimpleListener(); SpDurabilityListener dut = new SpDurabilityListener(sched, taskQueue); dut.setUniqueIdListener(listener); int cnt = 0; ArgumentCaptor<SiteTasker.SiteTaskerRunnable> captor = ArgumentCaptor.forClass(SiteTasker.SiteTaskerRunnable.class); for (boolean isSync : new boolean[] {false, true}) { dut.createFirstCompletionCheck(isSync, true); CompletionChecks completionChecks = dut.startNewTaskList(dut.getNumberOfTasks()); dut.processDurabilityChecks(completionChecks); verify(sched, never()).processDurabilityChecks(completionChecks); final List<Long> spUniqIds = logSp(dut, taskQueue, 0, 1, 2); completionChecks = dut.startNewTaskList(dut.getNumberOfTasks()); dut.processDurabilityChecks(completionChecks); verify(sched).processDurabilityChecks(completionChecks); cnt++; verify(stq, times(cnt)).offer(captor.capture()); captor.getValue().run(); assertEquals(spUniqIds.get(2).longValue(), listener.m_spUniqueId); assertEquals(Long.MIN_VALUE, listener.m_mpUniqueId); assertTrue(listener.m_notified); final List<Long> mpUniqIds = logMp(dut, taskQueue, 0, 1, 2); completionChecks = dut.startNewTaskList(dut.getNumberOfTasks()); dut.processDurabilityChecks(completionChecks); verify(sched).processDurabilityChecks(completionChecks); cnt++; verify(stq, times(cnt)).offer(captor.capture()); captor.getValue().run(); assertEquals(spUniqIds.get(2).longValue(), listener.m_spUniqueId); assertEquals(mpUniqIds.get(2).longValue(), listener.m_mpUniqueId); assertTrue(listener.m_notified); } } private List<Long> logSp(int...timestamps) { return logSp(dut, m_taskQueue, timestamps); } private static List<Long> logSp(SpDurabilityListener dut, TransactionTaskQueue taskQueue, int...timestamps) { List<Long> uniqIds = Lists.newArrayList(); for (int ts : timestamps) { final long id = UniqueIdGenerator.makeIdFromComponents(ts, 0, 0); uniqIds.add(id); dut.addTransaction(newInitMsg(true, taskQueue, id)); } return uniqIds; } private List<Long> logMp(int... timestamps) { return logMp(dut, m_taskQueue, timestamps); } private static List<Long> logMp(SpDurabilityListener dut, TransactionTaskQueue taskQueue, int... timestamps) { List<Long> uniqIds = Lists.newArrayList(); for (int ts : timestamps) { final long id = UniqueIdGenerator.makeIdFromComponents(ts, 0, MpInitiator.MP_INIT_PID); uniqIds.add(id); dut.addTransaction(newInitMsg(false, taskQueue, id)); } return uniqIds; } private List<Long> logEverywhere(int... timestamps) { return logEverywhere(dut, m_taskQueue, timestamps); } private static List<Long> logEverywhere(SpDurabilityListener dut, TransactionTaskQueue taskQueue, int... timestamps) { List<Long> uniqIds = Lists.newArrayList(); for (int ts : timestamps) { final long id = UniqueIdGenerator.makeIdFromComponents(ts, 0, MpInitiator.MP_INIT_PID); uniqIds.add(id); dut.addTransaction(newInitMsg(true, taskQueue, id)); } return uniqIds; } private static SpProcedureTask newInitMsg(boolean isSp, TransactionTaskQueue taskQueue, long uniqId) { final Iv2InitiateTaskMessage msg = new Iv2InitiateTaskMessage(0, 0, 0, 0, uniqId, false, isSp, new StoredProcedureInvocation(), 0, 0, false); return new SpProcedureTask(null, "Hello", taskQueue, msg); } }