All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.updater.trip.TimetableSnapshotManager Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.updater.trip;

import java.time.LocalDate;
import java.util.Objects;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.opentripplanner.model.RealTimeTripUpdate;
import org.opentripplanner.model.Timetable;
import org.opentripplanner.model.TimetableSnapshot;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.RealTimeRaptorTransitDataUpdater;
import org.opentripplanner.routing.util.ConcurrentPublished;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.framework.Result;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.updater.TimetableSnapshotParameters;
import org.opentripplanner.updater.spi.UpdateError;
import org.opentripplanner.updater.spi.UpdateSuccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A class which abstracts away locking, updating, committing and purging of the timetable snapshot.
 */
public final class TimetableSnapshotManager {

  private static final Logger LOG = LoggerFactory.getLogger(TimetableSnapshotManager.class);
  private final RealTimeRaptorTransitDataUpdater realtimeRaptorTransitDataUpdater;

  /**
   * The working copy of the timetable snapshot. Should not be visible to routing threads.
   * By design, only one thread should ever be writing to this buffer.
   */
  private final TimetableSnapshot buffer = new TimetableSnapshot();

  /**
   * The last committed snapshot that was handed off to a routing thread. This snapshot may be given
   * to more than one routing thread.
   */
  private final ConcurrentPublished snapshot = new ConcurrentPublished<>();

  /**
   * Should expired real-time data be purged from the graph.
   * TODO RT_AB: Clarify exactly what "purge" means and in what circumstances would one turn it off.
   */
  private final boolean purgeExpiredData;
  /**
   * We inject a provider to retrieve the current service-date(now). This enables us to unit-test
   * the purgeExpiredData feature.
   */
  private final Supplier localDateNow;

  private LocalDate lastPurgeDate = null;

  /**
   *
   * @param localDateNow This supplier allows you to inject a custom lambda to override what is
   *                     considered 'today'. This is useful for unit testing.
   */
  public TimetableSnapshotManager(
    @Nullable RealTimeRaptorTransitDataUpdater realtimeRaptorTransitDataUpdater,
    TimetableSnapshotParameters parameters,
    Supplier localDateNow
  ) {
    this.realtimeRaptorTransitDataUpdater = realtimeRaptorTransitDataUpdater;
    this.purgeExpiredData = parameters.purgeExpiredData();
    this.localDateNow = Objects.requireNonNull(localDateNow);
    // Force commit so that snapshot initializes
    commitTimetableSnapshot(true);
  }

  /**
   * @return an up-to-date snapshot of real-time data. This snapshot and the timetable objects it
   * references are guaranteed to never change, so the requesting thread is
   * provided a consistent view of all TripTimes. The routing thread need only release its reference
   * to the snapshot to release resources.
   */
  public TimetableSnapshot getTimetableSnapshot() {
    return snapshot.get();
  }

  /**
   * @return the current timetable snapshot buffer that contains pending changes (not yet published
   * in a snapshot).
   * This should be used in the context of an updater to build a TransitEditorService that sees all
   * the changes applied so far by real-time updates.
   */
  public TimetableSnapshot getTimetableSnapshotBuffer() {
    return buffer;
  }

  /**
   * Request a commit of the timetable snapshot.
   * 

* If there are no updates buffered up or not enough time has elapsed, the existing snapshot * is returned. * * @param force Force the committing of a new snapshot even if the above conditions are not met. */ void commitTimetableSnapshot(final boolean force) { if (force || buffer.isDirty()) { LOG.debug("Committing {}", buffer); snapshot.publish(buffer.commit(realtimeRaptorTransitDataUpdater, force)); } else { LOG.debug("Buffer was unchanged, keeping old snapshot."); } } /** * 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. * * @param tripId trip id * @param serviceDate service date * @return trip pattern created by the updater; null if pattern has not been changed for this trip. */ @Nullable public TripPattern getNewTripPatternForModifiedTrip(FeedScopedId tripId, LocalDate serviceDate) { return buffer.getNewTripPatternForModifiedTrip(tripId, serviceDate); } /** * Make a snapshot after each message in anticipation of incoming requests. * Purge data if necessary (and force new snapshot if anything was purged). * Make sure that the public (locking) getTimetableSnapshot function is not called. */ public void purgeAndCommit() { if (purgeExpiredData) { final boolean modified = purgeExpiredData(); commitTimetableSnapshot(modified); } else { commitTimetableSnapshot(false); } } /** * If a previous realtime update has changed which trip pattern is associated with the given trip * on the given service date, this method will dissociate the trip from that pattern and remove * the trip's timetables from that pattern on that particular service date. *

* For this service date, the trip will revert to its original trip pattern from the scheduled * data, remaining on that pattern unless it's changed again by a future realtime update. */ public void revertTripToScheduledTripPattern(FeedScopedId tripId, LocalDate serviceDate) { buffer.revertTripToScheduledTripPattern(tripId, serviceDate); } /** * Remove realtime data from previous service dates from the snapshot. This is useful so that * instances that run for multiple days don't accumulate a lot of realtime data for past * dates which would increase memory consumption. * If your OTP instances are restarted throughout the day, this is less useful and can be * turned off. * * @return true if any data has been modified and false if no purging has happened. */ private boolean purgeExpiredData() { final LocalDate today = localDateNow.get(); // TODO: Base this on numberOfDaysOfLongestTrip for tripPatterns final LocalDate previously = today.minusDays(2); // Just to be safe... // Purge data only if we have changed date if (lastPurgeDate != null && lastPurgeDate.compareTo(previously) >= 0) { return false; } LOG.debug("Purging expired realtime data"); lastPurgeDate = previously; return buffer.purgeExpiredData(previously); } /** * Clear all data of snapshot for the provided feed id */ public void clearBuffer(String feedId) { buffer.clear(feedId); } /** * Update the TripTimes of one Trip in a Timetable of a TripPattern. If the Trip of the TripTimes * does not exist yet in the Timetable, add it. This method will make a protective copy of the * Timetable if such a copy has not already been made while building up this snapshot, handling * both cases where patterns were pre-existing in static data or created by realtime data. * * @return whether the update was actually applied */ public Result updateBuffer(RealTimeTripUpdate realTimeTripUpdate) { return buffer.update(realTimeTripUpdate); } /** * Returns an updated timetable for the specified pattern if one is available in this snapshot, or * the originally scheduled timetable if there are no updates in this snapshot. */ public Timetable resolve(TripPattern pattern, LocalDate serviceDate) { return buffer.resolve(pattern, serviceDate); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy