@Override @VisibleForTesting public void forcePersistNow(boolean full, PersistenceExceptionHandler exceptionHandler) { if (full) { BrooklynMementoRawData memento = BrooklynPersistenceUtils.newStateMemento(managementContext, MementoCopyMode.LOCAL); if (exceptionHandler == null) { exceptionHandler = persistenceRealChangeListener.getExceptionHandler(); } persistenceStoreAccess.checkpoint(memento, exceptionHandler); } else { if (!persistenceRealChangeListener.persistNowSafely()) { throw new IllegalStateException("Forced persistence failed; see logs fore more detail"); } } }
@Override public void setPersister( BrooklynMementoPersister val, PersistenceExceptionHandler exceptionHandler) { if (persistenceStoreAccess != null && persistenceStoreAccess != val) { throw new IllegalStateException( "Dynamically changing persister is not supported: old=" + persistenceStoreAccess + "; new=" + val); } if (persistenceRealChangeListener != null) { // TODO should probably throw here, but previously we have not -- so let's log for now to be // sure it's not happening LOG.warn( "Persister reset after listeners have been set", new Throwable("Source of persister reset")); } this.persistenceStoreAccess = checkNotNull(val, "persister"); this.persistenceRealChangeListener = new PeriodicDeltaChangeListener( managementContext.getServerExecutionContext(), persistenceStoreAccess, exceptionHandler, persistMetrics, periodicPersistPeriod); this.persistencePublicChangeListener = new SafeChangeListener(persistenceRealChangeListener); if (persistenceRunning) { persistenceRealChangeListener.start(); } }
@Override @VisibleForTesting public void waitForPendingComplete(Duration timeout, boolean canTrigger) throws InterruptedException, TimeoutException { if (persistenceStoreAccess == null || !persistenceRunning) return; persistenceRealChangeListener.waitForPendingComplete(timeout, canTrigger); persistenceStoreAccess.waitForWritesCompleted(timeout); }
@Override public void stopPersistence() { LOG.debug( "Stopping persistence (" + this + "), mgmt " + managementContext.getManagementNodeId()); persistenceRunning = false; if (persistenceRealChangeListener != null) persistenceRealChangeListener.stop(); if (persistenceStoreAccess != null) persistenceStoreAccess.disableWriteAccess(true); LOG.debug("Stopped rebind (persistence), mgmt " + managementContext.getManagementNodeId()); }
@Override public void startPersistence() { if (readOnlyRunning) { throw new IllegalStateException( "Cannot start read-only when already running with persistence"); } LOG.debug( "Starting persistence (" + this + "), mgmt " + managementContext.getManagementNodeId()); if (!persistenceRunning) { if (managementContext .getBrooklynProperties() .getConfig(BrooklynServerConfig.PERSISTENCE_BACKUPS_REQUIRED_ON_PROMOTION)) { BrooklynPersistenceUtils.createBackup( managementContext, CreateBackupMode.PROMOTION, MementoCopyMode.REMOTE); } } persistenceRunning = true; readOnlyRebindCount.set(Integer.MIN_VALUE); persistenceStoreAccess.enableWriteAccess(); if (persistenceRealChangeListener != null) persistenceRealChangeListener.start(); }
@SuppressWarnings("unchecked") @Override public void startReadOnly(final ManagementNodeState mode) { if (!ManagementNodeState.isHotProxy(mode)) { throw new IllegalStateException( "Read-only rebind thread only permitted for hot proxy modes; not " + mode); } if (persistenceRunning) { throw new IllegalStateException( "Cannot start read-only when already running with persistence"); } if (readOnlyRunning || readOnlyTask != null) { LOG.warn( "Cannot request read-only mode for " + this + " when already running - " + readOnlyTask + "; ignoring"); return; } LOG.debug( "Starting read-only rebinding (" + this + "), mgmt " + managementContext.getManagementNodeId()); if (persistenceRealChangeListener != null) persistenceRealChangeListener.stop(); if (persistenceStoreAccess != null) persistenceStoreAccess.disableWriteAccess(true); readOnlyRunning = true; readOnlyRebindCount.set(0); try { rebind(null, null, mode); } catch (Exception e) { throw Exceptions.propagate(e); } Callable<Task<?>> taskFactory = new Callable<Task<?>>() { @Override public Task<Void> call() { return Tasks.<Void>builder() .dynamic(false) .displayName("rebind (periodic run") .body( new Callable<Void>() { public Void call() { try { rebind(null, null, mode); return null; } catch (RuntimeInterruptedException e) { LOG.debug("Interrupted rebinding (re-interrupting): " + e); if (LOG.isTraceEnabled()) LOG.trace("Interrupted rebinding (re-interrupting), details: " + e, e); Thread.currentThread().interrupt(); return null; } catch (Exception e) { // Don't rethrow: the behaviour of executionManager is different from a // scheduledExecutorService, // if we throw an exception, then our task will never get executed again if (!readOnlyRunning) { LOG.debug( "Problem rebinding (read-only running has probably just been turned off): " + e); if (LOG.isTraceEnabled()) { LOG.trace( "Problem rebinding (read-only running has probably just been turned off), details: " + e, e); } } else { LOG.error("Problem rebinding: " + Exceptions.collapseText(e), e); } return null; } catch (Throwable t) { LOG.warn("Problem rebinding (rethrowing)", t); throw Exceptions.propagate(t); } } }) .build(); } }; readOnlyTask = (ScheduledTask) managementContext .getServerExecutionContext() .submit( new ScheduledTask( MutableMap.of("displayName", "Periodic read-only rebind"), taskFactory) .period(periodicPersistPeriod)); }