Skip to content

Commit

Permalink
Merge pull request #6163 from entur/raptor_application_integration
Browse files Browse the repository at this point in the history
Remove unwanted dependencies in Raptor code
  • Loading branch information
t2gran authored Oct 21, 2024
2 parents db240e9 + 87cbb05 commit 0ae34a8
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ public String getMessage() {

/**
* The Grizzly web server is configured with a transaction timeout and will set the interrupt
* flag on the current thread. OTP does not have many blocking operations which check the
* interrupted flag, so instead we need to do the check manually. The check has a small
* performance overhead so try to place the check in the beginning of significantly big block of
* calculations.
* flag on the current thread. OTP has few blocking operations which check the interrupted flag,
* so instead we need to do the check manually. The check has a small performance overhead, so
* try to place the check at the beginning of a significant calculations.
*/
public static void checkForTimeout() {
// We call yield() to allow monitoring thread to interrupt current thread. If this work or not
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.opentripplanner.raptor.api.request;

import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;

/**
* The raptor environment provides a few hooks and integration points to the caller. The default
* implementation will work just fine, override to adjust Raptor to the calling application.
*/
public interface RaptorEnvironment {
Runnable NOOP = () -> {};

/**
* Use the timeout-hook to register a callback from Raptor. The hook is called periodically to
* check if a time-out is reached. The hook should then exit with an exception handled by the
* caller. Raptor does not have blocking method calls so just calling {@link Thread#interrupt()}
* will not terminate the Raptor search.
*/
default Runnable timeoutHook() {
return NOOP;
}

/**
* Raptor has support for running a few things in parallel. If Raptor catches an
* {@link InterruptedException}, Raptor will convert the checked exception to an unchecked
* exception. The default is {@link RuntimeException}. Override this method to map
* {@link InterruptedException} to your prefered runtime exception.
*/
default RuntimeException mapInterruptedException(InterruptedException e) {
return new RuntimeException(e);
}

/**
* Inject a thread pool into Raptor to run part of the raptor search in parallel. If no
* thread pool is provided, then Raptor runs everything in the caller thread.
*/
@Nullable
default ExecutorService threadPool() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ default int iterationDepartureStepInSeconds() {
return 60;
}

/** see {@link org.opentripplanner.standalone.config.routerconfig.TransitRoutingConfig} **/
default int searchThreadPoolSize() {
return 0;
}

/**
* Coefficients used to calculate raptor-search-window parameters dynamically from heuristics.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.opentripplanner.raptor.api.model.RaptorValueFormatter;
import org.opentripplanner.raptor.api.model.TransitArrival;
import org.opentripplanner.raptor.spi.RaptorCostCalculator;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator;

/**
* The purpose of the stop-arrival-view is to provide a common interface for stop-arrivals for
Expand Down Expand Up @@ -157,7 +156,7 @@ default String asString() {
String arrival =
"[" +
TimeUtils.timeToStrCompact(arrivalTime()) +
cost(c1(), DefaultCostCalculator.ZERO_COST, RaptorValueFormatter::formatC1) +
cost(c1(), RaptorCostCalculator.ZERO_COST, RaptorValueFormatter::formatC1) +
cost(c2(), RaptorConstants.NOT_SET, RaptorValueFormatter::formatC2) +
"]";
return switch (arrivedBy()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.opentripplanner.raptor.configure;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.Nullable;
import org.opentripplanner.framework.concurrent.OtpRequestThreadFactory;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.request.RaptorEnvironment;
import org.opentripplanner.raptor.api.request.RaptorRequest;
import org.opentripplanner.raptor.api.request.RaptorTuningParameters;
import org.opentripplanner.raptor.rangeraptor.DefaultRangeRaptorWorker;
Expand All @@ -27,26 +25,26 @@
/**
* This class is responsible for creating a new search and holding application scoped Raptor state.
* <p/>
* This class should have APPLICATION scope. It manage a threadPool, and hold a reference to the
* application tuning parameters.
* This class should have APPLICATION scope. It keeps a reference to the environment and the
* tuning parameters. The environment has a thread-pool, which should be APPLICATION scope.
*
* @param <T> The TripSchedule type defined by the user of the raptor API.
*/
public class RaptorConfig<T extends RaptorTripSchedule> {

private final ExecutorService threadPool;
private final RaptorEnvironment environment;
private final RaptorTuningParameters tuningParameters;

/** The service is not final, because it depends on the request. */
private PassThroughPointsService passThroughPointsService = null;

public RaptorConfig(RaptorTuningParameters tuningParameters) {
public RaptorConfig(RaptorTuningParameters tuningParameters, RaptorEnvironment environment) {
this.tuningParameters = tuningParameters;
this.threadPool = createNewThreadPool(tuningParameters.searchThreadPoolSize());
this.environment = environment;
}

public static <T extends RaptorTripSchedule> RaptorConfig<T> defaultConfigForTest() {
return new RaptorConfig<>(new RaptorTuningParameters() {});
return new RaptorConfig<>(new RaptorTuningParameters() {}, new RaptorEnvironment() {});
}

public SearchContext<T> context(RaptorTransitDataProvider<T> transit, RaptorRequest<T> request) {
Expand Down Expand Up @@ -114,19 +112,23 @@ public Heuristics createHeuristic(
}

public boolean isMultiThreaded() {
return threadPool != null;
return threadPool() != null;
}

public ExecutorService threadPool() {
return threadPool;
return environment.threadPool();
}

public void shutdown() {
if (threadPool != null) {
threadPool.shutdown();
if (threadPool() != null) {
threadPool().shutdown();
}
}

public RuntimeException mapInterruptedException(InterruptedException e) {
return environment.mapInterruptedException(e);
}

public RaptorSearchWindowCalculator searchWindowCalculator() {
return new RaptorSearchWindowCalculator(tuningParameters.dynamicSearchWindowCoefficients());
}
Expand Down Expand Up @@ -164,14 +166,8 @@ private RangeRaptor<T> createRangeRaptor(SearchContext<T> ctx, RangeRaptorWorker
ctx.roundTracker(),
ctx.calculator(),
ctx.createLifeCyclePublisher(),
ctx.performanceTimers()
ctx.performanceTimers(),
environment.timeoutHook()
);
}

@Nullable
private ExecutorService createNewThreadPool(int size) {
return size > 0
? Executors.newFixedThreadPool(size, OtpRequestThreadFactory.of("raptor-%d"))
: null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static java.util.Objects.requireNonNull;

import org.opentripplanner.framework.application.OTPRequestTimeoutException;
import org.opentripplanner.raptor.api.debug.RaptorTimers;
import org.opentripplanner.raptor.api.model.RaptorConstants;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
Expand Down Expand Up @@ -66,6 +65,8 @@ public final class RangeRaptor<T extends RaptorTripSchedule> implements RaptorRo

private final LifeCycleEventPublisher lifeCycle;

private final Runnable timeoutHook;

private final int minNumberOfRounds;

public RangeRaptor(
Expand All @@ -75,7 +76,8 @@ public RangeRaptor(
RoundTracker roundTracker,
RaptorTransitCalculator<T> calculator,
LifeCycleEventPublisher lifeCyclePublisher,
RaptorTimers timers
RaptorTimers timers,
Runnable timeoutHook
) {
this.worker = requireNonNull(worker);
this.transitData = requireNonNull(transitData);
Expand All @@ -85,6 +87,7 @@ public RangeRaptor(
this.minNumberOfRounds = accessPaths.calculateMaxNumberOfRides();
this.roundTracker = requireNonNull(roundTracker);
this.lifeCycle = requireNonNull(lifeCyclePublisher);
this.timeoutHook = requireNonNull(timeoutHook);
}

public RaptorRouterResult<T> route() {
Expand Down Expand Up @@ -165,7 +168,7 @@ private int round() {
* Run the raptor search for this particular iteration departure time
*/
private void setupIteration(int iterationDepartureTime) {
OTPRequestTimeoutException.checkForTimeout();
timeoutHook.run();
roundTracker.setupIteration();
lifeCycle.prepareForNextRound(round());
lifeCycle.setupIteration(iterationDepartureTime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch;
import org.opentripplanner.raptor.spi.RaptorTimeTable;
import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripScheduleBoardSearch;

/**
* This class contains code which is shared by all time-dependent {@link RoutingStrategy}s.
Expand Down Expand Up @@ -119,9 +118,6 @@ private int earliestBoardTime(int prevArrivalTime, int boardSlack) {
return calculator.plusDuration(prevArrivalTime, boardSlack);
}

/**
* Create a trip search using {@link TripScheduleBoardSearch}.
*/
private RaptorTripScheduleSearch<T> createTripSearch(RaptorTimeTable<T> timeTable) {
if (!inFirstIteration && isFirstRound(round) && !hasTimeDependentAccess) {
// For the first round of every iteration(except the first) we restrict the first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.opentripplanner.framework.application.OTPRequestTimeoutException;
import org.opentripplanner.raptor.RaptorService;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.request.RaptorRequest;
Expand Down Expand Up @@ -182,10 +181,10 @@ private void runHeuristicsInParallel() {
Thread.currentThread().interrupt();
// propagate interruption to the running task.
asyncResult.cancel(true);
throw new OTPRequestTimeoutException();
throw config.mapInterruptedException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof DestinationNotReachedException) {
throw new DestinationNotReachedException();
if (e.getCause() instanceof DestinationNotReachedException dnr) {
throw dnr;
}
LOG.error(e.getMessage() + ". Request: " + originalRequest, e);
throw new IllegalStateException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.opentripplanner.framework.application.OTPRequestTimeoutException;
import org.opentripplanner.raptor.RaptorService;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.request.RaptorRequest;
Expand Down Expand Up @@ -182,7 +181,7 @@ private void runHeuristicsInParallel() {
Thread.currentThread().interrupt();
// propagate interruption to the running task.
asyncResult.cancel(true);
throw new OTPRequestTimeoutException();
throw config.mapInterruptedException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof DestinationNotReachedException) {
throw new DestinationNotReachedException();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package org.opentripplanner.raptor.spi;

import java.util.Objects;
import org.opentripplanner.framework.tostring.ValueObjectToStringBuilder;
import org.opentripplanner.framework.time.DurationUtils;
import org.opentripplanner.framework.time.TimeUtils;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;

/**
Expand Down Expand Up @@ -68,19 +69,18 @@ public boolean equals(Object o) {

@Override
public String toString() {
return ValueObjectToStringBuilder
.of()
.addText("[")
.addObj(trip.pattern().stopIndex(boardStopPos))
.addText(" ~ ")
.addServiceTime(boardTime())
.addText(" ")
.addServiceTime(alightTime())
.addText("(")
.addDurationSec(alightTime() - boardTime())
.addText(") ~ ")
.addObj(trip.pattern().stopIndex(alightStopPos))
.addText("]")
.toString();
return (
"[" +
trip.pattern().stopIndex(boardStopPos) +
" ~ " +
TimeUtils.timeToStrCompact(boardTime()) +
" " +
TimeUtils.timeToStrCompact(alightTime()) +
"(" +
DurationUtils.durationToStr(alightTime() - boardTime()) +
") ~ " +
trip.pattern().stopIndex(alightStopPos) +
"]"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,10 @@ private Collection<? extends RoutingAccessEgress> fetchAccessEgresses(AccessEgre
}

/**
* Given a list of {@code results} shift the access ones which contain driving
* so that they only start at the time when the ride hailing vehicle can actually be there
* to pick up passengers.
* Given a list of {@code results} shift the access ones that contain driving so that they only
* start at the time when the ride hailing vehicle can actually be there to pick up passengers.
* <p>
* If there are accesses/egresses with only walking then they remain unchanged.
* If there are accesses/egresses with only walking, then they remain unchanged.
* <p>
* This method is a good candidate to be moved to the access/egress filter chain when that has
* been added.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* trips are ordered after the FIRST stop boarding times. We also assume that trips do not pass each
* other; Hence trips IN SERVICE on a given day will be in order for all other stops too.
* <p/>
* The search use a binary search if the number of trip schedules is above a given threshold. A
* The search uses a binary search if the number of trip schedules is above a given threshold. A
* linear search is slow when the number of schedules is very large, let say more than 300 trip
* schedules.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import dagger.Module;
import dagger.Provides;
import jakarta.inject.Singleton;
import org.opentripplanner.raptor.api.request.RaptorEnvironment;
import org.opentripplanner.raptor.configure.RaptorConfig;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule;
import org.opentripplanner.standalone.config.BuildConfig;
import org.opentripplanner.standalone.config.ConfigModel;
import org.opentripplanner.standalone.config.OtpConfig;
import org.opentripplanner.standalone.config.RouterConfig;
import org.opentripplanner.standalone.config.routerconfig.RaptorEnvironmentFactory;

/**
* Map {@link ConfigModel} into more specific types like {@link BuildConfig} to simplify
Expand All @@ -34,7 +36,17 @@ static RouterConfig provideRouterConfig(ConfigModel model) {

@Provides
@Singleton
static RaptorConfig<TripSchedule> providesRaptorConfig(ConfigModel config) {
return new RaptorConfig<>(config.routerConfig().transitTuningConfig());
static RaptorConfig<TripSchedule> providesRaptorConfig(
RouterConfig routerConfig,
RaptorEnvironment environment
) {
return new RaptorConfig<>(routerConfig.transitTuningConfig(), environment);
}

@Provides
@Singleton
static RaptorEnvironment providesRaptorEnvironment(RouterConfig routerConfig) {
int searchThreadPoolSize = routerConfig.transitTuningConfig().searchThreadPoolSize();
return RaptorEnvironmentFactory.create(searchThreadPoolSize);
}
}
Loading

0 comments on commit 0ae34a8

Please sign in to comment.