-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix real-time added patterns persistence with DIFFERENTIAL updates #5726
Fix real-time added patterns persistence with DIFFERENTIAL updates #5726
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev-2.x #5726 +/- ##
=============================================
+ Coverage 68.54% 68.64% +0.10%
- Complexity 16732 16838 +106
=============================================
Files 1918 1927 +9
Lines 72734 72943 +209
Branches 7456 7481 +25
=============================================
+ Hits 49854 50071 +217
+ Misses 20310 20292 -18
- Partials 2570 2580 +10 ☔ View full report in Codecov by Sentry. |
@optionsome What is the effective change for the SiriTimetableSnapshotSource? Is the code just pushed "down" or is there actual changes. It is a bit hard to compare it. |
I'm afraid that this logic is too brittle to go without unit tests documenting the use cases. I think we need to refactor to make unit tests possible. |
// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you moving these call up because they happen for every scenario or is there another reason for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were two reasons, one of them was that I saw this was used in every scenario and the other was that I was unsure what would happen if I run the new code before this. However, I think moving this creates a minor regression as now the previously added trip is always deleted. However, I think previously if you sent a CANCELLED message on the trip, it was just cancelled, not removed. Do we want to keep that behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So an ADDED trip was then CANCELLED again? If so, I'm quite relaxed about that but I would like to see tests documenting all these weird edge cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's just cancelling ADDDED trips, I think it doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@t2gran do you know how cancellation of an added trip works in SIRI? Do we cancel or remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should cancel it. I have asked @lassetyr about this now, and in the future we want to be able to "cancel" any previously added update - DELETE in GTFS?
Summary of my thought about this - related to todays dev meeting discussion:
- Trip updates is applied to the PLANED trips
- not the previous updated trip(if it exist)
- if the trip do not exist the update must create a new trip and set the appropriate flags. This mean in the future that we might need to support incomplete trips (trip update do not have a complete set of data).
- To support scalability beyond the current demand, we might have to support incremental updates(incomplete trip data). If done, it needs to be combined with a "aggregator-service" witch keep track of the complete state and can serve the full-updates to OTP instance starting up, or resetting the RT state (periodically). For reference, look at the event-sourcing design pattern.
Currently the code is just pushed down. However, I'm still slightly evaluating what to do with realtime added trips so I might have to split some functionality. |
Didn't end up refactoring this more so the code is just pushed to the TimetableSnapshot, nothing really at least should have changed for the SIRI updater. |
src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java
Outdated
Show resolved
Hide resolved
case CANCELED -> handleCanceledTrip( | ||
tripId, | ||
serviceDate, | ||
CancelationType.CANCEL, | ||
canceledPreviouslyAddedTrip | ||
); | ||
case DELETED -> handleCanceledTrip( | ||
tripId, | ||
serviceDate, | ||
CancelationType.DELETE, | ||
canceledPreviouslyAddedTrip | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too happy about passing down a boolean to method which leads to an immediate return and I would like to handle it earlier.
In an ideal world Java would have a syntax like this:
case CANCELED if canceledPreviouslyAddedTrip -> Result.success(UpdateSuccess.noWarnings());
but it doesn't.
How about catching that case before the switch? So something like
if (CANCELLED or DELETED) and canceledPreviouslyAddedTrip return Result.success(UpdateSuccess.noWarnings());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I agree with you. However, the handleCanceledTrip is where you expect the handling of any valid case of a CANCELED/DELETED scheduled relationship to be.
I'm sorry, don't review this even though I requested review. I realized I've by mistake included a commit which I forgot to remove a week ago which explains a lot of confusion I've had today related to this pr... I'll try to properly fix this pr tomorrow hopefully. |
ce0eeaa
to
bdf7161
Compare
I've hopefully now fixed the problems. I unfortunately had to force push due to accidentally leaving in a commit I shouldn't have previously. |
@@ -870,16 +891,14 @@ private boolean cancelScheduledTrip( | |||
* exist, and will be reused if a similar added/modified trip message is received with the same | |||
* route and stop sequence. | |||
* | |||
* @param tripId trip id without agency id | |||
* @param serviceDate service date | |||
* @return true if a previously added trip was cancelled | |||
*/ | |||
private boolean cancelPreviouslyAddedTrip( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of this method is slightly misleading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've limited what this method does so the naming is more correct now. I think this method was now otherwise doing duplicate stuff with the new clean-up method I've introduced to the GTFS RT logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now realize that 1cb32d9 commit title is unreadable but I don't think I'm going to fix it as we usually don't fix typos in commit titles 😅
@leonardehrenfried @abyrd this pr is now ready for review again. |
return applyTripUpdates(List.of(update), false); | ||
} | ||
|
||
public UpdateResult applyTripUpdate(GtfsRealtime.TripUpdate update, boolean differential) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you convert the boolean to an enum making it clear what exactly that means? After some discussion in another PR we concluded that we want to change to an enum in the main code as well. That I will do in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid conflicts, maybe it's easier if this gets either updated in this pr or in your pr depending on what gets merged first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, lets do that.
src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java
Outdated
Show resolved
Hide resolved
src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java
Outdated
Show resolved
Hide resolved
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. If a trip was previously added with the ScheduleRelationship | ||
// ADDED and is now cancelled or deleted, we still want to keep the realtime added trip pattern. | ||
if ( | ||
!canceledPreviouslyAddedTrip || | ||
( | ||
tripScheduleRelationship != TripDescriptor.ScheduleRelationship.CANCELED && | ||
tripScheduleRelationship != TripDescriptor.ScheduleRelationship.DELETED | ||
) | ||
) { | ||
this.buffer.revertTripToScheduledTripPattern(tripId, serviceDate); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move this into a method, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, why don't you do all of this logic in handelCancelledTrip
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just don't want to see half the logic on one level of abstraction and the other half in another one.
This is now ready for review again. I realized there was a potential issue with determining if trip was added based on the original trip pattern because of the trip pattern cache so I changed the logic to check the realtime state instead. I didn't update #5726 (comment) yet as there can be potential naming issues with the enum and I don't want accidentally end up with duplicate enum. |
private void purgePatternModifications( | ||
TripDescriptor.ScheduleRelationship tripScheduleRelationship, | ||
FeedScopedId tripId, | ||
LocalDate serviceDate | ||
) { | ||
final TripPattern pattern = buffer.getRealtimeAddedTripPattern(tripId, serviceDate); | ||
if ( | ||
!isPreviouslyAddedTrip(tripId, pattern, serviceDate) || | ||
( | ||
tripScheduleRelationship != TripDescriptor.ScheduleRelationship.CANCELED && | ||
tripScheduleRelationship != TripDescriptor.ScheduleRelationship.DELETED | ||
) | ||
) { | ||
// Remove previous realtime updates for this trip. This is necessary to avoid previous | ||
// stop pattern modifications from persisting. If a trip was previously added with the ScheduleRelationship | ||
// ADDED and is now cancelled or deleted, we still want to keep the realtime added trip pattern. | ||
this.buffer.revertTripToScheduledTripPattern(tripId, serviceDate); | ||
} | ||
} | ||
|
||
private boolean isPreviouslyAddedTrip( | ||
FeedScopedId tripId, | ||
TripPattern pattern, | ||
LocalDate serviceDate | ||
) { | ||
if (pattern == null) { | ||
return false; | ||
} | ||
var timetable = buffer.resolve(pattern, serviceDate); | ||
if (timetable == null) { | ||
return false; | ||
} | ||
var tripTimes = timetable.getTripTimes(tripId); | ||
if (tripTimes == null) { | ||
return false; | ||
} | ||
return tripTimes.getRealTimeState() == RealTimeState.ADDED; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is much nicer!
Summary
There is an issue with "cancelling" a stop closure when using DIFFERENTIAL GTFS realtime trip updates. A realtime added trip pattern for the trip remains in a broken state after a follow up update to the trip.
This also contains minor refactoring.
Issue
Closes #5725
Unit tests
Added tests
Documentation
Not needed
Changelog
From title