Skip to content

Upstream merge 2026-09- 16 - #322

Merged
miles-grant-ibigroup merged 107 commits into
ibi-dev-2.xfrom
upstream-merge-2026-09-16
Sep 16, 2026
Merged

miles-grant-ibigroup merged 107 commits into
ibi-dev-2.xfrom
upstream-merge-2026-09-16

Conversation

@leonardehrenfried

Copy link
Copy Markdown

☠️ Attention: this sets the GraphQL field Trip.serviceId to null as it is blocking a calendar refactoring. ☠️

@binh-dam-ibigroup and @alec-georgoff said that this is not used by Arcadis, so lets see if this holds up in practise.

testower and others added 30 commits August 25, 2026 13:15
Loads GBFS geofencing zones during the graph build rather than through the
runtime updater, avoiding the cost of re-fetching and re-applying them on every
restart. It takes a GBFS v3 manifest and discovers which systems to load,
skipping any whose gbfs.json does not publish a geofencing_zones feed, so a
blanket "permanent" default picks up only the systems that have zones.

There is no feature flag: the sandbox is activated by the presence of its url,
matching vehicleRentalServiceDirectory.

Adds the permanent phase to geofencingZones, which until now had nothing to
implement it, and persists the rental repository in the graph so the zones
survive to the serve phase. The spatial indices are transient - JTS STRtree and
PreparedGeometry caches do not survive Kryo - so the raw zones are stored and
the indices rebuilt lazily on first access.

Vehicles and stations remain realtime data, so a vehicle rental updater is still
required. Rental vertices only exist at runtime, so the updater seeds their
initial zones by reading through the repository rather than from an index of its
own, filtered to each vertex's network.

Note that a GBFS updater configured directly under "updaters" does not read the
shared gbfs section, so enabling geofencingZones on one for a network also built
here applies the zones twice.
The sandbox applies zones during the graph build, so both the zones and the
boundary markers they leave on vertices have to survive serialization. Neither
did.

GeofencingZone was not Serializable. Nothing had noticed because until now the
only zones in memory were created by updaters after the graph was loaded, so no
zone was ever written. Registering them from the graph build made the repository
fail to serialize.

Vertex.geofencingBoundaries was transient for the same reason, and would have
failed silently rather than loudly: the build would apply the boundaries, saving
the graph would drop them, and the router would then behave as though no zone
had a boundary.

The spatial indices stay transient - JTS STRtree and PreparedGeometry caches do
not survive Kryo - and are rebuilt lazily from the stored zones.
…ation

The repository is part of the serialized graph, so the requirement
belongs on the interface stored in SerializedGraphObject - matching
WorldEnvelopeRepository, StreetRepository and StreetDetailsRepository -
rather than on the one class that happens to implement it today.
# Conflicts:
#	application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java
- Take the geofencing_zones feed name from the GBFS model instead of a
  string literal.
- Drop the debug logging in network selection; the INFO line per applied
  network already shows what was selected.
- Derive zonesChanged from its precondition rather than assigning it
  inside the block it guards.
- Move the sandbox doc's changelog to the bottom.
Reading a non-HTTP scheme as a local file is behaviour of the shared
OtpHttpClient, useful for testing but not something to advertise as a
feature of this sandbox. Support is unchanged.
The graph builder held DefaultVehicleRentalRepository and the module
downcast to it, because only the implementation had the setter that also
kept the raw zones for serialization. Give the interface a single
registration method taking the zones, so both callers pass them and the
sandbox no longer depends on the implementation.

Whether a registration reaches the graph now depends on when it happens
rather than which overload is called: only the graph build runs before
the graph is written. Renames serializedZones to zonesByNetwork, which is
what it now holds.
Replace the Externalizable fallback for Trove's TIntObjectHashMap with a
dedicated Kryo serializer, following the pattern of conveyal kryo-tools'
TIntIntHashMapSerializer. The Externalizable path routes every entry
through the KryoObjectInput/KryoObjectOutput adapter and serializes
Trove-internal bookkeeping; writing the entries directly is faster and
smaller (measured -52 MB / -2.5% on the Norway graph with empirical
delay data, ~290k maps).

This changes the serialized wire format.
Both tests built the URI from a working-directory-relative path. The
fixture sits at the classpath root rather than beside either test, so
the loader is given the rooted path.

The missing-file and relative-file-URI cases keep their literal paths:
the loader throws when a resource does not exist, and the relative URI
is what that test exercises.
Same wording as the geofencing sandbox, which copied it from here. The
behaviour is unchanged; only the documentation stops presenting a
testing affordance as a feature.
Renames GeofencingZoneScope to GeofencingZoneOtpPhase, PERMANENT to
GRAPH_BUILD and REALTIME to SERVE, so the values name the phase a
deployment chooses rather than the kind of graph change that results.
Scope stays the vocabulary for the latter, and the enum documents how
the two correspond.

The config key applyGeofencingZones is unchanged; its values become
graph-build and serve. Nothing released used the old spellings, so the
migration guide maps the old boolean straight to the new names.
The index the applier returned was never read during the graph build:
the build wires GeofencingZoneService.EMPTY, the applier computes
boundary markers from each zone's geometry rather than the index, and
both callers passed it straight to the repository. In the build-and-save
path it was then discarded at serialization and rebuilt on load.

Registration now takes the zones alone and the repository indexes them,
so the serialized state is just the zones and index construction happens
in one place. Drops GeofencingZoneApplierResult, which had one field
left, and the write-only latestZoneIndex field in the updater.
The repository was serialized purely to carry geofencing zones out of
the build: nothing in the build read it, and at serve time its stored
zones were read exactly once, to rebuild the indices Kryo could not
carry. Being serialized is also what forced the transient index, the
volatile field and the double-checked locking around it.

The zones now travel on the Graph, which was already serialized, so
SerializedGraphObject loses its repository field rather than gaining a
new one. The repository is created in the serve phase from those zones
and holds indices alone. It is created on first injection, late enough
that a combined build-and-serve run sees what the build just wrote.

Bumps the serialization version id: the graph no longer contains a
vehicle rental repository.
The manifest is named by build-config.json, so failing to read it is a
misconfigured or broken build rather than a data problem, and continuing
would produce a graph silently missing every network's zones.

An individual network is third-party data the manifest references, so a
network that cannot be read is added to the build issue report and
skipped. Nothing recovers it later - a network in the graph-build phase
is skipped by the updater - so the report is the only signal that its
restrictions are absent, and the sandbox doc now says so.

Networks that are simply unconfigured or in another phase stay ordinary
log lines; with includeUnlistedNetworks off they are the common case and
would bury the real failures.
# Conflicts:
#	application/src/test/java/org/opentripplanner/netex/mapping/NetexMapperTest.java
…interface

Building the record used to force every dependency (RoutingService,
RealtimeVehicleService, NearbyPlaceFinder, ...) to be constructed as soon as
any GraphQL data fetcher touched the context, even if the query never used
most of them. GtfsGraphQLRequestContext is now an interface; the production
implementation delegates each accessor to the enclosing RequestScopedFactory,
so a dependency is only resolved on first actual use. Tests that need a
plain, hand-built context use the new TestGtfsGraphQLRequestContext record.
…nterface

Same motivation as the GTFS context: the class's constructor forced every
dependency to be resolved as soon as any Transmodel data fetcher touched the
context. TransmodelRequestContext is now an interface; the production
implementation delegates each getter to the enclosing RequestScopedFactory,
so a dependency is only resolved on first actual use. Tests that need a
plain, hand-built context use the new TestTransmodelRequestContext class.

# Conflicts:
#	application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java
Matches the naming already used for GtfsGraphQLRequestContext and the
OTPFeature.TransmodelGraphQlApi flag / TransmodelGraphQLSchema — the
Transmodel API is a GraphQL API just like GTFS, so its request context
should say so too.

# Conflicts:
#	application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java

fix: correct stale @link in DaggerTransmodelGraphQLRequestContext javadoc

Leftover reference to the old TransmodelRequestContext name.
leonardehrenfried and others added 26 commits September 10, 2026 13:32
…port-mode

Add snowAndIce to the Transmodel TransportMode enum
…lease-age

Add minimum release age to renovate automerges
…memory

Limit growth of NeTEx stop time index
…ld-request-services-on-demand

Turn GraphQL request contexts into lazy, request-scoped interfaces
…m-svc

Remove .idea files from version control
…l-template

Expand {from}/{to} placeholders in the carpool booking URL
…/gtfs-api-code-generation

Update dependency @graphql-codegen/cli to v7.4.0
…-comparison

Remove 'Version Comparison' documentation page
…/highly-trusted-dependencies-(patch)

Update highly trusted dependencies (patch)
Vitest 5 resolves the -c/config path relative to --root instead of the
cwd, so `vitest --root src/` no longer finds client/vite.config.ts.
Without it, the jsdom test environment configured there is never
applied, so tests run under node and fail with "document is not
defined". Point the test/coverage scripts at the config explicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018sSwbgmVFJz2sao3UFPX65
…/major-vitest-monorepo

Update vitest monorepo to v5
…-api

Deprecate serviceId in GTFS API and stop populating it

@miles-grant-ibigroup miles-grant-ibigroup left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like we do use it, but this somehow doesn't break anything

@miles-grant-ibigroup
miles-grant-ibigroup merged commit 9859cde into ibi-dev-2.x Sep 16, 2026
16 checks passed
@miles-grant-ibigroup
miles-grant-ibigroup deleted the upstream-merge-2026-09-16 branch September 16, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants