Skip to content

Commit 9ba72c3

Browse files
authored
Merge pull request #947 from conveyal/LinkageCache-javadoc
Update javadoc to reflect recent LinkageCache changes
2 parents e5a9a26 + 5176cba commit 9ba72c3

File tree

3 files changed

+44
-40
lines changed

3 files changed

+44
-40
lines changed

src/main/java/com/conveyal/r5/analyst/LinkageCache.java

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,34 @@
1616
import java.util.concurrent.ExecutionException;
1717

1818
/**
19-
* Retains linkages between PointSets and the StreetLayers for specific StreetModes.
20-
* This used to be embedded in the PointSets themselves, now there should be one instance per TransportNetwork.
19+
* Retains linkages between PointSets and the StreetLayers for specific StreetModes, including egress distance tables.
20+
* LinkageCaches used to be associated with individual PointSets, but now there is a single cache per TransportNetwork.
2121
* There could instead be one instance per AnalystWorker or per JVM (static), but this would cause the mappings
2222
* including PointSets, StreetLayers, and Linkages (which hold references to the TransportNetwork) to stick around
23-
* even when we try to garbage collect a TransportNetwork. This is less of an issue now that we don't plan to have
24-
* workers migrate between TransportNetworks.
23+
* even when we try to garbage collect a TransportNetwork. In cloud operation, this problem would not necessarily arise
24+
* in practice since workers are permanently associated with a single base TransportNetwork.
2525
*/
2626
public class LinkageCache {
2727

2828
private static final Logger LOG = LoggerFactory.getLogger(LinkageCache.class);
2929

3030
/**
31-
* Maximum number of street network linkages to cache per PointSet. This is a crude way of limiting memory
32-
* consumption, and should eventually be replaced with a WeighingCache. Since every Scenario including the
33-
* baseline has its own StreetLayer instance now, this means we can hold walk, bike, and car linkages (with
34-
* distance tables) for 2 scenarios plus the baseline at once.
35-
* FIXME this used to be per-PointSet, now it's one single limit per TransportNetwork.
31+
* Maximum number of street network linkages and associated egress tables to retain in this LinkageCache.
32+
* This is a crude way of limiting memory consumption, and would ideally be replaced with a WeighingCache.
33+
* However, the memory consumption of a particular linkage is difficult to quantify, as the bulk of the data
34+
* is distance tables, and multiple linkages may share a large number of references to reused distance tables.
35+
* Since every Scenario including the baseline has its own StreetLayer instance, we could for example hold linkages
36+
* (with associated distance tables) for walk, bike, and car egress for 2 scenarios plus the baseline at once.
3637
*/
3738
public static int LINKAGE_CACHE_SIZE = 9;
3839

3940
/**
40-
* When this PointSet is connected to the street network, the resulting data are cached in this Map to speed up
41-
* later reuse. Different linkages are produced for different street networks and for different on-street modes
42-
* of travel. At first we were careful to key this cache on the StreetNetwork itself (rather than the
43-
* TransportNetwork or Scenario) to ensure that linkages were re-used for multiple scenarios that have the same
44-
* street network. However, selectively re-linking to the street network is now usually fast, and
45-
* StreetNetworks must be copied for every scenario due to references to their containing TransportNetwork.
41+
* For a particular TransportNetwork, a different linkage is produced for each unique combination of destination
42+
* points, StreetLayer, and on-street mode of travel (see details of Key). A distinct StreetLayer instance exists
43+
* for each scenario even when its contents remain unchanged by the scenario, because the StreetLayer references
44+
* the enclosing TransportNetwork for the scenario.
4645
* Note that this cache will be serialized with the PointSet, but serializing a Guava cache only serializes the
47-
* cache instance and its settings, not the contents of the cache. We consider this sane behavior.
46+
* cache instance and its settings, not the contents of the cache. This is the intended behavior.
4847
*/
4948
protected transient LoadingCache<Key, LinkedPointSet> linkageCache;
5049

@@ -59,24 +58,24 @@ public class LinkageCache {
5958
/**
6059
* The logic for lazy-loading linkages into the cache.
6160
*
62-
// FIXME FIXME clean up these notes on sub-linkages.
63-
// We know that pointSet is a WebMercatorGridPointSet, but if it's a new one we want to replicate its
64-
// linkages based on the base scenarioNetwork.gridPointSet's linkages. We don't know if it's new, so such
65-
// logic has to happen in the loop below over all streetModes, where we fetch and build the egress cost
66-
// tables. We already know for sure this is a scenarioNetwork.
67-
// So if we see a linkage for scenarioNetwork.gridPointSet, we need to add another linkage.
68-
// When this mapping exists:
69-
// (scenarioNetwork.gridPointSet, StreetLayer, StreetMode) -> linkageFor(scenarioNetwork.gridPointSet)
70-
// We need to generate this mapping:
71-
// (pointSet, StreetLayer, StreetMode) -> new LinkedPointSet(linkageFor(scenarioNetwork.gridPointSet), pointSet);
72-
// Note that: ((WebMercatorGridPointSet)pointSet).base == scenarioNetwork.gridPointSet
73-
// I'm afraid BaseLinkage means two different things here: we can subset bigger linkages that already
74-
// exist, or we can redo subsets of linkages of the same size them them when applying scenarios.
75-
// Yes: in one situation, the PointSet objects are identical when making the new linkage, but the
76-
// streetLayer differs. In the other situation, the PointSet objects are different but the other aspects
77-
// are the same. Again this is the difference between a PointSet and its linkage. We should call them
78-
// PointSetLinkages instead of LinkedPointSets because they do not subclass PointSet.
79-
// basePointSet vs. baseStreetLayer vs. baseLinkage.
61+
* FIXME clean up these notes on sub-linkages, some of which may be obsolete.
62+
* We know that pointSet is a WebMercatorGridPointSet, but if it's a new one we want to replicate its
63+
* linkages based on the base scenarioNetwork.gridPointSet's linkages. We don't know if it's new, so such
64+
* logic has to happen in the loop below over all streetModes, where we fetch and build the egress cost
65+
* tables. We already know for sure this is a scenarioNetwork.
66+
* So if we see a linkage for scenarioNetwork.gridPointSet, we need to add another linkage.
67+
* When this mapping exists:
68+
* (scenarioNetwork.gridPointSet, StreetLayer, StreetMode) -> linkageFor(scenarioNetwork.gridPointSet)
69+
* We need to generate this mapping:
70+
* (pointSet, StreetLayer, StreetMode) -> new LinkedPointSet(linkageFor(scenarioNetwork.gridPointSet), pointSet);
71+
* Note that: ((WebMercatorGridPointSet)pointSet).base == scenarioNetwork.gridPointSet
72+
* I'm afraid BaseLinkage means two different things here: we can subset bigger linkages that already
73+
* exist, or we can redo subsets of linkages of the same size when applying scenarios.
74+
* Yes: in one situation, the PointSet objects are identical when making the new linkage, but the
75+
* streetLayer differs. In the other situation, the PointSet objects are different but the other aspects
76+
* are the same. Again this is the difference between a PointSet and its linkage. We should call them
77+
* PointSetLinkages instead of LinkedPointSets because they do not subclass PointSet.
78+
* basePointSet vs. baseStreetLayer vs. baseLinkage.
8079
*/
8180
private class LinkageCacheLoader extends CacheLoader<Key, LinkedPointSet> implements Serializable {
8281
@Override

src/main/java/com/conveyal/r5/analyst/NetworkPreloader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ public LoaderState<TransportNetwork> preloadData (AnalysisWorkerTask task) {
8888

8989
/**
9090
* A blocking way to ensure the network and all linkages and precomputed tables are prepared in advance of routing.
91-
* Note that this does not perform any blocking or locking of its own - any synchronization will be that of the
92-
* underlying caches (synchronized methods on TransportNetworkCache or LinkedPointSet). It also bypasses the
91+
* Note that this does not perform any blocking or locking of its own. Any synchronization or turn-taking will be
92+
* that of the underlying caches (TransportNetworkCache or LinkageCache). It also bypasses the
9393
* AsyncLoader locking that would usually allow only one buildValue operation at a time. All threads that call with
9494
* similar tasks will make interleaved calls to setProgress (with superficial map synchronization). Other than
9595
* causing a value to briefly revert from PRESENT to BUILDING this doesn't seem deeply problematic.

src/main/java/com/conveyal/r5/analyst/cluster/AnalysisWorker.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,15 @@ protected void handleOneRegionalTask (RegionalTask task) throws Throwable {
458458
}
459459

460460
// Pull all necessary inputs into cache in a blocking fashion, unlike single-point tasks where prep is async.
461-
// Avoids auto-shutdown while preloading. Must be done after loading destination pointsets to establish extents.
462-
// Note we're completely bypassing the async loader here and relying on the older nested LoadingCaches.
463-
// If those are ever removed, the async loader will need a synchronous mode with per-path blocking (kind of
464-
// reinventing the wheel of LoadingCache) or we'll need to make preparation for regional tasks async.
461+
// This is because single point tasks return fast to report progress, while regional tasks currently do not.
462+
// Worker auto-shutdown time should remain very high during this blocking preload step. Destination point sets
463+
// must already be loaded to establish extents before the preloading step begins. Note that we're still using
464+
// the NetworkPreloader which is an AsyncLoader, but calling a method that intentionally skips all the async or
465+
// background proccessing machinery. Usually, N RegionalTasks all try to preload at once, and all block on
466+
// this method. Redundant slow calculation is not prevented by the AsyncLoader class itself, but by the other
467+
// LoadingCaches behind it. Specifically, the TransportNetworkCache and LinkageCache enforce turn-taking and
468+
// prevent redundant work. If those are ever removed, we would need either async regional task preparation, or
469+
// a synchronous mode with per-key blocking on AsyncLoader (kind of reinventing the wheel of LoadingCache).
465470
TransportNetwork transportNetwork = networkPreloader.synchronousPreload(task);
466471

467472
// If we are generating a static site, there must be a single metadata file for an entire batch of results.

0 commit comments

Comments
 (0)