Skip to content

Commit dabcf1f

Browse files
committed
Merge branch 'dev' for release
2 parents a528745 + 9cdd402 commit dabcf1f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1099
-321
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
**Describe the bug**
11+
A clear and concise description of what the bug is.
12+
13+
**To Reproduce**
14+
Steps to reproduce the behavior:
15+
1. Go to '...'
16+
2. Click on '....'
17+
3. Scroll down to '....'
18+
4. See error
19+
20+
**Expected behavior**
21+
A clear and concise description of what you expected to happen.
22+
23+
**Screenshots**
24+
If applicable, add screenshots to help explain your problem.
25+
26+
**Desktop (please complete the following information):**
27+
- OS: [e.g. iOS]
28+
- Browser [e.g. chrome, safari]
29+
- Version [e.g. 22]
30+
31+
**Smartphone (please complete the following information):**
32+
- Device: [e.g. iPhone6]
33+
- OS: [e.g. iOS8.1]
34+
- Browser [e.g. stock browser, safari]
35+
- Version [e.g. 22]
36+
37+
**Additional context**
38+
Add any other context about the problem here.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
**Is your feature request related to a problem? Please describe.**
11+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12+
13+
**Describe the solution you'd like**
14+
A clear and concise description of what you want to happen.
15+
16+
**Describe alternatives you've considered**
17+
A clear and concise description of any alternative solutions or features you've considered.
18+
19+
**Additional context**
20+
Add any other context or screenshots about the feature request here.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# This file was created by CodeQL for this repository. The only change was
2+
# removing 'python' and 'javascript' from the language array.
3+
#
4+
# For most projects, this workflow file will not need changing; you simply need
5+
# to commit it to your repository.
6+
#
7+
# You may wish to alter this file to override the set of languages analyzed,
8+
# or to provide custom queries or build logic.
9+
#
10+
# ******** NOTE ********
11+
# We have attempted to detect the languages in your repository. Please check
12+
# the `language` matrix defined below to confirm you have the correct set of
13+
# supported CodeQL languages.
14+
#
15+
name: "CodeQL"
16+
17+
on:
18+
push:
19+
branches: [ dev ]
20+
pull_request:
21+
# The branches below must be a subset of the branches above
22+
branches: [ dev ]
23+
schedule:
24+
- cron: '24 12 * * 1'
25+
26+
jobs:
27+
analyze:
28+
name: Analyze
29+
runs-on: ubuntu-latest
30+
permissions:
31+
actions: read
32+
contents: read
33+
security-events: write
34+
35+
strategy:
36+
fail-fast: false
37+
matrix:
38+
language: [ 'java' ]
39+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
40+
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
41+
42+
steps:
43+
- name: Checkout repository
44+
uses: actions/checkout@v3
45+
46+
# Initializes the CodeQL tools for scanning.
47+
- name: Initialize CodeQL
48+
uses: github/codeql-action/init@v2
49+
with:
50+
languages: ${{ matrix.language }}
51+
# If you wish to specify custom queries, you can do so here or in a config file.
52+
# By default, queries listed here will override any specified in a config file.
53+
# Prefix the list here with "+" to use these queries and those in the config file.
54+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
55+
56+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57+
# If this step fails, then you should remove it and run the build manually (see below)
58+
- name: Autobuild
59+
uses: github/codeql-action/autobuild@v2
60+
61+
# ℹ️ Command-line programs to run using the OS shell.
62+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63+
64+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
65+
# and modify them (or add more) to build your code if your project
66+
# uses a compiled language
67+
68+
#- run: |
69+
# make bootstrap
70+
# make release
71+
72+
- name: Perform CodeQL Analysis
73+
uses: github/codeql-action/analyze@v2

build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,8 @@ repositories {
123123
// Put Open Source Geospatial before Maven Central to get JAI core, see https://stackoverflow.com/a/26993223
124124
maven { url 'https://repo.osgeo.org/repository/release/' }
125125
mavenCentral()
126-
// TODO review whether we really need these repositories
126+
// TODO review whether we really need the repositories below
127127
maven { url 'https://maven.conveyal.com' }
128-
// Used for importing java projects from github (why do we need this?)
129-
maven { url 'https://jitpack.io' }
130128
// For the polyline encoder
131129
maven { url 'https://nexus.axiomalaska.com/nexus/content/repositories/public-releases' }
132130
}

src/main/java/com/conveyal/analysis/components/broker/Broker.java

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.conveyal.analysis.components.broker;
22

3-
import com.conveyal.analysis.AnalysisServerException;
43
import com.conveyal.analysis.components.Component;
54
import com.conveyal.analysis.components.WorkerLauncher;
65
import com.conveyal.analysis.components.eventbus.ErrorEvent;
@@ -108,7 +107,8 @@ public interface Config {
108107
* Used when auto-starting spot instances. Set to a smaller value to increase the number of
109108
* workers requested automatically
110109
*/
111-
public final int TARGET_TASKS_PER_WORKER = 800;
110+
public final int TARGET_TASKS_PER_WORKER_TRANSIT = 800;
111+
public final int TARGET_TASKS_PER_WORKER_NONTRANSIT = 4_000;
112112

113113
/**
114114
* We want to request spot instances to "boost" regional analyses after a few regional task
@@ -243,28 +243,54 @@ public void createOnDemandWorkerInCategory(WorkerCategory category, WorkerTags w
243243
/**
244244
* Create on-demand/spot workers for a given job, after certain checks
245245
* @param nOnDemand EC2 on-demand instances to request
246-
* @param nSpot EC2 spot instances to request
246+
* @param nSpot Target number of EC2 spot instances to request. The actual number requested may be lower if the
247+
* total number of workers running is approaching the maximum specified in the Broker config.
247248
*/
248249
public void createWorkersInCategory (WorkerCategory category, WorkerTags workerTags, int nOnDemand, int nSpot) {
249250

251+
// Log error messages rather than throwing exceptions, as this code often runs in worker poll handlers.
252+
// Throwing an exception there would not report any useful information to anyone.
253+
250254
if (config.offline()) {
251-
LOG.info("Work offline enabled, not creating workers for {}", category);
255+
LOG.info("Work offline enabled, not creating workers for {}.", category);
256+
return;
257+
}
258+
259+
if (nOnDemand < 0 || nSpot < 0) {
260+
LOG.error("Negative number of workers requested, not starting any.");
261+
return;
262+
}
263+
264+
final int nRequested = nOnDemand + nSpot;
265+
if (nRequested <= 0) {
266+
LOG.error("No workers requested, not starting any.");
252267
return;
253268
}
254269

255-
if (nOnDemand < 0 || nSpot < 0){
256-
LOG.info("Negative number of workers requested, not starting any");
270+
// Zeno's worker pool management: never start more than half the remaining capacity.
271+
final int remainingCapacity = config.maxWorkers() - workerCatalog.totalWorkerCount();
272+
final int maxToStart = remainingCapacity / 2;
273+
if (maxToStart <= 0) {
274+
LOG.error("Due to capacity limiting, not starting any workers.");
257275
return;
258276
}
259277

260-
if (workerCatalog.totalWorkerCount() + nOnDemand + nSpot >= config.maxWorkers()) {
261-
String message = String.format(
262-
"Maximum of %d workers already started, not starting more;" +
263-
"jobs will not complete on %s",
264-
config.maxWorkers(),
265-
category
278+
if (nRequested > maxToStart) {
279+
LOG.warn("Request for {} workers is more than half the remaining worker pool capacity.", nRequested);
280+
nSpot = maxToStart;
281+
nOnDemand = 0;
282+
LOG.warn("Lowered to {} on-demand and {} spot workers.", nOnDemand, nSpot);
283+
}
284+
285+
// Just an assertion for consistent state - this should never happen.
286+
// Re-sum nOnDemand + nSpot here instead of using nTotal, as they may have been revised.
287+
if (workerCatalog.totalWorkerCount() + nOnDemand + nSpot > config.maxWorkers()) {
288+
LOG.error(
289+
"Starting workers would exceed the maximum capacity of {}. Jobs may stall on {}.",
290+
config.maxWorkers(),
291+
category
266292
);
267-
throw AnalysisServerException.forbidden(message);
293+
return;
268294
}
269295

270296
// If workers have already been started up, don't repeat the operation.
@@ -483,9 +509,27 @@ private void requestExtraWorkersIfAppropriate(Job job) {
483509
WorkerCategory workerCategory = job.workerCategory;
484510
int categoryWorkersAlreadyRunning = workerCatalog.countWorkersInCategory(workerCategory);
485511
if (categoryWorkersAlreadyRunning < MAX_WORKERS_PER_CATEGORY) {
486-
// Start a number of workers that scales with the number of total tasks, up to a fixed number.
487-
// TODO more refined determination of number of workers to start (e.g. using tasks per minute)
488-
int targetWorkerTotal = Math.min(MAX_WORKERS_PER_CATEGORY, job.nTasksTotal / TARGET_TASKS_PER_WORKER);
512+
// TODO more refined determination of number of workers to start (e.g. using observed tasks per minute
513+
// for recently completed tasks -- but what about when initial origins are in a desert/ocean?)
514+
int targetWorkerTotal;
515+
if (job.templateTask.hasTransit()) {
516+
// Total computation for a task with transit depends on the number of stops and whether the
517+
// network has frequency-based routes. The total computation for the job depends on these
518+
// factors as well as the number of tasks (origins). Zoom levels add a complication: the number of
519+
// origins becomes an even poorer proxy for the number of stops. We use a scale factor to compensate
520+
// -- all else equal, high zoom levels imply fewer stops per origin (task) and a lower ideal target
521+
// for number of workers. TODO reduce scale factor further when there are no frequency routes. But is
522+
// this worth adding a field to Job or RegionalTask?
523+
float transitScaleFactor = (9f / job.templateTask.zoom);
524+
targetWorkerTotal = (int) ((job.nTasksTotal / TARGET_TASKS_PER_WORKER_TRANSIT) * transitScaleFactor);
525+
} else {
526+
// Tasks without transit are simpler. They complete relatively quickly, and the total computation for
527+
// the job increases roughly with linearly with the number of origins.
528+
targetWorkerTotal = job.nTasksTotal / TARGET_TASKS_PER_WORKER_NONTRANSIT;
529+
}
530+
531+
// Do not exceed the limit on workers per category TODO add similar limit per accessGroup or user
532+
targetWorkerTotal = Math.min(targetWorkerTotal, MAX_WORKERS_PER_CATEGORY);
489533
// Guardrail until freeform pointsets are tested more thoroughly
490534
if (job.templateTask.originPointSet != null) targetWorkerTotal = Math.min(targetWorkerTotal, 5);
491535
int nSpot = targetWorkerTotal - categoryWorkersAlreadyRunning;

src/main/java/com/conveyal/analysis/controllers/WorkerProxyController.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
import java.net.http.HttpRequest;
2424
import java.net.http.HttpResponse;
2525

26-
import static com.conveyal.analysis.util.HttpStatus.OK_200;
27-
2826
/**
2927
* This proxies requests coming from the UI over to any currently active worker for the specified network bundle.
3028
* This could be used for point-to-point routing or the existing R5 endpoints producing debug tiles of the graph.
@@ -104,7 +102,7 @@ private Object proxyRequest (Request request, Response response) throws IOExcept
104102
resp.headers().map().forEach((key, value) -> {
105103
if (!value.isEmpty()) response.header(key, value.get(0));
106104
});
107-
response.status(OK_200);
105+
response.status(resp.statusCode());
108106
return resp.body();
109107
} catch (Exception exception) {
110108
response.status(HttpStatus.BAD_REQUEST_400);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public static FreeFormPointSet fromCsv (
123123
return ret;
124124
} catch (NumberFormatException nfe) {
125125
throw new ParameterException(
126-
String.format("Improperly formatted floating point value on line %d of CSV input", rec)
126+
String.format("Improperly formatted floating point value near line %d of CSV input", rec + 1)
127127
);
128128
}
129129
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.time.format.DateTimeFormatter;
3939
import java.util.ArrayList;
4040
import java.util.Arrays;
41-
import java.util.Collections;
4241
import java.util.List;
4342
import java.util.Random;
4443
import java.util.UUID;
@@ -348,7 +347,7 @@ private byte[] singlePointResultToBinary (
348347
oneOriginResult.accessibility,
349348
transportNetwork.scenarioApplicationWarnings,
350349
transportNetwork.scenarioApplicationInfo,
351-
oneOriginResult.paths
350+
oneOriginResult.paths != null ? new PathResultSummary(oneOriginResult.paths, transportNetwork.transitLayer) : null
352351
);
353352
}
354353
// Single-point tasks don't have a job ID. For now, we'll categorize them by scenario ID.
@@ -488,7 +487,7 @@ public static class GridJsonBlock {
488487

489488
public List<TaskError> scenarioApplicationInfo;
490489

491-
public List<PathResult.PathIterations> pathSummaries;
490+
public PathResultSummary pathSummaries;
492491

493492
@Override
494493
public String toString () {
@@ -515,7 +514,7 @@ public static void addJsonToGrid (
515514
AccessibilityResult accessibilityResult,
516515
List<TaskError> scenarioApplicationWarnings,
517516
List<TaskError> scenarioApplicationInfo,
518-
PathResult pathResult
517+
PathResultSummary pathResult
519518
) throws IOException {
520519
var jsonBlock = new GridJsonBlock();
521520
jsonBlock.scenarioApplicationInfo = scenarioApplicationInfo;
@@ -526,7 +525,7 @@ public static void addJsonToGrid (
526525
// study area). But we'd need to control the number of decimal places serialized into the JSON.
527526
jsonBlock.accessibility = accessibilityResult.getIntValues();
528527
}
529-
jsonBlock.pathSummaries = pathResult == null ? Collections.EMPTY_LIST : pathResult.getPathIterationsForDestination();
528+
jsonBlock.pathSummaries = pathResult;
530529
LOG.debug("Travel time surface written, appending {}.", jsonBlock);
531530
// We could do this when setting up the Spark handler, supplying writeValue as the response transformer
532531
// But then you also have to handle the case where you are returning raw bytes.

0 commit comments

Comments
 (0)