Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/fix-skipped-removal' into dev-2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
vesameskanen committed May 8, 2024
2 parents 91c8396 + b98c3c8 commit eaaa9cc
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ private Result<TripUpdate, UpdateError> handleModifiedTrip(

// Also check whether trip id has been used for previously ADDED/MODIFIED trip message and
// remove the previously created trip
removePreviousRealtimeUpdate(trip, serviceDate);
this.buffer.removeRealtimeAddedTripPatternAndTimetablesForTrip(trip.getId(), serviceDate);

return updateResult;
}
Expand Down Expand Up @@ -410,27 +410,6 @@ private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDat
return success;
}

/**
* Removes previous trip-update from buffer if there is an update with given trip on service date
*
* @param serviceDate service date
* @return true if a previously added trip was removed
*/
private boolean removePreviousRealtimeUpdate(final Trip trip, final LocalDate serviceDate) {
boolean success = false;

final TripPattern pattern = buffer.getRealtimeAddedTripPattern(trip.getId(), serviceDate);
if (pattern != null) {
// Remove the previous real-time-added TripPattern from buffer.
// Only one version of the real-time-update should exist
buffer.removeLastAddedTripPattern(trip.getId(), serviceDate);
buffer.removeRealtimeUpdatedTripTimes(pattern, trip.getId(), serviceDate);
success = true;
}

return success;
}

private boolean purgeExpiredData() {
final LocalDate today = LocalDate.now(transitModel.getTimeZone());
final LocalDate previously = today.minusDays(2); // Just to be safe...
Expand Down
86 changes: 49 additions & 37 deletions src/main/java/org/opentripplanner/model/TimetableSnapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.updater.spi.UpdateError;
import org.opentripplanner.updater.spi.UpdateSuccess;
Expand Down Expand Up @@ -111,38 +110,6 @@ public Timetable resolve(TripPattern pattern, LocalDate serviceDate) {
return pattern.getScheduledTimetable();
}

public void removeRealtimeUpdatedTripTimes(
TripPattern tripPattern,
FeedScopedId tripId,
LocalDate serviceDate
) {
SortedSet<Timetable> sortedTimetables = this.timetables.get(tripPattern);
if (sortedTimetables != null) {
TripTimes tripTimesToRemove = null;
for (Timetable timetable : sortedTimetables) {
if (timetable.isValidFor(serviceDate)) {
final TripTimes tripTimes = timetable.getTripTimes(tripId);
if (tripTimes == null) {
LOG.debug("No triptimes to remove for trip {}", tripId);
} else if (tripTimesToRemove != null) {
LOG.debug("Found two triptimes to remove for trip {}", tripId);
} else {
tripTimesToRemove = tripTimes;
}
}
}

if (tripTimesToRemove != null) {
for (Timetable sortedTimetable : sortedTimetables) {
boolean isDirty = sortedTimetable.getTripTimes().remove(tripTimesToRemove);
if (isDirty) {
dirtyTimetables.add(sortedTimetable);
}
}
}
}
}

/**
* Get the current trip pattern given a trip id and a service date, if it has been changed from
* the scheduled pattern with an update, for which the stopPattern is different.
Expand Down Expand Up @@ -295,11 +262,56 @@ public void clear(String feedId) {
}

/**
* Removes the latest added trip pattern from the cache. This should be done when removing the
* trip times from the timetable the trip has been added to.
* If a previous realtime update has changed which trip pattern is used for this trip on the given
* service date, this removes the timetables for this trip on the service date for the trip
* pattern and also the connection of that trip pattern for this trip on the given service date.
* The original trip pattern from the scheduled data will be used for the trip again on this
* service date until a new trip pattern is used for the trip.
*
* @return true if a new trip pattern was used for the trip previously and its connection to the
* trip one the given service date was attempted to removed together with its timetables for the
* trip.
*/
public void removeLastAddedTripPattern(FeedScopedId feedScopedTripId, LocalDate serviceDate) {
realtimeAddedTripPattern.remove(new TripIdAndServiceDate(feedScopedTripId, serviceDate));
public boolean removeRealtimeAddedTripPatternAndTimetablesForTrip(
FeedScopedId tripId,
LocalDate serviceDate
) {
boolean success = false;

final TripPattern pattern = getRealtimeAddedTripPattern(tripId, serviceDate);
if (pattern != null) {
// Remove the previous real-time-added TripPattern from buffer.
// Only one version of the real-time-update should exist
realtimeAddedTripPattern.remove(new TripIdAndServiceDate(tripId, serviceDate));
SortedSet<Timetable> sortedTimetables = this.timetables.get(pattern);
if (sortedTimetables != null) {
TripTimes tripTimesToRemove = null;
for (Timetable timetable : sortedTimetables) {
if (timetable.isValidFor(serviceDate)) {
final TripTimes tripTimes = timetable.getTripTimes(tripId);
if (tripTimes == null) {
LOG.debug("No triptimes to remove for trip {}", tripId);
} else if (tripTimesToRemove != null) {
LOG.debug("Found two triptimes to remove for trip {}", tripId);
} else {
tripTimesToRemove = tripTimes;
}
}
}

if (tripTimesToRemove != null) {
for (Timetable sortedTimetable : sortedTimetables) {
boolean isDirty = sortedTimetable.getTripTimes().remove(tripTimesToRemove);
if (isDirty) {
dirtyTimetables.add(sortedTimetable);
}
}
}
}
success = true;
}

return success;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,28 @@ public UpdateResult applyTripUpdates(
// starts for example at 40:00, yesterday would probably be a better guess.
serviceDate = localDateNow.get();
}

uIndex += 1;
LOG.debug("trip update #{} ({} updates) :", uIndex, tripUpdate.getStopTimeUpdateCount());
LOG.trace("{}", tripUpdate);

// Determine what kind of trip update this is
final TripDescriptor.ScheduleRelationship tripScheduleRelationship = determineTripScheduleRelationship(
tripDescriptor
);
var canceledPreviouslyAddedTrip = false;
if (!fullDataset) {
// Check whether trip id has been used for previously ADDED trip message and mark previously
// created trip as DELETED unless schedule relationship is CANCELED, then as CANCEL
var cancelationType = tripScheduleRelationship ==
TripDescriptor.ScheduleRelationship.CANCELED
? CancelationType.CANCEL
: CancelationType.DELETE;
canceledPreviouslyAddedTrip =
cancelPreviouslyAddedTrip(tripId, serviceDate, cancelationType);
// Remove previous realtime updates for this trip. This is necessary to avoid previous
// stop pattern modifications from persisting
this.buffer.removeRealtimeAddedTripPatternAndTimetablesForTrip(tripId, serviceDate);
}

uIndex += 1;
LOG.debug("trip update #{} ({} updates) :", uIndex, tripUpdate.getStopTimeUpdateCount());
LOG.trace("{}", tripUpdate);

Result<UpdateSuccess, UpdateError> result;
try {
Expand All @@ -297,8 +310,18 @@ public UpdateResult applyTripUpdates(
tripId,
serviceDate
);
case CANCELED -> handleCanceledTrip(tripId, serviceDate, CancelationType.CANCEL);
case DELETED -> handleCanceledTrip(tripId, serviceDate, CancelationType.DELETE);
case CANCELED -> handleCanceledTrip(
tripId,
serviceDate,
CancelationType.CANCEL,
canceledPreviouslyAddedTrip
);
case DELETED -> handleCanceledTrip(
tripId,
serviceDate,
CancelationType.DELETE,
canceledPreviouslyAddedTrip
);
case REPLACEMENT -> validateAndHandleModifiedTrip(
tripUpdate,
tripDescriptor,
Expand Down Expand Up @@ -347,6 +370,26 @@ public UpdateResult applyTripUpdates(
return updateResult;
}

/**
* This shouldn't be used outside of this class for other purposes than testing where the forced
* snapshot commit can guarantee consistent behaviour.
*/
TimetableSnapshot getTimetableSnapshot(final boolean force) {
final long now = System.currentTimeMillis();
if (force || now - lastSnapshotTime > maxSnapshotFrequency.toMillis()) {
if (force || buffer.isDirty()) {
LOG.debug("Committing {}", buffer);
snapshot = buffer.commit(transitLayerUpdater, force);
} else {
LOG.debug("Buffer was unchanged, keeping old snapshot.");
}
lastSnapshotTime = System.currentTimeMillis();
} else {
LOG.debug("Snapshot frequency exceeded. Reusing snapshot {}", snapshot);
}
return snapshot;
}

private static void logUpdateResult(
String feedId,
Map<TripDescriptor.ScheduleRelationship, Integer> failuresByRelationship,
Expand All @@ -367,22 +410,6 @@ private static void logUpdateResult(
});
}

private TimetableSnapshot getTimetableSnapshot(final boolean force) {
final long now = System.currentTimeMillis();
if (force || now - lastSnapshotTime > maxSnapshotFrequency.toMillis()) {
if (force || buffer.isDirty()) {
LOG.debug("Committing {}", buffer);
snapshot = buffer.commit(transitLayerUpdater, force);
} else {
LOG.debug("Buffer was unchanged, keeping old snapshot.");
}
lastSnapshotTime = System.currentTimeMillis();
} else {
LOG.debug("Snapshot frequency exceeded. Reusing snapshot {}", snapshot);
}
return snapshot;
}

/**
* Determine how the trip update should be handled.
*
Expand Down Expand Up @@ -435,11 +462,6 @@ private Result<UpdateSuccess, UpdateError> handleScheduledTrip(
return UpdateError.result(tripId, NO_SERVICE_ON_DATE);
}

// If this trip_id has been used for previously ADDED/MODIFIED trip message (e.g. when the
// sequence of stops has changed, and is now changing back to the originally scheduled one),
// mark that previously created trip as DELETED.
cancelPreviouslyAddedTrip(tripId, serviceDate, CancelationType.DELETE);

// Get new TripTimes based on scheduled timetable
var result = pattern
.getScheduledTimetable()
Expand Down Expand Up @@ -686,10 +708,6 @@ private Result<UpdateSuccess, UpdateError> handleAddedTrip(
"number of stop should match the number of stop time updates"
);

// Check whether trip id has been used for previously ADDED trip message and mark previously
// created trip as DELETED
cancelPreviouslyAddedTrip(tripId, serviceDate, CancelationType.DELETE);

Route route = getOrCreateRoute(tripDescriptor, tripId);

// Create new Trip
Expand Down Expand Up @@ -1104,10 +1122,6 @@ private Result<UpdateSuccess, UpdateError> handleModifiedTrip(
var tripId = trip.getId();
cancelScheduledTrip(tripId, serviceDate, CancelationType.DELETE);

// Check whether trip id has been used for previously ADDED/REPLACEMENT trip message and mark it
// as DELETED
cancelPreviouslyAddedTrip(tripId, serviceDate, CancelationType.DELETE);

// Add new trip
return addTripToGraphAndBuffer(
trip,
Expand All @@ -1122,19 +1136,21 @@ private Result<UpdateSuccess, UpdateError> handleModifiedTrip(
private Result<UpdateSuccess, UpdateError> handleCanceledTrip(
FeedScopedId tripId,
final LocalDate serviceDate,
CancelationType markAsDeleted
CancelationType cancelationType,
boolean canceledPreviouslyAddedTrip
) {
// if previously a added trip was removed, there can't be a scheduled trip to remove
if (canceledPreviouslyAddedTrip) {
return Result.success(UpdateSuccess.noWarnings());
}
// Try to cancel scheduled trip
final boolean cancelScheduledSuccess = cancelScheduledTrip(tripId, serviceDate, markAsDeleted);

// Try to cancel previously added trip
final boolean cancelPreviouslyAddedSuccess = cancelPreviouslyAddedTrip(
final boolean cancelScheduledSuccess = cancelScheduledTrip(
tripId,
serviceDate,
markAsDeleted
cancelationType
);

if (!cancelScheduledSuccess && !cancelPreviouslyAddedSuccess) {
if (!cancelScheduledSuccess) {
debug(tripId, "No pattern found for tripId. Skipping cancellation.");
return UpdateError.result(tripId, NO_TRIP_FOR_CANCELLATION_FOUND);
}
Expand Down
Loading

0 comments on commit eaaa9cc

Please sign in to comment.