@Override public void awaitLiveNode() throws Exception { LOGGER.debug("ENTER awaitLiveNode"); try { boolean liveWhileLocked = false; while (!liveWhileLocked) { //check first without holding any lock final SharedStateManager.State state = readSharedState(); if (state == SharedStateManager.State.LIVE) { //verify if the state is live while holding the live node lock too liveWhileLocked = lockLiveAndCheckLiveState(); } else { LOGGER.debugf("state while awaiting live node: %s", state); } if (!liveWhileLocked) { checkInterrupted(() -> "awaitLiveNode got interrupted!"); pauser.idle(); } } //state is LIVE and live lock is acquired and valid LOGGER.debugf("acquired live node lock while state is %s: starting scheduledLiveLock", SharedStateManager.State.LIVE); this.scheduledLiveLock.start(); } finally { LOGGER.debug("EXIT awaitLiveNode"); } }
@Override public void startBackup() throws Exception { LOGGER.debug("ENTER startBackup"); try { ActiveMQServerLogger.LOGGER.waitingToBecomeBackup(); lock(scheduledBackupLock.lock()); scheduledBackupLock.start(); ActiveMQServerLogger.LOGGER.gotBackupLock(); if (getUUID() == null) readNodeId(); } finally { LOGGER.debug("EXIT startBackup"); } }
/** * Lock live node and check for a live state, taking care to renew it (if needed) or releasing it otherwise */ private boolean lockLiveAndCheckLiveState() throws Exception { lock(this.scheduledLiveLock.lock()); final long acquiredOn = System.nanoTime(); boolean liveWhileLocked = false; //check if the state is live final SharedStateManager.State stateWhileLocked; try { stateWhileLocked = readSharedState(); } catch (Throwable t) { LOGGER.error("error while holding the live node lock and tried to read the shared state", t); this.scheduledLiveLock.lock().release(); throw t; } if (stateWhileLocked == SharedStateManager.State.LIVE) { renewLiveLockIfNeeded(acquiredOn); liveWhileLocked = true; } else { LOGGER.debugf("state is %s while holding the live lock: releasing live lock", stateWhileLocked); //state is not live: can (try to) release the lock this.scheduledLiveLock.lock().release(); } return liveWhileLocked; }
@Override protected NodeManager createNodeManager() throws Exception { switch (nodeManagerType) { case InVM: return new InVMNodeManager(false); case Jdbc: final ThreadFactory daemonThreadFactory = t -> { final Thread th = new Thread(t); th.setDaemon(true); return th; }; final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(daemonThreadFactory); scheduledExecutorServices.add(scheduledExecutorService); final ExecutorService executor = Executors.newFixedThreadPool(2, daemonThreadFactory); executors.add(executor); final DatabaseStorageConfiguration dbConf = createDefaultDatabaseStorageConfiguration(); final ExecutorFactory executorFactory = new OrderedExecutorFactory(executor); return JdbcNodeManager.with(dbConf, scheduledExecutorService, executorFactory, (code, message, file) -> { code.printStackTrace(); Assert.fail(message); }); default: throw new AssertionError("enum type not supported!"); } }
@Override public void start() throws Exception { try { synchronized (this) { if (isStarted()) { return; } this.sharedStateManager = sharedStateManagerFactory.get(); LOGGER.debug("setup sharedStateManager on start"); final UUID nodeId = sharedStateManager.setup(UUIDGenerator.getInstance()::generateUUID); setUUID(nodeId); this.scheduledLiveLock = scheduledLiveLockFactory.get(); this.scheduledBackupLock = scheduledBackupLockFactory.get(); super.start(); } } catch (IllegalStateException e) { this.sharedStateManager = null; this.scheduledLiveLock = null; this.scheduledBackupLock = null; if (this.ioCriticalErrorListener != null) { this.ioCriticalErrorListener.onIOException(e, "Failed to setup the JdbcNodeManager", null); } throw e; } }
@Override public ActivateCallback startLiveNode() throws Exception { LOGGER.debug("ENTER startLiveNode"); try { setFailingBack(); final String timeoutMessage = lockAcquisitionTimeoutMillis == -1 ? "indefinitely" : lockAcquisitionTimeoutMillis + " milliseconds"; ActiveMQServerLogger.LOGGER.waitingToObtainLiveLock(timeoutMessage); lock(this.scheduledLiveLock.lock()); this.scheduledLiveLock.start(); ActiveMQServerLogger.LOGGER.obtainedLiveLock(); return new ActivateCallback() { @Override public void activationComplete() { LOGGER.debug("ENTER activationComplete"); try { //state can be written only if the live renew task is running setLive(); } catch (Exception e) { ActiveMQServerLogger.LOGGER.warn(e.getMessage(), e); } finally { LOGGER.debug("EXIT activationComplete"); } } }; } finally { LOGGER.debug("EXIT startLiveNode"); } }
@Override public boolean isAwaitingFailback() throws Exception { LOGGER.debug("ENTER isAwaitingFailback"); try { return readSharedState() == SharedStateManager.State.FAILING_BACK; } finally { LOGGER.debug("EXIT isAwaitingFailback"); } }
private static JdbcNodeManager usingDataSource(String brokerId, int networkTimeoutMillis, long lockExpirationMillis, long lockRenewPeriodMillis, long lockAcquisitionTimeoutMillis, DataSource dataSource, SQLProvider provider, ScheduledExecutorService scheduledExecutorService, ExecutorFactory executorFactory, IOCriticalErrorListener ioCriticalErrorListener) { return new JdbcNodeManager( () -> JdbcSharedStateManager.usingDataSource(brokerId, networkTimeoutMillis, executorFactory == null ? null : executorFactory.getExecutor(), lockExpirationMillis, dataSource, provider), lockRenewPeriodMillis, lockAcquisitionTimeoutMillis, scheduledExecutorService, executorFactory, ioCriticalErrorListener); }
@Override public synchronized void stop() throws Exception { if (isStarted()) { try { this.scheduledLiveLock.stop(); this.scheduledBackupLock.stop(); } finally { super.stop(); this.sharedStateManager.close(); this.sharedStateManager = null; this.scheduledLiveLock = null; this.scheduledBackupLock = null; } } }
protected NodeManager createNodeManager(final File directory, boolean replicatingBackup) { NodeManager manager; if (!configuration.isPersistenceEnabled()) { manager = new InVMNodeManager(replicatingBackup); } else if (configuration.getStoreConfiguration() != null && configuration.getStoreConfiguration().getStoreType() == StoreConfiguration.StoreType.DATABASE) { final HAPolicyConfiguration.TYPE haType = configuration.getHAPolicyConfiguration() == null ? null : configuration.getHAPolicyConfiguration().getType(); if (haType == HAPolicyConfiguration.TYPE.SHARED_STORE_MASTER || haType == HAPolicyConfiguration.TYPE.SHARED_STORE_SLAVE) { if (replicatingBackup) { throw new IllegalArgumentException("replicatingBackup is not supported yet while using JDBC persistence"); } final DatabaseStorageConfiguration dbConf = (DatabaseStorageConfiguration) configuration.getStoreConfiguration(); manager = JdbcNodeManager.with(dbConf, scheduledPool, executorFactory, shutdownOnCriticalIO); } else if (haType == null || haType == HAPolicyConfiguration.TYPE.LIVE_ONLY) { if (logger.isDebugEnabled()) { logger.debug("Detected no Shared Store HA options on JDBC store"); } //LIVE_ONLY should be the default HA option when HA isn't configured manager = new FileLockNodeManager(directory, replicatingBackup, configuration.getJournalLockAcquisitionTimeout()); } else { throw new IllegalArgumentException("JDBC persistence allows only Shared Store HA options"); } } else { manager = new FileLockNodeManager(directory, replicatingBackup, configuration.getJournalLockAcquisitionTimeout()); } return manager; }
@Override public void awaitLiveStatus() { LOGGER.debug("ENTER awaitLiveStatus"); try { while (readSharedState() != SharedStateManager.State.LIVE) { pauser.idle(); } } finally { LOGGER.debug("EXIT awaitLiveStatus"); } }
private static JdbcNodeManager usingConnectionUrl(String brokerId, int networkTimeoutMillis, long lockExpirationMillis, long lockRenewPeriodMillis, long lockAcquisitionTimeoutMillis, String jdbcUrl, String driverClass, SQLProvider provider, ScheduledExecutorService scheduledExecutorService, ExecutorFactory executorFactory, IOCriticalErrorListener ioCriticalErrorListener) { return new JdbcNodeManager( () -> JdbcSharedStateManager.usingConnectionUrl(brokerId, networkTimeoutMillis, executorFactory == null ? null : executorFactory.getExecutor(), lockExpirationMillis, jdbcUrl, driverClass, provider), lockRenewPeriodMillis, lockAcquisitionTimeoutMillis, scheduledExecutorService, executorFactory, ioCriticalErrorListener); }