Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -750,43 +750,6 @@ public boolean shouldSkipMonitoredTripCheck() throws Exception {
}

public boolean shouldSkipMonitoredTripCheck(boolean persist) throws Exception {
// before anything else, return true if the trip is inactive
if (!trip.isActive) {
LOG.info("Skipping: Trip is inactive.");
return true;
}

// If trip is no longer possible, no further checking is needed. The itinerary existence data should not be
// checked here to avoid incorrectly skipping trips that are monitored on a single day of the week, but which
// may have not had a matching itinerary on that day for one week (even though the trip could be possible the
// next week).
if (previousJourneyState.tripStatus == TripStatus.NO_LONGER_POSSIBLE) {
LOG.info("Skipping: Trip is no longer possible.");
return true;
}

// If trip is one-time and has ended, no further checking is needed. The itinerary existence data should not be
// checked here to avoid incorrectly skipping trips that are monitored on a single day of the week, but which
// may have not had a matching itinerary on that day for one week (even though the trip could be possible the
// next week).
if (isOneTimeTripInPast()) {
LOG.info("Skipping: One-time trip is in the past.");
return true;
}

// For trips that are snoozed, see if they should be unsnoozed first.
if (trip.snoozed) {
if (shouldUnsnoozeTrip()) {
// Clear previous matching itinerary as we want to start afresh.
previousMatchingItinerary = null;
// unsnooze trip now, for cases where the next itinerary isn't calculated
trip.snoozed = false;
} else {
LOG.info("Skipping: Trip is snoozed.");
return true;
}
}

// initialize the trip's journey state and matching itinerary to the latest journeyState's matching
// itinerary, or use the itinerary that the trip was saved with
if (previousMatchingItinerary == null) {
Expand Down Expand Up @@ -858,6 +821,63 @@ public boolean shouldSkipMonitoredTripCheck(boolean persist) throws Exception {
return true;
}

/**
* Convenience method for unit tests to do the precheck before the full check
*/
protected boolean shouldSkipMonitoredTripCheck(boolean persist, boolean preCheck) throws Exception {
if (preCheck && CheckMonitoredTrip.shouldSkipMonitoredTripPreCheck(trip)) {
return true;
}
return shouldSkipMonitoredTripCheck(persist);
}

/**
* A series of trivial checks a {@pre MonitoredTrip} object, just fields obtained from the database, to
* determine whether to instantiate a {@pre CheckMonitoredTrip} object to do further checking.
* @param trip candidate monitored trip to quickly check some fields of
* @return whether to skip the trip, boolean value
*/
protected static boolean shouldSkipMonitoredTripPreCheck(MonitoredTrip trip) {
// before anything else, return true if the trip is inactive
if (!trip.isActive) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because no action is performed on trips that match at least the criteria in these methods, I think the criteria should be added to the Mongo filter in MonitorAllTripsJob.makeTripFilter (where some checks here are redundant with those filters). What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JymDyerIBI (not blocking) Any thoughts on my comment above?

LOG.info("Skipping: Trip {} is inactive.", trip.id);
return true;
}

// If trip is no longer possible, no further checking is needed. The itinerary existence data should not be
// checked here to avoid incorrectly skipping trips that are monitored on a single day of the week, but which
// may have not had a matching itinerary on that day for one week (even though the trip could be possible the
// next week).
if (trip.journeyState.tripStatus == TripStatus.NO_LONGER_POSSIBLE) {
LOG.info("Skipping: Trip {} is no longer possible.", trip.id);
return true;
}

// If trip is a one-off trip which has already happened, no further checking is needed. The itinerary existence
// data should not be checked here to avoid incorrectly skipping trips that are monitored on a single day of
// the week, but which may have not had a matching itinerary on that day for one week (even though the trip
// could be possible the next week).
if (trip.isOneTime() && trip.journeyState.tripStatus == TripStatus.PAST_TRIP) {
LOG.info("Skipping: One-time trip {} is in the past.", trip.id);
return true;
}

// For trips that are snoozed, see if they should be unsnoozed first.
if (trip.snoozed) {
if (shouldUnsnoozeTrip(trip.journeyState.lastCheckedEpochMillis)) {
// Clear previous matching itinerary as we want to start afresh.
trip.journeyState.matchingItinerary = null;
// unsnooze trip now, for cases where the next itinerary isn't calculated
trip.snoozed = false;
} else {
LOG.info("Skipping: Trip {} is snoozed.", trip.id);
return true;
}
}

return false;
}

/**
* Calculate target time for the next trip plan request. Find the next possible day the trip is active by
* initializing the appropriate target time.
Expand Down Expand Up @@ -921,13 +941,6 @@ private static ZonedDateTime findNextMonitoredDay(MonitoredTrip trip, ZonedDateT
return nextMonitoredDay;
}

/**
* Is a one-off trip which has already happened.
*/
private boolean isOneTimeTripInPast() {
return trip.isOneTime() && previousJourneyState.tripStatus == TripStatus.PAST_TRIP;
}

/** Check if previous matching itinerary day is still valid */
private boolean isPrevMatchingItineraryDayValid() {
if (previousMatchingItinerary == null) return false;
Expand Down Expand Up @@ -1070,14 +1083,15 @@ private Locale getOtpUserLocale() {

/**
* Whether a trip should be unsnoozed and monitoring should resume.
* @param lastCheckedEpochMillis to acquire the trip's start day
* @return true if the current time is after the calendar day (on or after midnight)
* after the matching trip start day, false otherwise.
*/
public boolean shouldUnsnoozeTrip() {
public static boolean shouldUnsnoozeTrip(long lastCheckedEpochMillis) {
ZoneId otpZoneId = DateTimeUtils.getOtpZoneId();
var midnightAfterLastChecked = ZonedDateTime
.ofInstant(
Instant.ofEpochMilli(previousJourneyState.lastCheckedEpochMillis).plus(1, ChronoUnit.DAYS),
Instant.ofEpochMilli(lastCheckedEpochMillis).plus(1, ChronoUnit.DAYS),
otpZoneId
)
.withHour(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ public void run() {
continue;
}

// Do a few trivial prechecks to see whether to even bother locking the trip
// and instantiating a CheckMonitoredTrip run.
if (CheckMonitoredTrip.shouldSkipMonitoredTripPreCheck(trip)) {
analyzerIsIdle.set(true);
continue;
}

Comment on lines +75 to +81
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this block and move the call to shouldSkipMonitoredTripPreCheck, either back inside shouldSkipMonitoredTripCheck, or in doRun right before the call to shouldSkipMonitoredTripCheck.

This change as written causes an error when updating a trip from the UI, for instance to pause notifications or modify a paused trip. When a trip is updated (PUT request), new CheckMonitoredTrip(trip).run() gets run. Because the skipping logic is no longer run, it will try to compute the next active date of an inactive trip (and will never find one).

LOG.info("Analyzing trip {}", tripId);

// place lock on trip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void testSkipMonitoredTripCheck(SkipMonitoringTestArgs args) throws Exception {
}
assertEquals(
args.expectedResult,
new CheckMonitoredTrip(trip).shouldSkipMonitoredTripCheck(false),
new CheckMonitoredTrip(trip).shouldSkipMonitoredTripCheck(false, true),
args.message
);
}
Expand Down
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some code broke in CI with the error "CheckMonitoredTripTest.java:[1164,43] method shouldUnsnoozeTrip in class org.opentripplanner.middleware.tripmonitor.jobs.CheckMonitoredTrip cannot be applied to given types".

Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ void shouldReportRecurringTripInstanceInPastWithTrackingAsActive() throws Except
String todayFormatted = trip.journeyState.targetDate;

CheckMonitoredTrip check = new CheckMonitoredTrip(trip, this::mockOtpPlanResponse);
check.shouldSkipMonitoredTripCheck(false);
check.shouldSkipMonitoredTripCheck(false, true);
check.checkOtpAndUpdateTripStatus();
// Trip should remain active, and the target date should still be "today".
assertEquals(TRIP_ACTIVE, trip.journeyState.tripStatus);
Expand Down Expand Up @@ -1215,13 +1215,15 @@ void canUpdateTripWithStaleState(
// Mock the current time
DateTimeUtils.useFixedClockAt(clockTime);

// After trip has completed, check that trip status has been updated.
CheckMonitoredTrip check = new CheckMonitoredTrip(monitoredTrip, this::mockOtpPlanResponse);
if (!CheckMonitoredTrip.shouldSkipMonitoredTripPreCheck(monitoredTrip)) {
// After trip has completed, check that trip status has been updated.
CheckMonitoredTrip check = new CheckMonitoredTrip(monitoredTrip, this::mockOtpPlanResponse);

check.run();
check.run();

MonitoredTrip modifiedTrip = Persistence.monitoredTrips.getById(monitoredTrip.id);
assertEquals(expectedStatus, modifiedTrip.journeyState.tripStatus, message);
MonitoredTrip modifiedTrip = Persistence.monitoredTrips.getById(monitoredTrip.id);
assertEquals(expectedStatus, modifiedTrip.journeyState.tripStatus, message);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indent for if block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indent for the closing bracket.

}

private static Stream<Arguments> createUpdateTripWithStaleStateCases() {
Expand Down Expand Up @@ -1266,17 +1268,19 @@ void shouldNotUpdateInactiveOrSnoozedTrip(
// Mock the current time
DateTimeUtils.useFixedClockAt(clockTime);

// After trip has completed, check that trip status has been updated.
CheckMonitoredTrip check = new CheckMonitoredTrip(monitoredTrip, this::mockOtpPlanResponse);
if (!CheckMonitoredTrip.shouldSkipMonitoredTripPreCheck(monitoredTrip)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to replace this if block with an assertion on the expected value for shouldSkipMonitoredTripCheck?

// After trip has completed, check that trip status has been updated.
CheckMonitoredTrip check = new CheckMonitoredTrip(monitoredTrip, this::mockOtpPlanResponse);

check.run();
check.run();

MonitoredTrip modifiedTrip = Persistence.monitoredTrips.getById(monitoredTrip.id);
assertEquals(
monitoredTrip.journeyState.lastCheckedEpochMillis,
modifiedTrip.journeyState.lastCheckedEpochMillis,
"Should not check trip if " + (isActive ? "" : "in") + "active and " + (isSnoozed ? "" : "not ") + "snoozed."
);
MonitoredTrip modifiedTrip = Persistence.monitoredTrips.getById(monitoredTrip.id);
assertEquals(
monitoredTrip.journeyState.lastCheckedEpochMillis,
modifiedTrip.journeyState.lastCheckedEpochMillis,
"Should not check trip if " + (isActive ? "" : "in") + "active and " + (isSnoozed ? "" : "not ") + "snoozed."
);
}
}

private static Stream<Arguments> shouldNotUpdateInactiveOrSnoozedTripCases() {
Expand Down
Loading