org.opentripplanner.updater.GraphUpdaterManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.updater;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.opentripplanner.routing.graph.Graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* This class is attached to the graph:
*
*
* GraphUpdaterManager updaterManager = graph.getUpdaterManager();
*
*
* Each updater will run in its own thread. When changes to the graph have to be made by these
* updaters, this should be done via the execute method of this manager to prevent race conditions
* between graph write operations.
*
*/
public class GraphUpdaterManager {
private static Logger LOG = LoggerFactory.getLogger(GraphUpdaterManager.class);
/**
* Text used for naming threads when the graph lacks a routerId.
*/
private static String DEFAULT_ROUTER_ID = "(default)";
/**
* Thread factory used to create new threads, giving them more human-readable names including the routerId.
*/
private ThreadFactory threadFactory;
/**
* OTP's multi-version concurrency control model for graph updating allows simultaneous reads,
* but never simultaneous writes. We ensure this policy is respected by having a single writer
* thread, which sequentially executes all graph updater tasks. Each task is a runnable that is
* scheduled with the ExecutorService to run at regular intervals.
* FIXME: In reality we're not using scheduleAtFixedInterval.
* We're scheduling for immediate execution from separate threads that sleep in a loop.
* We should perhaps switch to having polling GraphUpdaters call scheduleAtFixedInterval.
*/
private ScheduledExecutorService scheduler;
/**
* A pool of threads on which the updaters will run.
* This creates a pool that will auto-scale up to any size (maximum pool size is MAX_INT).
* FIXME The polling updaters occupy an entire thread, sleeping in between polling operations.
*/
private ExecutorService updaterPool;
/**
* Keep track of all updaters so we can cleanly free resources associated with them at shutdown.
*/
private List updaterList = new ArrayList<>();
/**
* The Graph that will be updated.
*/
private Graph graph;
/**
* Constructor.
* @param graph is the Graph that will be updated.
*/
public GraphUpdaterManager(Graph graph) {
this.graph = graph;
String routerId = graph.routerId;
if(routerId == null || routerId.isEmpty())
routerId = DEFAULT_ROUTER_ID;
threadFactory = new ThreadFactoryBuilder().setNameFormat("GraphUpdater-" + routerId + "-%d").build();
scheduler = Executors.newSingleThreadScheduledExecutor(threadFactory);
updaterPool = Executors.newCachedThreadPool(threadFactory);
}
public GraphUpdaterManager(Graph graph, List updaters) {
this(graph);
for (GraphUpdater updater : updaters) {
this.addUpdater(updater);
updater.setGraphUpdaterManager(this);
}
}
public void stop() {
// TODO: find a better way to stop these threads
// Shutdown updaters
updaterPool.shutdownNow();
try {
boolean ok = updaterPool.awaitTermination(30, TimeUnit.SECONDS);
if (!ok) {
LOG.warn("Timeout waiting for updaters to finish.");
}
} catch (InterruptedException e) {
// This should not happen
LOG.warn("Interrupted while waiting for updaters to finish.");
}
// Clean up updaters
for (GraphUpdater updater : updaterList) {
updater.teardown();
}
updaterList.clear();
// Shutdown scheduler
scheduler.shutdownNow();
try {
boolean ok = scheduler.awaitTermination(30, TimeUnit.SECONDS);
if (!ok) {
LOG.warn("Timeout waiting for scheduled task to finish.");
}
} catch (InterruptedException e) {
// This should not happen
LOG.warn("Interrupted while waiting for scheduled task to finish.");
}
}
/**
* Adds an updater to the manager and runs it immediately in its own thread.
*
* @param updater is the updater to add and run
*/
public void addUpdater(final GraphUpdater updater) {
updaterList.add(updater);
}
/**
* This is the method to use to modify the graph from the updaters. The runnables will be
* scheduled after each other, guaranteeing that only one of these runnables will be active at
* any time. If a particular GraphUpdater calls this method on more than one GraphWriterRunnable, they should be
* executed in the same order that GraphUpdater made the calls.
*
* @param runnable is a graph writer runnable
*/
public void execute(GraphWriterRunnable runnable) {
scheduler.submit(() -> {
try {
runnable.run(graph);
} catch (Exception e) {
LOG.error("Error while running graph writer {}:", runnable.getClass().getName(), e);
}
});
}
public int size() {
return updaterList.size();
}
/**
* This should be called only once at startup to kick off every updater in its own thread, and only after all
* the updaters have had their setup methods called.
*/
public void startUpdaters() {
for (GraphUpdater updater : updaterList) {
LOG.info("Starting new thread for updater {}", updater.toString());
updaterPool.execute(() -> {
try {
updater.run();
} catch (Exception e) {
LOG.error("Error while running updater {}:", updater.getClass().getName(), e);
}
});
}
}
/**
* Just an example of fetching status information from the graph updater manager to expose it in a web service.
* More useful stuff should be added later.
*/
public Map getUpdaterDescriptions () {
Map ret = Maps.newTreeMap();
int i = 0;
for (GraphUpdater updater : updaterList) {
ret.put(i++, updater.toString());
}
return ret;
}
/**
* Just an example of fetching status information from the graph updater manager to expose it in a web service.
* More useful stuff should be added later.
*/
public GraphUpdater getUpdater (int id) {
if (id >= updaterList.size()) return null;
return updaterList.get(id);
}
public List getUpdaterList() {
return updaterList;
}
public Collection waitingUpdaters() {
Collection waitingUpdaters = new ArrayList<>();
for (GraphUpdater updater : graph.updaterManager.getUpdaterList()) {
if (!(updater).isPrimed()) {
waitingUpdaters.add(updater.getConfigRef());
}
}
return waitingUpdaters;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy