Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3cf583c
Update google.dagger.version to v2.60.1
renovate[bot] Jul 14, 2026
b30c56b
Do not use empty string as headsign sentinel in StopTimesMapper
vpaturet Jul 14, 2026
67c3b65
refactor: Make TransactionFrameworkTest#testPeriodicCommits more robust.
t2gran Jul 14, 2026
a252b5e
refactor: introduce `ReadOnlyTimetableSnapshot` and `MutableTimetable…
jessicaKoehnke Jun 1, 2026
97eade5
feat: wire `TransactionModule` and `timetableRepositoryHandle` into D…
jessicaKoehnke Jun 17, 2026
5fa9300
refactor: use `ReadOnlyTimetableSnapshot` and `MutableTimetableSnapsh…
jessicaKoehnke Jun 18, 2026
45b8ab6
refactor: extract `GraphWriterService` from `GraphUpdaterManager` to …
jessicaKoehnke Jun 18, 2026
76e68ca
refactor: move `clearForBuffer` call into `createReadOnlySnapshot` an…
jessicaKoehnke Jun 23, 2026
fc2d531
Update Test dependencies to v6.1.2
renovate[bot] Jul 15, 2026
1af695e
Merge pull request #7824 from opentripplanner/renovate/test-dependencies
renovate[bot] Jul 15, 2026
d0fa22c
Merge pull request #7821 from entur/TransactionFrameworkTest-more-robust
t2gran Jul 15, 2026
227bf13
Only use major version for stale action [ci skip]
leonardehrenfried Jul 15, 2026
1526e31
refactor: Rename SiriRealTimeTripUpdateAdapter to SiriRealTimeUpdateH…
t2gran Jul 16, 2026
2db6a37
refactor: Rename GtfsRealTimeTripUpdateAdapter to GtfsRealTimeUpdateH…
t2gran Jul 16, 2026
df99750
refactor: extract update handlers from update adapters and scope `Tra…
jessicaKoehnke Jul 8, 2026
c1fb942
refactor: migrate `TimetableSnapshotManager` and `TimetableSnapshotFl…
jessicaKoehnke Jul 8, 2026
ef0c052
refactor: move `@Inject` to single-arg constructor and remove `timeta…
jessicaKoehnke Jul 10, 2026
bbbc323
refactor: extract `SiriFuzzyTripMatcherCache` and scope `SiriFuzzyTri…
jessicaKoehnke Jul 13, 2026
d410a20
refactor: update warning limit handling in PeriodicCommitScheduler
jessicaKoehnke Jul 8, 2026
a9b4721
refactor: Make sure the updater manager is initialized once in Timeta…
t2gran Jul 15, 2026
415b1cd
refactor: Remove GraphWriterService from TimetableRepository
t2gran Jul 15, 2026
7823d5c
Apply suggestions from code review
t2gran Jul 16, 2026
8cf929d
review: Initialize the repositoryRegistry using the mutable buffer, n…
t2gran Jul 16, 2026
dd6cc7f
review: Rename TripUpdateApplicator to TripUpdateApplier
t2gran Jul 16, 2026
a7371f7
Merge pull request #7689 from opentripplanner/snapshot-framework
t2gran Jul 16, 2026
d6d94f6
Add changelog entry for #7689 [ci skip]
Jul 16, 2026
94d0ad7
Bump serialization version id for #7689
Jul 16, 2026
60cb79c
Merge pull request #7819 from opentripplanner/renovate/google.dagger.…
leonardehrenfried Jul 16, 2026
1ec00aa
Merge pull request #7818 from entur/fix-stopheadsign-empty-string
vpaturet Jul 16, 2026
dda58b2
Also compute zero-meters transfer for flex
leonardehrenfried Jul 16, 2026
be7a982
Merge branch 'extra-flex-transfer' into ibi-extra-flex-transfer
leonardehrenfried Jul 16, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/close_stale_pr_and_issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
if: github.repository_owner == 'opentripplanner'
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10.3.0
- uses: actions/stale@v10
id: stale
with:
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 30 days'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ static void setup() {
transferRepository,
model.fareServiceFactory().makeFareService(),
null,
null,
FLEX_PARAMETERS
).routingService();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public class SiriAzureETUpdater implements SiriAzureMessageHandler {

private final SiriRealTimeTripUpdateAdapter adapter;
private final Consumer<UpdateResult> recordMetrics;
private final boolean fuzzyTripMatching;
private final String feedId;

private WriteToGraphCallback writeToGraphCallback;
Expand All @@ -33,7 +32,6 @@ public SiriAzureETUpdater(
) {
this.adapter = adapter;
this.recordMetrics = TripUpdateMetrics.streaming(config);
this.fuzzyTripMatching = config.isFuzzyTripMatching();
this.feedId = Objects.requireNonNull(config.feedId(), "feedId must not be null");
}

Expand All @@ -56,13 +54,14 @@ public Future<?> handleMessage(ServiceDelivery serviceDelivery, String messageId

private Future<?> processMessage(List<EstimatedTimetableDeliveryStructure> updates) {
return writeToGraphCallback.execute(context -> {
var result = adapter.applyEstimatedTimetable(
fuzzyTripMatching ? context.siriFuzzyTripMatcher() : null,
context.entityResolver(feedId),
feedId,
UpdateIncrementality.DIFFERENTIAL,
updates
);
var result = adapter
.forUpdate(context.mutableSnapshot())
.applyEstimatedTimetable(
context.entityResolver(feedId),
feedId,
UpdateIncrementality.DIFFERENTIAL,
updates
);
ResultLogger.logUpdateResultErrors(feedId, "siri-et", result);
recordMetrics.accept(result);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.opentripplanner.transit.service.TimetableRepository;
import org.opentripplanner.updater.alert.siri.SiriAlertsUpdateHandler;
import org.opentripplanner.updater.spi.WriteToGraphCallback;
import org.opentripplanner.updater.trip.siri.SiriFuzzyTripMatcherCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.org.siri.siri21.ServiceDelivery;
Expand All @@ -22,13 +23,15 @@ public class SiriAzureSXUpdater implements SiriAzureMessageHandler {

public SiriAzureSXUpdater(
SiriAzureSXUpdaterParameters config,
TimetableRepository timetableRepository
TimetableRepository timetableRepository,
@Nullable SiriFuzzyTripMatcherCache siriFuzzyTripMatcherCache
) {
this.transitAlertService = new TransitAlertServiceImpl(timetableRepository);
this.updateHandler = new SiriAlertsUpdateHandler(
config.feedId(),
transitAlertService,
Duration.ZERO
Duration.ZERO,
siriFuzzyTripMatcherCache
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.opentripplanner.updater.alert.TransitAlertProvider;
import org.opentripplanner.updater.spi.GraphUpdater;
import org.opentripplanner.updater.spi.WriteToGraphCallback;
import org.opentripplanner.updater.trip.siri.SiriFuzzyTripMatcherCache;
import org.opentripplanner.updater.trip.siri.SiriRealTimeTripUpdateAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -133,9 +134,14 @@ public static SiriAzureUpdater createETUpdater(

public static SiriAzureUpdater createSXUpdater(
SiriAzureSXUpdaterParameters config,
TimetableRepository timetableRepository
TimetableRepository timetableRepository,
@Nullable SiriFuzzyTripMatcherCache siriFuzzyTripMatcherCache
) {
var messageHandler = new SiriAzureSXUpdater(config, timetableRepository);
var messageHandler = new SiriAzureSXUpdater(
config,
timetableRepository,
siriFuzzyTripMatcherCache
);
return new SxWrapper(config, messageHandler);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ public SiriETMqttUpdater(
) {
configRef = parameters.configRef();
asyncEstimatedTimetableSource = new MqttEstimatedTimetableSource(parameters);
estimatedTimetableHandler = new EstimatedTimetableHandler(
updateAdapter,
parameters.fuzzyTripMatching(),
parameters.feedId()
);
estimatedTimetableHandler = new EstimatedTimetableHandler(updateAdapter, parameters.feedId());
updateResultConsumer = TripUpdateMetrics.streaming(parameters);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class PeriodicCommitScheduler {
private final String name;
private final ScheduledExecutorService scheduler;
private final Supplier<Future<Void>> performCommit;
private long warningLimitMillis;
private final long interval_ms;
private long warningLimit_ms;

PeriodicCommitScheduler(
String name,
Expand All @@ -30,9 +31,9 @@ class PeriodicCommitScheduler {
this.name = name;
this.scheduler = Executors.newSingleThreadScheduledExecutor(threadFactory);
this.performCommit = performCommit;
var intervalMillis = commitInterval.toMillis();
this.warningLimitMillis = intervalMillis;
scheduler.scheduleAtFixedRate(this::runCommit, intervalMillis, intervalMillis, MILLISECONDS);
this.interval_ms = commitInterval.toMillis();
this.warningLimit_ms = interval_ms;
this.scheduler.scheduleAtFixedRate(this::runCommit, interval_ms, interval_ms, MILLISECONDS);
}

void shutdown() {
Expand All @@ -50,13 +51,31 @@ private void runCommit() {
LOG.error("Error during periodic commit", e);
}
long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime > warningLimitMillis) {
warningLimitMillis *= 2;
LOG.warn(
"Commit is taking a long time to complete (including queued wait), {} ms for {}.",
elapsedTime,
name
);
if (elapsedTime > warningLimit_ms) {
if (warningLimit_ms == interval_ms) {
// We increase the limit after the first log event to:
// - avoid spamming the logs if the update is exceeding the limit by a small amount.
// - distinguish between the first (initialization) and the followups.
warningLimit_ms *= 2;
LOG.warn(
"Commit is taking a long time to complete (including queued wait), {} ms for {}. " +
"The update interval is {} ms. This is the first warning, increasing the warning limit " +
"to {} ms.",
elapsedTime,
name,
interval_ms,
warningLimit_ms
);
} else {
LOG.warn(
"Commit is taking a long time to complete (including queued wait), {} ms for {}. " +
"The warning limit is {} ms. If this continues to happen, consider investigating why or " +
"increasing the update interval.",
elapsedTime,
name,
warningLimit_ms
);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ else if (pathLeg.isTransferLeg()) {

// Map egress leg
EgressPathLeg<T> egressPathLeg = pathLeg.asEgressLeg();
if (
OTPFeature.ExtraTransferLegOnSameStop.isOn() &&
isPathTransferAtSameStop(previousLeg, egressPathLeg)
) {
legs.add(createTransferLegAtSameStop(previousLeg, pathLeg));
}
var egressLegs = mapEgressLeg(egressPathLeg);
legs.addAll(egressLegs);

Expand Down Expand Up @@ -199,6 +205,19 @@ private static <T extends TripSchedule> boolean isPathTransferAtSameStop(
);
}

private static <T extends TripSchedule> boolean isPathTransferAtSameStop(
PathLeg<T> previousLeg,
EgressPathLeg<T> egressPathLeg
) {
return (
previousLeg != null &&
previousLeg.isTransitLeg() &&
egressPathLeg.egress().hasRides() &&
!previousLeg.asTransitLeg().isStaySeatedOntoNextLeg() &&
(previousLeg.asTransitLeg().toStop() == egressPathLeg.fromStop())
);
}

private List<Leg> mapAccessLeg(AccessPathLeg<T> accessPathLeg) {
if (accessPathLeg.access().isFree()) {
return List.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import org.opentripplanner.routing.alertpatch.TransitAlert;
import org.opentripplanner.routing.services.TransitAlertService;
import org.opentripplanner.transit.model.timetable.Direction;
import org.opentripplanner.transit.service.TimetableRepository;
import org.opentripplanner.updater.alert.TransitAlertProvider;
import org.opentripplanner.updater.spi.GraphUpdater;

/**
* This class is used to combine alerts from multiple {@link TransitAlertService}s. Each
Expand All @@ -35,16 +35,11 @@ public class DelegatingTransitAlertServiceImpl implements TransitAlertService {
* This implies that these instances are expected to remain in use indefinitely (not be replaced
* with new instances or taken out of service over time).
*/
public DelegatingTransitAlertServiceImpl(TimetableRepository timetableRepository) {
if (timetableRepository.getUpdaterManager() != null) {
timetableRepository
.getUpdaterManager()
.getUpdaterList()
.stream()
.filter(TransitAlertProvider.class::isInstance)
.map(TransitAlertProvider.class::cast)
.map(TransitAlertProvider::getTransitAlertService)
.forEach(transitAlertServices::add);
public DelegatingTransitAlertServiceImpl(Iterable<GraphUpdater> updaters) {
for (GraphUpdater updater : updaters) {
if (updater instanceof TransitAlertProvider alertProvider) {
transitAlertServices.add(alertProvider.getTransitAlertService());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.opentripplanner.ext.sorlandsbanen.SorlandsbanenNorwayService;
import org.opentripplanner.ext.stopconsolidation.StopConsolidationService;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.framework.transaction.api.TransactionScope;
import org.opentripplanner.place.NearbyPlaceFinder;
import org.opentripplanner.place.NearbyStopFinder;
import org.opentripplanner.place.nearbystopfinder.StraightLineNearbyStopFinder;
Expand Down Expand Up @@ -79,6 +80,14 @@
public interface OtpServerRequestContext {
DebugUiConfig debugUiConfig();

/**
* The transaction scope that was captured at the start of this HTTP request. It holds a strong
* reference to the current {@link org.opentripplanner.framework.transaction.internal.Transaction},
* preventing the {@link org.opentripplanner.framework.transaction.internal.RepositorySnapshotCache}
* from GC-ing any snapshot used during this request.
*/
TransactionScope transactionScope();

/**
* A RouteRequest containing default parameters that will be cloned when handling each request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import org.opentripplanner.transfer.regular.TransferRepository;
import org.opentripplanner.transit.service.TimetableRepository;
import org.opentripplanner.updater.configure.UpdaterConfigurator;
import org.opentripplanner.updater.trip.TimetableSnapshotManager;
import org.opentripplanner.utils.logging.ProgressTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -216,7 +215,8 @@ private void setupTransitRoutingServer() {
vehicleParkingRepository(),
timetableRepository(),
carpoolingRepository(),
snapshotManager(),
factory.updateManager(),
factory.timetableRepositoryHandle(),
routerConfig().updaterConfig()
);

Expand Down Expand Up @@ -305,10 +305,6 @@ public VehicleRentalRepository vehicleRentalRepository() {
return factory.vehicleRentalRepository();
}

private TimetableSnapshotManager snapshotManager() {
return factory.timetableSnapshotManager();
}

public VehicleParkingService vehicleParkingService() {
return factory.vehicleParkingService();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import org.opentripplanner.ext.sorlandsbanen.configure.SorlandsbanenNorwayModule;
import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository;
import org.opentripplanner.ext.stopconsolidation.configure.StopConsolidationServiceModule;
import org.opentripplanner.framework.transaction.UpdateManager;
import org.opentripplanner.framework.transaction.api.RepositoryHandle;
import org.opentripplanner.framework.transaction.configure.TransactionModule;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary;
import org.opentripplanner.raptor.configure.RaptorConfig;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.RaptorTransitData;
Expand Down Expand Up @@ -65,9 +68,10 @@
import org.opentripplanner.transfer.regular.configure.TransferServiceModule;
import org.opentripplanner.transit.configure.TransitModule;
import org.opentripplanner.transit.model.calendar.DefaultTripCalendars;
import org.opentripplanner.transit.repository.MutableTimetableSnapshot;
import org.opentripplanner.transit.repository.ReadOnlyTimetableSnapshot;
import org.opentripplanner.transit.service.TimetableRepository;
import org.opentripplanner.transit.service.TransitService;
import org.opentripplanner.updater.trip.TimetableSnapshotManager;
import org.opentripplanner.warmup.WarmupLauncher;
import org.opentripplanner.warmup.configure.WarmupModule;

Expand Down Expand Up @@ -105,6 +109,7 @@
ViaModule.class,
WarmupModule.class,
WorldEnvelopeServiceModule.class,
TransactionModule.class,
}
)
public interface ConstructApplicationFactory {
Expand All @@ -123,7 +128,8 @@ public interface ConstructApplicationFactory {
VehicleRentalService vehicleRentalService();
VehicleParkingRepository vehicleParkingRepository();
VehicleParkingService vehicleParkingService();
TimetableSnapshotManager timetableSnapshotManager();
UpdateManager updateManager();
RepositoryHandle<ReadOnlyTimetableSnapshot, MutableTimetableSnapshot> timetableRepositoryHandle();
DataImportIssueSummary dataImportIssueSummary();

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.opentripplanner.ext.ridehailing.RideHailingService;
import org.opentripplanner.ext.sorlandsbanen.SorlandsbanenNorwayService;
import org.opentripplanner.ext.stopconsolidation.StopConsolidationService;
import org.opentripplanner.framework.transaction.RepositoryRegistry;
import org.opentripplanner.framework.transaction.api.RepositoryHandle;
import org.opentripplanner.raptor.configure.RaptorConfig;
import org.opentripplanner.routing.algorithm.filterchain.ext.EmissionDecorator;
import org.opentripplanner.routing.algorithm.filterchain.framework.spi.ItineraryDecorator;
Expand All @@ -38,7 +40,10 @@
import org.opentripplanner.street.linking.VertexLinker;
import org.opentripplanner.street.service.StreetLimitationParametersService;
import org.opentripplanner.transfer.regular.RegularTransferService;
import org.opentripplanner.transit.service.TransitService;
import org.opentripplanner.transit.repository.MutableTimetableSnapshot;
import org.opentripplanner.transit.repository.ReadOnlyTimetableSnapshot;
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.TimetableRepository;

@Module
public class ConstructApplicationModule {
Expand All @@ -51,7 +56,9 @@ OtpServerRequestContext providesServerContext(
Graph graph,
LinkingContextFactory linkingContextFactory,
VertexLinker vertexLinker,
TransitService transitService,
TimetableRepository timetableRepository,
RepositoryRegistry repositoryRegistry,
RepositoryHandle<ReadOnlyTimetableSnapshot, MutableTimetableSnapshot> timetableRepositoryHandle,
RegularTransferService transferService,
WorldEnvelopeService worldEnvelopeService,
RealtimeVehicleService realtimeVehicleService,
Expand Down Expand Up @@ -83,6 +90,13 @@ OtpServerRequestContext providesServerContext(
var flexParameters = routerConfig.flexParameters();
var transmodelAPIParameters = routerConfig.transmodelApi();

// Create exactly one TransactionScope per request. The scope holds a strong reference to the
// current Transaction, preventing the RepositorySnapshotCache from GC-ing the snapshot while
// this request is in flight. The scope is stored on DefaultServerRequestContext for this reason.
var transactionScope = repositoryRegistry.scope();
var timetableSnapshot = timetableRepositoryHandle.repositorySnapshot(transactionScope);
var transitService = new DefaultTransitService(timetableRepository, timetableSnapshot);

return new DefaultServerRequestContext(
debugUiConfig,
fareService,
Expand All @@ -97,6 +111,7 @@ OtpServerRequestContext providesServerContext(
defaultRequest,
streetLimitationParametersService,
transferService,
transactionScope,
transitRoutingConfig,
transitService,
triasApiParameters,
Expand Down
Loading
Loading