package io.eguan.dtx; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * 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. * #L% */ import static io.eguan.dtx.DtxResourceManagerState.LATE; import static io.eguan.dtx.DtxResourceManagerState.POST_SYNC_PROCESSING; import static io.eguan.dtx.DtxResourceManagerState.SYNCHRONIZING; import static io.eguan.dtx.DtxResourceManagerState.UNDETERMINED; import static io.eguan.dtx.DtxResourceManagerState.UP_TO_DATE; import static org.junit.Assert.assertTrue; import io.eguan.dtx.DtxManager; import io.eguan.dtx.DtxResourceManager; import io.eguan.dtx.DtxResourceManagerState; import io.eguan.dtx.DtxEventListeners.StateCountListener; import io.eguan.dtx.DtxEventListeners.StateSetCountListener; import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.transaction.xa.XAException; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.Table; import com.google.common.collect.TreeBasedTable; /** * Tests covering the discovery phase between nodes upon joining the cluster. * * @author oodrive * @author pwehrle * */ public final class TestDtxManagerDiscoveryL extends AbstractCommonClusterTest { private static final Logger LOGGER = LoggerFactory.getLogger(TestDtxManagerDiscoveryL.class); private static final int NB_OF_TEST_TX = 10; private static final int DISCOVERY_WAIT_TIME_MS = 10000; private static final List<DtxResourceManagerState> INITIAL_STATES = Arrays.asList(new DtxResourceManagerState[] { UNDETERMINED, SYNCHRONIZING, LATE }); private static final List<DtxResourceManagerState> LATE_STATES = Arrays.asList(new DtxResourceManagerState[] { LATE, SYNCHRONIZING, POST_SYNC_PROCESSING, UP_TO_DATE }); /** * Tests successful discovery of nodes upon startup with pre-registered {@link DtxResourceManager}s. * * @throws XAException * if mock setup fails, not part of this test * @throws IOException * if writing transactions to journals fails, not part of this test * @throws IllegalStateException * if setting up existing journals fails, not part of this test * @throws InterruptedException * if interrupted while waiting for results */ @Test public final void testDiscoveryBetweenNodesOnInit() throws XAException, IllegalStateException, IOException, InterruptedException { LOGGER.info("Executing"); final Set<DtxManager> dtxMgrs = DTX_MGR_JOURNAL_MAP.keySet(); final UUID resUuid = UUID.randomUUID(); final TreeBasedTable<Long, UUID, DtxManager> testTable = TreeBasedTable.create(); long targetTxId = DtxTestHelper.nextTxId(); final CountDownLatch allDoneLatch = new CountDownLatch(NB_OF_NODES); final HashMultimap<UUID, DtxManager> lateMap = HashMultimap.create(); final HashMultimap<UUID, DtxManager> uptodateMap = HashMultimap.create(); for (final Iterator<DtxManager> dtxIter = dtxMgrs.iterator(); dtxIter.hasNext();) { final DtxManager currDtxMgr = dtxIter.next(); currDtxMgr.stop(); targetTxId += NB_OF_TEST_TX; testTable.put(Long.valueOf(targetTxId), resUuid, currDtxMgr); if (dtxIter.hasNext()) { lateMap.put(resUuid, currDtxMgr); } else { uptodateMap.put(resUuid, currDtxMgr); } } final StateSetCountListener lateListener = new StateSetCountListener(allDoneLatch, LATE_STATES, lateMap); final StateCountListener upToDateListener = new StateCountListener(allDoneLatch, UP_TO_DATE, lateMap); final Table<DtxResourceManager, Long, Path> resMgrMap = DtxTestHelper.prepareExistingJournals(testTable, DTX_MGR_JOURNAL_MAP, SETUP_ROT_MGR); // initializes all nodes for (final DtxResourceManager currResMgr : resMgrMap.rowKeySet()) { final Map<Long, Path> currRow = resMgrMap.row(currResMgr); for (final Long currIndex : currRow.keySet()) { final UUID currResId = currResMgr.getId(); final DtxManager currDtxMgr = testTable.get(currIndex, currResId); currDtxMgr.init(); LOGGER.debug("Initialized; node=" + currDtxMgr.getNodeId()); currDtxMgr.registerResourceManager(currResMgr); assertTrue(INITIAL_STATES.contains(currDtxMgr.getResourceManagerState(resUuid))); currDtxMgr.registerDtxEventListener(lateListener); currDtxMgr.registerDtxEventListener(upToDateListener); } } // starts all nodes for (final DtxManager currDtxMgr : dtxMgrs) { LOGGER.debug("Starting; node=" + currDtxMgr.getNodeId()); currDtxMgr.start(); LOGGER.debug("Started; node=" + currDtxMgr.getNodeId() + ", last txId=" + currDtxMgr.getLastCompleteTxIdForResMgr(resUuid)); } // waits for all nodes to be up to date assertTrue(allDoneLatch.await(DISCOVERY_WAIT_TIME_MS, TimeUnit.MILLISECONDS)); for (final DtxManager currDtxMgr : dtxMgrs) { currDtxMgr.unregisterDtxEventListener(lateListener); currDtxMgr.unregisterDtxEventListener(upToDateListener); currDtxMgr.unregisterResourceManager(resUuid); } } /** * Tests successful discovery of nodes upon registering each with already started nodes. * * @throws XAException * if mock setup fails, not part of this test * @throws IOException * if writing transactions to journals fails, not part of this test * @throws IllegalStateException * if setting up existing journals fails, not part of this test * @throws InterruptedException * if interrupted while waiting for results */ @Test public final void testDiscoveryBetweenNodesOnRegister() throws XAException, IllegalStateException, IOException, InterruptedException { LOGGER.info("Executing"); final Set<DtxManager> dtxMgrs = DTX_MGR_JOURNAL_MAP.keySet(); final UUID resUuid = UUID.randomUUID(); final TreeBasedTable<Long, UUID, DtxManager> testTable = TreeBasedTable.create(); long targetTxId = DtxTestHelper.nextTxId(); final CountDownLatch allDoneLatch = new CountDownLatch(NB_OF_NODES); final HashMultimap<UUID, DtxManager> lateMap = HashMultimap.create(); final HashMultimap<UUID, DtxManager> uptodateMap = HashMultimap.create(); for (final Iterator<DtxManager> dtxIter = dtxMgrs.iterator(); dtxIter.hasNext();) { final DtxManager currDtxMgr = dtxIter.next(); targetTxId += NB_OF_TEST_TX; testTable.put(Long.valueOf(targetTxId), resUuid, currDtxMgr); if (dtxIter.hasNext()) { lateMap.put(resUuid, currDtxMgr); } else { uptodateMap.put(resUuid, currDtxMgr); } } final StateSetCountListener lateListener = new StateSetCountListener(allDoneLatch, LATE_STATES, lateMap); final StateCountListener upToDateListener = new StateCountListener(allDoneLatch, UP_TO_DATE, lateMap); final Table<DtxResourceManager, Long, Path> resMgrMap = DtxTestHelper.prepareExistingJournals(testTable, DTX_MGR_JOURNAL_MAP, SETUP_ROT_MGR); // registers listeners on all nodes for (final DtxManager currDtxMgr : dtxMgrs) { currDtxMgr.registerDtxEventListener(lateListener); currDtxMgr.registerDtxEventListener(upToDateListener); } for (final DtxResourceManager currResMgr : resMgrMap.rowKeySet()) { final Map<Long, Path> currRow = resMgrMap.row(currResMgr); for (final Long currIndex : currRow.keySet()) { final DtxManager currDtxMgr = testTable.get(currIndex, resUuid); currDtxMgr.registerResourceManager(currResMgr); assertTrue(INITIAL_STATES.contains(currDtxMgr.getResourceManagerState(resUuid))); } } // waits for all nodes to be up to date assertTrue(allDoneLatch.await(DISCOVERY_WAIT_TIME_MS, TimeUnit.MILLISECONDS)); for (final DtxManager currDtxMgr : dtxMgrs) { currDtxMgr.unregisterDtxEventListener(lateListener); currDtxMgr.unregisterDtxEventListener(upToDateListener); currDtxMgr.unregisterResourceManager(resUuid); } } /** * Tests the discovery process with resource managers only registered on one node and absent on all other nodes. * * @throws XAException * if mock setup fails, not part of this test * @throws IOException * if writing transactions to journals fails, not part of this test * @throws IllegalStateException * if setting up existing journals fails, not part of this test * @throws InterruptedException * if interrupted while waiting for results */ @Test public final void testDiscoverResMgrOnlyOnOneNode() throws XAException, IllegalStateException, IOException, InterruptedException { LOGGER.info("Executing"); final Set<DtxManager> dtxMgrs = DTX_MGR_JOURNAL_MAP.keySet(); final TreeBasedTable<Long, UUID, DtxManager> testTable = TreeBasedTable.create(); long targetTxId = DtxTestHelper.nextTxId(); final CountDownLatch allUpToDateLatch = new CountDownLatch(NB_OF_NODES); final HashMultimap<UUID, DtxManager> upToDateMap = HashMultimap.create(); for (final DtxManager currDtxMgr : dtxMgrs) { targetTxId += NB_OF_TEST_TX; final UUID resUuid = UUID.randomUUID(); testTable.put(Long.valueOf(targetTxId), resUuid, currDtxMgr); upToDateMap.put(resUuid, currDtxMgr); } final StateCountListener upToDateListener = new StateCountListener(allUpToDateLatch, UP_TO_DATE, upToDateMap); final Table<DtxResourceManager, Long, Path> resMgrMap = DtxTestHelper.prepareExistingJournals(testTable, DTX_MGR_JOURNAL_MAP, SETUP_ROT_MGR); // registers listener on all nodes for (final DtxManager currDtxMgr : dtxMgrs) { currDtxMgr.registerDtxEventListener(upToDateListener); } for (final DtxResourceManager currResMgr : resMgrMap.rowKeySet()) { final Map<Long, Path> currRow = resMgrMap.row(currResMgr); for (final Long currIndex : currRow.keySet()) { final UUID resUuid = currResMgr.getId(); final DtxManager currDtxMgr = testTable.get(currIndex, resUuid); currDtxMgr.registerResourceManager(currResMgr); assertTrue(INITIAL_STATES.contains(currDtxMgr.getResourceManagerState(resUuid))); } } // waits for all nodes to be up to date assertTrue(allUpToDateLatch.await(DISCOVERY_WAIT_TIME_MS, TimeUnit.MILLISECONDS)); for (final DtxManager currDtxMgr : dtxMgrs) { currDtxMgr.unregisterDtxEventListener(upToDateListener); } } }