Search in sources :

Example 1 with JobTriggerUpdate

use of org.graylog.scheduler.JobTriggerUpdate in project graylog2-server by Graylog2.

the class EventProcessorExecutionJobTest method catchupWindowTestHelper.

private void catchupWindowTestHelper(long catchUpWindowSize, long processingHopSize, long processingWindowSize) throws Exception {
    when(eventsConfigurationProvider.get()).thenReturn(EventsConfiguration.builder().eventCatchupWindow(catchUpWindowSize).build());
    // for easier testing. don't run into the previous day
    clock.plus(1, TimeUnit.MINUTES);
    final DateTime now = clock.nowUTC();
    final long processingCatchUpWindowSize = eventsConfigurationProvider.get().eventCatchupWindow();
    final int scheduleIntervalSeconds = (int) processingHopSize * 1000;
    final DateTime from = now.minus(processingWindowSize);
    final DateTime to = now;
    final DateTime triggerNextTime = now;
    final Duration timeSpentInEventProcessor = Duration.standardSeconds(7);
    final TestEventProcessorParameters eventProcessorParameters = TestEventProcessorParameters.create(from, to);
    final JobDefinitionDto jobDefinition = JobDefinitionDto.builder().id("job-1").title("Test").description("A test").config(EventProcessorExecutionJob.Config.builder().eventDefinitionId("processor-1").processingWindowSize(processingWindowSize).processingHopSize(processingHopSize).parameters(eventProcessorParameters).build()).build();
    final EventProcessorExecutionJob job = new EventProcessorExecutionJob(jobScheduleStrategies, clock, eventProcessorEngine, eventsConfigurationProvider, jobDefinition);
    final JobTriggerDto trigger = JobTriggerDto.builderWithClock(clock).id("trigger-1").jobDefinitionId(jobDefinition.id()).startTime(now).nextTime(triggerNextTime).status(JobTriggerStatus.RUNNABLE).schedule(IntervalJobSchedule.builder().interval(scheduleIntervalSeconds).unit(TimeUnit.SECONDS).build()).build();
    final JobExecutionContext jobExecutionContext = JobExecutionContext.builder().definition(jobDefinition).trigger(trigger).isRunning(new AtomicBoolean(true)).jobTriggerUpdates(new JobTriggerUpdates(clock, jobScheduleStrategies, trigger)).build();
    doAnswer(invocation -> {
        // Simulate work in the event processor
        clock.plus(timeSpentInEventProcessor.getStandardSeconds(), TimeUnit.SECONDS);
        return null;
    }).when(eventProcessorEngine).execute(any(), any());
    // Simulate that we are behind at least one `processingCatchUpWindowSize`
    clock.plus(EventsConfiguration.DEFAULT_CATCH_UP_WINDOW_MS, TimeUnit.MILLISECONDS);
    clock.plus(1, TimeUnit.MILLISECONDS);
    final JobTriggerUpdate triggerUpdate = job.execute(jobExecutionContext);
    verify(eventProcessorEngine, times(1)).execute("processor-1", eventProcessorParameters);
    assertThat(triggerUpdate.nextTime()).isPresent().get().isEqualTo(clock.nowUTC().minus(timeSpentInEventProcessor));
    if (catchUpWindowSize > processingWindowSize && processingHopSize <= processingWindowSize) {
        // We are behind at least one chunk of catchUpWindowSize
        // The new nextFrom should ignore the processingHopSize and start 1ms after the last `To` Range
        // The nextTo will be one window of the processingCatchUpWindowSize
        assertThat(triggerUpdate.data()).isPresent().get().isEqualTo(EventProcessorExecutionJob.Data.builder().timerangeFrom(to.plus(processingHopSize).minus(processingWindowSize)).timerangeTo(to.plus(processingCatchUpWindowSize)).build());
    } else {
        // If no catchup is in effect, we fall back into the configured hopping window mode.
        // With a hopping window the "to" is calculated by adding the hopSize and the "from" is based on the next "to"
        // minus the windowSize + 1 millisecond.
        assertThat(triggerUpdate.data()).isPresent().get().isEqualTo(EventProcessorExecutionJob.Data.builder().timerangeFrom(to.plus(processingHopSize).minus(processingWindowSize)).timerangeTo(to.plus(processingHopSize)).build());
    }
    assertThat(triggerUpdate.status()).isNotPresent();
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) JobDefinitionDto(org.graylog.scheduler.JobDefinitionDto) JobTriggerUpdates(org.graylog.scheduler.JobTriggerUpdates) JobTriggerUpdate(org.graylog.scheduler.JobTriggerUpdate) JobExecutionContext(org.graylog.scheduler.JobExecutionContext) Duration(org.joda.time.Duration) DateTime(org.joda.time.DateTime) TestEventProcessorParameters(org.graylog.events.TestEventProcessorParameters) EventProcessorExecutionJob(org.graylog.events.processor.EventProcessorExecutionJob) JobTriggerDto(org.graylog.scheduler.JobTriggerDto)

Example 2 with JobTriggerUpdate

use of org.graylog.scheduler.JobTriggerUpdate in project graylog2-server by Graylog2.

the class EventProcessorExecutionJobTest method executeWithNextTimeNotBasedOnCurrentTime.

@Test
public void executeWithNextTimeNotBasedOnCurrentTime() throws Exception {
    final DateTime now = clock.nowUTC();
    final long processingWindowSize = Duration.standardSeconds(60).getMillis();
    final long processingHopSize = Duration.standardSeconds(60).getMillis();
    final int scheduleIntervalSeconds = 1;
    final DateTime from = now.minus(processingWindowSize);
    final DateTime to = now;
    final DateTime triggerNextTime = now;
    final TestEventProcessorParameters eventProcessorParameters = TestEventProcessorParameters.create(from, to);
    final JobDefinitionDto jobDefinition = JobDefinitionDto.builder().id("job-1").title("Test").description("A test").config(EventProcessorExecutionJob.Config.builder().eventDefinitionId("processor-1").processingWindowSize(processingWindowSize).processingHopSize(processingHopSize).parameters(eventProcessorParameters).build()).build();
    final EventProcessorExecutionJob job = new EventProcessorExecutionJob(jobScheduleStrategies, clock, eventProcessorEngine, eventsConfigurationProvider, jobDefinition);
    final JobTriggerDto trigger = JobTriggerDto.builderWithClock(clock).id("trigger-1").jobDefinitionId(jobDefinition.id()).startTime(now).nextTime(triggerNextTime).status(JobTriggerStatus.RUNNABLE).schedule(IntervalJobSchedule.builder().interval(scheduleIntervalSeconds).unit(TimeUnit.SECONDS).build()).build();
    final JobExecutionContext jobExecutionContext = JobExecutionContext.builder().definition(jobDefinition).trigger(trigger).isRunning(new AtomicBoolean(true)).jobTriggerUpdates(new JobTriggerUpdates(clock, jobScheduleStrategies, trigger)).build();
    doAnswer(invocation -> {
        // Simulate work in the event processor
        clock.plus(10, TimeUnit.SECONDS);
        return null;
    }).when(eventProcessorEngine).execute(any(), any());
    final JobTriggerUpdate triggerUpdate = job.execute(jobExecutionContext);
    verify(eventProcessorEngine, times(1)).execute("processor-1", eventProcessorParameters);
    // The next time should be based on the previous nextTime + the schedule. The 10 second event processor
    // runtime should not be added to the new nextTime.
    assertThat(triggerUpdate.nextTime()).isPresent().get().isEqualTo(triggerNextTime.plusSeconds(scheduleIntervalSeconds));
    assertThat(triggerUpdate.data()).isPresent().get().isEqualTo(EventProcessorExecutionJob.Data.builder().timerangeFrom(to).timerangeTo(to.plus(processingWindowSize)).build());
    assertThat(triggerUpdate.status()).isNotPresent();
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) JobDefinitionDto(org.graylog.scheduler.JobDefinitionDto) JobTriggerUpdates(org.graylog.scheduler.JobTriggerUpdates) JobTriggerUpdate(org.graylog.scheduler.JobTriggerUpdate) JobExecutionContext(org.graylog.scheduler.JobExecutionContext) DateTime(org.joda.time.DateTime) TestEventProcessorParameters(org.graylog.events.TestEventProcessorParameters) EventProcessorExecutionJob(org.graylog.events.processor.EventProcessorExecutionJob) JobTriggerDto(org.graylog.scheduler.JobTriggerDto) Test(org.junit.Test)

Example 3 with JobTriggerUpdate

use of org.graylog.scheduler.JobTriggerUpdate in project graylog2-server by Graylog2.

the class EventProcessorExecutionJobTest method executeWithTriggerDataTimerange.

@Test
public void executeWithTriggerDataTimerange() throws Exception {
    final DateTime now = clock.nowUTC();
    final long processingWindowSize = Duration.standardSeconds(60).getMillis();
    final long processingHopSize = Duration.standardSeconds(60).getMillis();
    final int scheduleIntervalSeconds = 1;
    final DateTime from = now.minusDays(10).minus(processingWindowSize);
    final DateTime to = now.minusDays(10);
    final DateTime triggerFrom = now.minus(processingWindowSize);
    final DateTime triggerTo = now;
    final DateTime triggerNextTime = now;
    final TestEventProcessorParameters eventProcessorParameters = TestEventProcessorParameters.create(from, to);
    final JobDefinitionDto jobDefinition = JobDefinitionDto.builder().id("job-1").title("Test").description("A test").config(EventProcessorExecutionJob.Config.builder().eventDefinitionId("processor-1").processingWindowSize(processingWindowSize).processingHopSize(processingHopSize).parameters(eventProcessorParameters).build()).build();
    final EventProcessorExecutionJob job = new EventProcessorExecutionJob(jobScheduleStrategies, clock, eventProcessorEngine, eventsConfigurationProvider, jobDefinition);
    final JobTriggerDto trigger = JobTriggerDto.builderWithClock(clock).id("trigger-1").jobDefinitionId(jobDefinition.id()).startTime(now).nextTime(triggerNextTime).status(JobTriggerStatus.RUNNABLE).schedule(IntervalJobSchedule.builder().interval(scheduleIntervalSeconds).unit(TimeUnit.SECONDS).build()).data(EventProcessorExecutionJob.Data.create(triggerFrom, triggerTo)).build();
    final JobExecutionContext jobExecutionContext = JobExecutionContext.builder().definition(jobDefinition).trigger(trigger).isRunning(new AtomicBoolean(true)).jobTriggerUpdates(new JobTriggerUpdates(clock, jobScheduleStrategies, trigger)).build();
    doAnswer(invocation -> {
        // Simulate work in the event processor
        clock.plus(5, TimeUnit.SECONDS);
        return null;
    }).when(eventProcessorEngine).execute(any(), any());
    final JobTriggerUpdate triggerUpdate = job.execute(jobExecutionContext);
    // Check that we use the timerange from the trigger instead of the parameters
    verify(eventProcessorEngine, times(1)).execute("processor-1", eventProcessorParameters.withTimerange(triggerFrom, triggerTo));
    assertThat(triggerUpdate.nextTime()).isPresent().get().isEqualTo(triggerNextTime.plusSeconds(scheduleIntervalSeconds));
    // The next timerange in the trigger update also needs to be based on the timerange from the trigger
    assertThat(triggerUpdate.data()).isPresent().get().isEqualTo(EventProcessorExecutionJob.Data.builder().timerangeFrom(triggerTo).timerangeTo(triggerTo.plus(processingWindowSize)).build());
    assertThat(triggerUpdate.status()).isNotPresent();
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) JobDefinitionDto(org.graylog.scheduler.JobDefinitionDto) JobTriggerUpdates(org.graylog.scheduler.JobTriggerUpdates) JobTriggerUpdate(org.graylog.scheduler.JobTriggerUpdate) JobExecutionContext(org.graylog.scheduler.JobExecutionContext) DateTime(org.joda.time.DateTime) TestEventProcessorParameters(org.graylog.events.TestEventProcessorParameters) EventProcessorExecutionJob(org.graylog.events.processor.EventProcessorExecutionJob) JobTriggerDto(org.graylog.scheduler.JobTriggerDto) Test(org.junit.Test)

Example 4 with JobTriggerUpdate

use of org.graylog.scheduler.JobTriggerUpdate in project graylog2-server by Graylog2.

the class EventProcessorExecutionJob method execute.

@Override
public JobTriggerUpdate execute(JobExecutionContext ctx) throws JobExecutionException {
    final Optional<Data> data = ctx.trigger().data().map(d -> (Data) d);
    // Use timerange from job trigger data if it exists
    final EventProcessorParametersWithTimerange parameters;
    if (data.isPresent()) {
        LOG.trace("Using timerange from job trigger data: from={} to={} (trigger={})", data.get().timerangeFrom(), data.get().timerangeTo(), ctx.trigger().id());
        parameters = config.parameters().withTimerange(data.get().timerangeFrom(), data.get().timerangeTo());
    } else {
        parameters = config.parameters();
    }
    final DateTime from = parameters.timerange().getFrom();
    final DateTime to = parameters.timerange().getTo();
    // The "to" timestamp must be after the "from" timestamp!
    if (!to.isAfter(from)) {
        // This should not happen(TM)
        // If it does, set the error status to ERROR so the scheduler doesn't try to execute it until the problem
        // has been resolved.
        // TODO: Send an event when this happens so admins can get alerted
        final JobTriggerUpdate triggerUpdate = JobTriggerUpdate.withError(ctx.trigger());
        throw new JobExecutionException("Invalid time range - \"to\" timestamp <" + to.toString() + "> is not after \"from\" timestamp <" + from.toString() + ">", ctx.trigger(), triggerUpdate);
    }
    // We cannot run the event processor if the "to" timestamp of the timerange we want to process is in the future.
    final DateTime now = clock.nowUTC();
    if (now.isBefore(to)) {
        LOG.error("The end of the timerange to process is in the future, re-scheduling job trigger <{}> to run at <{}>", ctx.trigger().id(), to);
        return JobTriggerUpdate.withNextTime(to);
    }
    try {
        eventProcessorEngine.execute(config.eventDefinitionId(), parameters);
        // By using the processingWindowSize and the processingHopSize we can implement hopping and tumbling
        // windows. (a tumbling window is simply a hopping window where windowSize and hopSize are the same)
        DateTime nextTo = to.plus(config.processingHopSize());
        DateTime nextFrom = nextTo.minus(config.processingWindowSize());
        // If the event processor is catching up on old data (e.g. the server was shut down for a significant time),
        // we can switch to a bigger scheduling window: `processingCatchUpWindowSize`.
        // If engaged, we will schedule jobs with a timerange of multiple processingWindowSize chunks.
        // It's the specific event processors' duty to handle being executed with this larger timerange.
        // If an event processor was configured with a processingHopSize greater than the processingWindowSize
        // we can't use the catchup mode.
        final long catchUpSize = configurationProvider.get().eventCatchupWindow();
        if (catchUpSize > 0 && catchUpSize > config.processingWindowSize() && to.plus(catchUpSize).isBefore(now) && config.processingHopSize() <= config.processingWindowSize()) {
            final long chunkCount = catchUpSize / config.processingWindowSize();
            // Align to multiples of the processingWindowSize
            nextTo = to.plus(config.processingWindowSize() * chunkCount);
            LOG.info("Event processor <{}> is catching up on old data. Combining {} search windows with catchUpWindowSize={}ms: from={} to={}", config.eventDefinitionId(), chunkCount, catchUpSize, nextFrom, nextTo);
        }
        LOG.trace("Set new timerange of eventproc <{}> in job trigger data: from={} to={} (hopSize={}ms windowSize={}ms)", config.eventDefinitionId(), nextFrom, nextTo, config.processingHopSize(), config.processingWindowSize());
        final Data newData = data.map(Data::toBuilder).orElse(Data.builder()).timerangeFrom(nextFrom).timerangeTo(nextTo).build();
        final Optional<DateTime> nextTime = scheduleStrategies.nextTime(ctx.trigger());
        // The nextTime Optional can be empty if there will be no further executions of the trigger
        if (nextTime.isPresent()) {
            if (nextTo.isBefore(now)) {
                // If the next "to" timestamp of the timerange to process is in the past, we want to schedule the next
                // execution of this job as soon as possible to make sure we catch up.
                LOG.trace("Set nextTime to <{}> to catch up faster - calculated nextTime was <{}>", now, nextTime.get());
                return JobTriggerUpdate.withNextTimeAndData(now, newData);
            } else if (nextTo.isBefore(nextTime.get())) {
                LOG.trace("Set nextTime to <{}> because it's closer to the timerange time - calculated nextTime was <{}>", nextTo, nextTime.get());
                return JobTriggerUpdate.withNextTimeAndData(nextTo, newData);
            } else {
                // Otherwise use the calculated nextTime
                LOG.trace("Set nextTime to <{}>", nextTime.get());
                return JobTriggerUpdate.withNextTimeAndData(nextTime.get(), newData);
            }
        } else {
            // Or no next time if this has been a ONCE trigger
            LOG.trace("No nextTime for trigger <{}>", ctx.trigger().id());
            return JobTriggerUpdate.withoutNextTime();
        }
    } catch (EventProcessorPreconditionException e) {
        // A precondition for the event processor is not ready yet. This job must be retried.
        if (e.getEventDefinition().isPresent()) {
            LOG.debug("Event processor <{}/{}> couldn't be executed because of a failed precondition (retry in {} ms)", e.getEventDefinition().get().title(), e.getEventDefinitionId(), RETRY_INTERVAL);
        } else {
            LOG.debug("Event processor <{}> couldn't be executed because of a failed precondition (retry in {} ms)", e.getEventDefinitionId(), RETRY_INTERVAL);
        }
        return ctx.jobTriggerUpdates().retryIn(RETRY_INTERVAL, MILLISECONDS);
    } catch (EventProcessorException e) {
        if (e.getEventDefinition().isPresent()) {
            LOG.error("Event processor <{}/{}> failed to execute: {} (retry in {} ms)", e.getEventDefinition().get().config().type(), e.getEventDefinitionId(), e.getMessage(), RETRY_INTERVAL, e);
        } else {
            LOG.error("Event processor <{}> failed to execute: {} (retry in {} ms)", e.getEventDefinitionId(), e.getMessage(), RETRY_INTERVAL, e);
        }
        if (e.isPermanent()) {
            // We cannot retry a permanent error so we have to set the job trigger status to ERROR so it doesn't
            // get executed again
            LOG.error("Caught a permanent error, trigger <{}> will go into ERROR state - it will not be executed anymore and needs manual intervention! (event-definition-id: {} job-definition={}/{})", ctx.trigger().id(), e.getEventDefinitionId(), ctx.definition().id(), ctx.definition().title());
            return JobTriggerUpdate.withError(ctx.trigger());
        }
        return ctx.jobTriggerUpdates().retryIn(RETRY_INTERVAL, MILLISECONDS);
    } catch (Exception e) {
        LOG.error("Event processor <{}> failed to execute: parameters={} (retry in {} ms)", config.eventDefinitionId(), parameters, RETRY_INTERVAL, e);
        return ctx.jobTriggerUpdates().retryIn(RETRY_INTERVAL, MILLISECONDS);
    }
}
Also used : JobExecutionException(org.graylog.scheduler.JobExecutionException) JobTriggerUpdate(org.graylog.scheduler.JobTriggerUpdate) JobTriggerData(org.graylog.scheduler.JobTriggerData) DateTime(org.joda.time.DateTime) JobExecutionException(org.graylog.scheduler.JobExecutionException)

Example 5 with JobTriggerUpdate

use of org.graylog.scheduler.JobTriggerUpdate in project graylog2-server by Graylog2.

the class EventProcessorExecutionJobTest method executeWithTimerangeInTheFuture.

@Test
public void executeWithTimerangeInTheFuture() throws Exception {
    final DateTime now = clock.nowUTC();
    final long processingWindowSize = Duration.standardSeconds(60).getMillis();
    final long processingHopSize = Duration.standardSeconds(60).getMillis();
    final int scheduleIntervalSeconds = 1;
    final DateTime from = now;
    // Set the "to" timestamp of the timerange way into the future
    final DateTime to = now.plusDays(1);
    final DateTime triggerNextTime = now;
    final TestEventProcessorParameters eventProcessorParameters = TestEventProcessorParameters.create(from, to);
    final JobDefinitionDto jobDefinition = JobDefinitionDto.builder().id("job-1").title("Test").description("A test").config(EventProcessorExecutionJob.Config.builder().eventDefinitionId("processor-1").processingWindowSize(processingWindowSize).processingHopSize(processingHopSize).parameters(eventProcessorParameters).build()).build();
    final EventProcessorExecutionJob job = new EventProcessorExecutionJob(jobScheduleStrategies, clock, eventProcessorEngine, eventsConfigurationProvider, jobDefinition);
    final JobTriggerDto trigger = JobTriggerDto.builderWithClock(clock).id("trigger-1").jobDefinitionId(jobDefinition.id()).startTime(now).nextTime(triggerNextTime).status(JobTriggerStatus.RUNNABLE).schedule(IntervalJobSchedule.builder().interval(scheduleIntervalSeconds).unit(TimeUnit.SECONDS).build()).build();
    final JobExecutionContext jobExecutionContext = JobExecutionContext.builder().definition(jobDefinition).trigger(trigger).isRunning(new AtomicBoolean(true)).jobTriggerUpdates(new JobTriggerUpdates(clock, jobScheduleStrategies, trigger)).build();
    final JobTriggerUpdate triggerUpdate = job.execute(jobExecutionContext);
    // The engine should not be called because the "to" timestamp of the timerange is in the future
    verify(eventProcessorEngine, never()).execute(any(), any());
    // The update sets the nextTime to the "to" timestamp because that is the earliest time we can execute
    // the job for the timerange
    assertThat(triggerUpdate.nextTime()).isPresent().get().isEqualTo(to);
    // Data should not be updated with any new timerange
    assertThat(triggerUpdate.data()).isNotPresent();
    assertThat(triggerUpdate.status()).isNotPresent();
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) JobDefinitionDto(org.graylog.scheduler.JobDefinitionDto) JobTriggerUpdates(org.graylog.scheduler.JobTriggerUpdates) JobTriggerUpdate(org.graylog.scheduler.JobTriggerUpdate) JobExecutionContext(org.graylog.scheduler.JobExecutionContext) DateTime(org.joda.time.DateTime) TestEventProcessorParameters(org.graylog.events.TestEventProcessorParameters) EventProcessorExecutionJob(org.graylog.events.processor.EventProcessorExecutionJob) JobTriggerDto(org.graylog.scheduler.JobTriggerDto) Test(org.junit.Test)

Aggregations

JobTriggerUpdate (org.graylog.scheduler.JobTriggerUpdate)8 DateTime (org.joda.time.DateTime)8 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)7 TestEventProcessorParameters (org.graylog.events.TestEventProcessorParameters)7 EventProcessorExecutionJob (org.graylog.events.processor.EventProcessorExecutionJob)7 JobDefinitionDto (org.graylog.scheduler.JobDefinitionDto)7 JobExecutionContext (org.graylog.scheduler.JobExecutionContext)7 JobTriggerDto (org.graylog.scheduler.JobTriggerDto)7 JobTriggerUpdates (org.graylog.scheduler.JobTriggerUpdates)7 Test (org.junit.Test)6 JobExecutionException (org.graylog.scheduler.JobExecutionException)1 JobTriggerData (org.graylog.scheduler.JobTriggerData)1 Duration (org.joda.time.Duration)1