@Override public void schedule(final ReportingTaskNode taskNode, final ScheduleState scheduleState) { final Runnable reportingTaskWrapper = new ReportingTaskWrapper(taskNode, scheduleState); final long schedulingNanos = taskNode.getSchedulingPeriod(TimeUnit.NANOSECONDS); final ScheduledFuture<?> future = flowEngine.scheduleWithFixedDelay( reportingTaskWrapper, 0L, schedulingNanos, TimeUnit.NANOSECONDS); final List<ScheduledFuture<?>> futures = new ArrayList<>(1); futures.add(future); scheduleState.setFutures(futures); logger.info("{} started.", taskNode.getReportingTask()); }
@Override public final synchronized void start() { if (!stopped) { throw new IllegalStateException( "Heartbeat Monitor cannot be started because it is already started"); } stopped = false; logger.info("Heartbeat Monitor started"); try { onStart(); } catch (final Exception e) { logger.error("Failed to start Heartbeat Monitor", e); } this.future = flowEngine.scheduleWithFixedDelay( new Runnable() { @Override public void run() { try { monitorHeartbeats(); } catch (final Exception e) { clusterCoordinator.reportEvent( null, Severity.ERROR, "Failed to process heartbeats from nodes due to " + e.toString()); logger.error("Failed to process heartbeats", e); } } }, heartbeatIntervalMillis, heartbeatIntervalMillis, TimeUnit.MILLISECONDS); }
@Override public void shutdown() { flowEngine.shutdown(); }
@Override public void schedule(final Connectable connectable, final ScheduleState scheduleState) { final List<ScheduledFuture<?>> futures = new ArrayList<>(); for (int i = 0; i < connectable.getMaxConcurrentTasks(); i++) { final Callable<Boolean> continuallyRunTask; final ProcessContext processContext; // Determine the task to run and create it. if (connectable.getConnectableType() == ConnectableType.PROCESSOR) { final ProcessorNode procNode = (ProcessorNode) connectable; final StandardProcessContext standardProcContext = new StandardProcessContext( procNode, flowController, encryptor, getStateManager(connectable.getIdentifier())); final ContinuallyRunProcessorTask runnableTask = new ContinuallyRunProcessorTask( this, procNode, flowController, contextFactory, scheduleState, standardProcContext); continuallyRunTask = runnableTask; processContext = standardProcContext; } else { processContext = new ConnectableProcessContext( connectable, encryptor, getStateManager(connectable.getIdentifier())); continuallyRunTask = new ContinuallyRunConnectableTask( contextFactory, connectable, scheduleState, processContext); } final AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>(); final Runnable yieldDetectionRunnable = new Runnable() { @Override public void run() { // Call the continually run task. It will return a boolean indicating whether or not // we should yield // based on a lack of work for to do for the component. final boolean shouldYield; try { shouldYield = continuallyRunTask.call(); } catch (final RuntimeException re) { throw re; } catch (final Exception e) { throw new ProcessException(e); } // If the component is yielded, cancel its future and re-submit it to run again // after the yield has expired. final long newYieldExpiration = connectable.getYieldExpiration(); if (newYieldExpiration > System.currentTimeMillis()) { final long yieldMillis = newYieldExpiration - System.currentTimeMillis(); final ScheduledFuture<?> scheduledFuture = futureRef.get(); if (scheduledFuture == null) { return; } // If we are able to cancel the future, create a new one and update the // ScheduleState so that it has // an accurate accounting of which futures are outstanding; we must then also update // the futureRef // so that we can do this again the next time that the component is yielded. if (scheduledFuture.cancel(false)) { final long yieldNanos = TimeUnit.MILLISECONDS.toNanos(yieldMillis); synchronized (scheduleState) { if (scheduleState.isScheduled()) { final ScheduledFuture<?> newFuture = flowEngine.scheduleWithFixedDelay( this, yieldNanos, connectable.getSchedulingPeriod(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS); scheduleState.replaceFuture(scheduledFuture, newFuture); futureRef.set(newFuture); } } } } else if (noWorkYieldNanos > 0L && shouldYield) { // Component itself didn't yield but there was no work to do, so the framework will // choose // to yield the component automatically for a short period of time. final ScheduledFuture<?> scheduledFuture = futureRef.get(); if (scheduledFuture == null) { return; } // If we are able to cancel the future, create a new one and update the // ScheduleState so that it has // an accurate accounting of which futures are outstanding; we must then also update // the futureRef // so that we can do this again the next time that the component is yielded. if (scheduledFuture.cancel(false)) { synchronized (scheduleState) { if (scheduleState.isScheduled()) { final ScheduledFuture<?> newFuture = flowEngine.scheduleWithFixedDelay( this, noWorkYieldNanos, connectable.getSchedulingPeriod(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS); scheduleState.replaceFuture(scheduledFuture, newFuture); futureRef.set(newFuture); } } } } } }; // Schedule the task to run final ScheduledFuture<?> future = flowEngine.scheduleWithFixedDelay( yieldDetectionRunnable, 0L, connectable.getSchedulingPeriod(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS); // now that we have the future, set the atomic reference so that if the component is yielded // we // are able to then cancel this future. futureRef.set(future); // Keep track of the futures so that we can update the ScheduleState. futures.add(future); } scheduleState.setFutures(futures); logger.info( "Scheduled {} to run with {} threads", connectable, connectable.getMaxConcurrentTasks()); }