|
@@ -12,6 +12,7 @@ import org.elasticsearch.test.ESTestCase;
|
|
|
import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
|
|
|
import org.elasticsearch.xpack.core.watcher.watch.ClockMock;
|
|
|
import org.elasticsearch.xpack.core.watcher.watch.Watch;
|
|
|
+import org.elasticsearch.xpack.core.watcher.watch.WatchStatus;
|
|
|
import org.elasticsearch.xpack.watcher.condition.InternalAlwaysCondition;
|
|
|
import org.elasticsearch.xpack.watcher.input.none.ExecutableNoneInput;
|
|
|
import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule;
|
|
@@ -283,6 +284,244 @@ public class TickerScheduleEngineTests extends ESTestCase {
|
|
|
assertThat(engine.getSchedules().get("_id"), not(is(activeSchedule)));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * This test verifies that a watch with a valid lastCheckedTime executes before the interval time to ensure the job resumes waiting
|
|
|
+ * from the same point it left off before the reallocation / restart
|
|
|
+ */
|
|
|
+ public void testWatchWithLastCheckedTimeExecutesBeforeInitialInterval() throws Exception {
|
|
|
+ final var firstLatch = new CountDownLatch(1);
|
|
|
+ final var secondLatch = new CountDownLatch(1);
|
|
|
+
|
|
|
+ Watch watch = new Watch(
|
|
|
+ "watch",
|
|
|
+ new ScheduleTrigger(interval("1s")),
|
|
|
+ new ExecutableNoneInput(),
|
|
|
+ InternalAlwaysCondition.INSTANCE,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ Collections.emptyList(),
|
|
|
+ null,
|
|
|
+ new WatchStatus(-1L, null, null, clock.instant().minusMillis(500).atZone(ZoneOffset.UTC), null, null, null),
|
|
|
+ SequenceNumbers.UNASSIGNED_SEQ_NO,
|
|
|
+ SequenceNumbers.UNASSIGNED_PRIMARY_TERM
|
|
|
+ );
|
|
|
+
|
|
|
+ var watches = Collections.singletonList(watch);
|
|
|
+
|
|
|
+ var runCount = new AtomicInteger(0);
|
|
|
+
|
|
|
+ engine.register(events -> {
|
|
|
+ for (TriggerEvent ignored : events) {
|
|
|
+ if (runCount.get() == 0) {
|
|
|
+ logger.info("job first fire");
|
|
|
+ firstLatch.countDown();
|
|
|
+ } else {
|
|
|
+ logger.info("job second fire");
|
|
|
+ secondLatch.countDown();
|
|
|
+ }
|
|
|
+ runCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ engine.start(watches);
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(510).atZone(ZoneOffset.UTC));
|
|
|
+ if (firstLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ if (secondLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ assertThat(runCount.get(), is(2));
|
|
|
+
|
|
|
+ engine.stop();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This test verifies that a watch without a lastCheckedTime but with a valid activationTime executes before the interval time to
|
|
|
+ * ensure the job resumes waiting from the same point it left off before the reallocation / restart
|
|
|
+ */
|
|
|
+ public void testWatchWithNoLastCheckedTimeButHasActivationTimeExecutesBeforeInitialInterval() throws Exception {
|
|
|
+ final var firstLatch = new CountDownLatch(1);
|
|
|
+ final var secondLatch = new CountDownLatch(1);
|
|
|
+
|
|
|
+ Watch watch = new Watch(
|
|
|
+ "watch",
|
|
|
+ new ScheduleTrigger(interval("1s")),
|
|
|
+ new ExecutableNoneInput(),
|
|
|
+ InternalAlwaysCondition.INSTANCE,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ Collections.emptyList(),
|
|
|
+ null,
|
|
|
+ new WatchStatus(
|
|
|
+ -1L,
|
|
|
+ new WatchStatus.State(true, clock.instant().minusMillis(500).atZone(ZoneOffset.UTC)),
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null
|
|
|
+ ),
|
|
|
+ SequenceNumbers.UNASSIGNED_SEQ_NO,
|
|
|
+ SequenceNumbers.UNASSIGNED_PRIMARY_TERM
|
|
|
+ );
|
|
|
+
|
|
|
+ var watches = Collections.singletonList(watch);
|
|
|
+
|
|
|
+ var runCount = new AtomicInteger(0);
|
|
|
+
|
|
|
+ engine.register(events -> {
|
|
|
+ for (TriggerEvent ignored : events) {
|
|
|
+ if (runCount.get() == 0) {
|
|
|
+ logger.info("job first fire");
|
|
|
+ firstLatch.countDown();
|
|
|
+ } else {
|
|
|
+ logger.info("job second fire");
|
|
|
+ secondLatch.countDown();
|
|
|
+ }
|
|
|
+ runCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ engine.start(watches);
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(510).atZone(ZoneOffset.UTC));
|
|
|
+ if (firstLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ if (secondLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ assertThat(runCount.get(), is(2));
|
|
|
+
|
|
|
+ engine.stop();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This test verifies that a watch added after service start with a lastCheckedTime executes before the interval time to ensure the job
|
|
|
+ * resumes waiting from the same point it left off before the reallocation / restart
|
|
|
+ */
|
|
|
+ public void testAddWithLastCheckedTimeExecutesBeforeInitialInterval() throws Exception {
|
|
|
+ final var firstLatch = new CountDownLatch(1);
|
|
|
+ final var secondLatch = new CountDownLatch(1);
|
|
|
+
|
|
|
+ Watch watch = new Watch(
|
|
|
+ "watch",
|
|
|
+ new ScheduleTrigger(interval("1s")),
|
|
|
+ new ExecutableNoneInput(),
|
|
|
+ InternalAlwaysCondition.INSTANCE,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ Collections.emptyList(),
|
|
|
+ null,
|
|
|
+ new WatchStatus(-1L, null, null, clock.instant().minusMillis(500).atZone(ZoneOffset.UTC), null, null, null),
|
|
|
+ SequenceNumbers.UNASSIGNED_SEQ_NO,
|
|
|
+ SequenceNumbers.UNASSIGNED_PRIMARY_TERM
|
|
|
+ );
|
|
|
+
|
|
|
+ var runCount = new AtomicInteger(0);
|
|
|
+
|
|
|
+ engine.register(events -> {
|
|
|
+ for (TriggerEvent ignored : events) {
|
|
|
+ if (runCount.get() == 0) {
|
|
|
+ logger.info("job first fire");
|
|
|
+ firstLatch.countDown();
|
|
|
+ } else {
|
|
|
+ logger.info("job second fire");
|
|
|
+ secondLatch.countDown();
|
|
|
+ }
|
|
|
+ runCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ engine.start(Collections.emptyList());
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ engine.add(watch);
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(510).atZone(ZoneOffset.UTC));
|
|
|
+ if (firstLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ if (secondLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ assertThat(runCount.get(), is(2));
|
|
|
+
|
|
|
+ engine.stop();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This test verifies that a watch added after service start without a lastCheckedTime but with a valid activationTime executes before
|
|
|
+ * the interval time to ensure the job resumes waiting from the same point it left off before the reallocation / restart
|
|
|
+ */
|
|
|
+ public void testAddWithNoLastCheckedTimeButHasActivationTimeExecutesBeforeInitialInterval() throws Exception {
|
|
|
+ final var firstLatch = new CountDownLatch(1);
|
|
|
+ final var secondLatch = new CountDownLatch(1);
|
|
|
+
|
|
|
+ Watch watch = new Watch(
|
|
|
+ "watch",
|
|
|
+ new ScheduleTrigger(interval("1s")),
|
|
|
+ new ExecutableNoneInput(),
|
|
|
+ InternalAlwaysCondition.INSTANCE,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ Collections.emptyList(),
|
|
|
+ null,
|
|
|
+ new WatchStatus(
|
|
|
+ -1L,
|
|
|
+ new WatchStatus.State(true, clock.instant().minusMillis(500).atZone(ZoneOffset.UTC)),
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null
|
|
|
+ ),
|
|
|
+ SequenceNumbers.UNASSIGNED_SEQ_NO,
|
|
|
+ SequenceNumbers.UNASSIGNED_PRIMARY_TERM
|
|
|
+ );
|
|
|
+
|
|
|
+ var runCount = new AtomicInteger(0);
|
|
|
+
|
|
|
+ engine.register(events -> {
|
|
|
+ for (TriggerEvent ignored : events) {
|
|
|
+ if (runCount.get() == 0) {
|
|
|
+ logger.info("job first fire");
|
|
|
+ firstLatch.countDown();
|
|
|
+ } else {
|
|
|
+ logger.info("job second fire");
|
|
|
+ secondLatch.countDown();
|
|
|
+ }
|
|
|
+ runCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ engine.start(Collections.emptyList());
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ engine.add(watch);
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(510).atZone(ZoneOffset.UTC));
|
|
|
+ if (firstLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ advanceClockIfNeeded(clock.instant().plusMillis(1100).atZone(ZoneOffset.UTC));
|
|
|
+ if (secondLatch.await(3, TimeUnit.SECONDS) == false) {
|
|
|
+ fail("waiting too long for all watches to be triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ assertThat(runCount.get(), is(2));
|
|
|
+
|
|
|
+ engine.stop();
|
|
|
+ }
|
|
|
+
|
|
|
private Watch createWatch(String name, Schedule schedule) {
|
|
|
return new Watch(
|
|
|
name,
|