stac-api-load-testing 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/PKG-INFO +49 -15
  2. stac_api_load_testing-0.2.0/stac_api_load_testing.egg-info/PKG-INFO → stac_api_load_testing-0.3.0/README.md +37 -22
  3. stac_api_load_testing-0.3.0/pyproject.toml +35 -0
  4. stac_api_load_testing-0.3.0/stac_api_load_testing/__init__.py +0 -0
  5. stac_api_load_testing-0.3.0/stac_api_load_testing/cli.py +308 -0
  6. stac_api_load_testing-0.3.0/stac_api_load_testing/config_files/locustfile.py +402 -0
  7. stac_api_load_testing-0.3.0/stac_api_load_testing/config_files/taurus_locust.yml +14 -0
  8. stac_api_load_testing-0.3.0/stac_api_load_testing/data_loader/__init__.py +1 -0
  9. stac_api_load_testing-0.3.0/stac_api_load_testing/data_loader/data_loader.py +60 -0
  10. stac_api_load_testing-0.3.0/stac_api_load_testing/data_loader/setup_data/collection.json +194 -0
  11. stac_api_load_testing-0.3.0/stac_api_load_testing/data_loader/setup_data/sentinel-s2-l2a-cogs_0_100.json +1 -0
  12. stac_api_load_testing-0.3.0/stac_api_load_testing.egg-info/PKG-INFO +114 -0
  13. stac_api_load_testing-0.3.0/stac_api_load_testing.egg-info/SOURCES.txt +17 -0
  14. stac_api_load_testing-0.3.0/stac_api_load_testing.egg-info/requires.txt +8 -0
  15. stac_api_load_testing-0.2.0/README.md +0 -61
  16. stac_api_load_testing-0.2.0/setup.py +0 -35
  17. stac_api_load_testing-0.2.0/stac_api_load_testing/cli.py +0 -143
  18. stac_api_load_testing-0.2.0/stac_api_load_testing.egg-info/SOURCES.txt +0 -10
  19. stac_api_load_testing-0.2.0/stac_api_load_testing.egg-info/requires.txt +0 -8
  20. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/LICENSE +0 -0
  21. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/setup.cfg +0 -0
  22. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/stac_api_load_testing.egg-info/dependency_links.txt +0 -0
  23. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/stac_api_load_testing.egg-info/entry_points.txt +0 -0
  24. {stac_api_load_testing-0.2.0 → stac_api_load_testing-0.3.0}/stac_api_load_testing.egg-info/top_level.txt +0 -0
@@ -1,31 +1,44 @@
1
- Metadata-Version: 2.1
2
- Name: stac_api_load_testing
3
- Version: 0.2.0
1
+ Metadata-Version: 2.4
2
+ Name: stac-api-load-testing
3
+ Version: 0.3.0
4
4
  Summary: Load testing for stac compliant apis
5
- Home-page: https://github.com/Healy-Hyperspatial/stac-api-load-testing
6
- Author: Jonathan Healy
7
- Author-email: jonathan.d.healy@gmail.com
5
+ Author-email: Jonathan Healy <jonathan.d.healy@gmail.com>
8
6
  License: MIT
9
- Requires-Python: >=3.7
7
+ Project-URL: Homepage, https://github.com/Healy-Hyperspatial/stac-api-load-testing
8
+ Requires-Python: >=3.11
10
9
  Description-Content-Type: text/markdown
11
10
  License-File: LICENSE
12
- Requires-Dist: click>=7.1.2
13
- Requires-Dist: locust
14
- Requires-Dist: setuptools
15
- Requires-Dist: Cython
16
- Requires-Dist: bzt
11
+ Requires-Dist: click>=8.1
12
+ Requires-Dist: locust<3,>=2.32
13
+ Requires-Dist: requests>=2.31
14
+ Requires-Dist: pyyaml>=6.0
15
+ Requires-Dist: bzt>=1.17.0
17
16
  Provides-Extra: test
18
17
  Requires-Dist: pytest; extra == "test"
18
+ Dynamic: license-file
19
19
 
20
20
  # stac-api-load-testing
21
21
  Taurus and Locust load testing tools for stac-api backends.
22
22
 
23
23
  ![Alt text](readme_files/taurus-pgstac.png?raw=true "stac-fastapi-pgstac")
24
24
 
25
+ Requires Python >= 3.11.
25
26
 
26
- ## Run stac-fastapi backends locally (elasticsearch|opensearch|pgstac|mongo)
27
- ```$ docker-compose up elasticsearch```
28
- ```$ docker-compose up app-elasticsearch```
27
+ ## Run stac-fastapi backends locally (elasticsearch|opensearch|pgstac|mongo|stac-os-rs)
28
+ ```$ docker compose up elasticsearch```
29
+ ```$ docker compose up app-elasticsearch```
30
+
31
+ Backend ports: pgstac `:8083`, elasticsearch `:8084`, opensearch `:8085`, mongo `:8086`, stac-os-rs `:8087`
32
+
33
+ | backend | app image | database image |
34
+ | --- | --- | --- |
35
+ | pgstac | stac-fastapi.pgstac 7.2.0 (built locally) | ghcr.io/stac-utils/pgstac:v0.10.0 |
36
+ | elasticsearch | ghcr.io/stac-utils/stac-fastapi-es:v8.1.1 | elasticsearch 9.3.2 |
37
+ | opensearch | ghcr.io/stac-utils/stac-fastapi-os:v8.1.1 | opensearch 3.8.0 |
38
+ | mongo | stac-fastapi.mongo 3.2.0 (built locally) | mongo 7.0 |
39
+ | stac-os-rs | ghcr.io/jonhealy1/stac-os-rs:0.2.0 | opensearch 3.8.0 |
40
+
41
+ All datastores are given a ~2GB memory budget for comparable benchmarks: ES/OpenSearch heaps `-Xms2g -Xmx2g` with `bootstrap.memory_lock`, pgstac `shared_buffers=2GB`, mongo `--wiredTigerCacheSizeGB 2`. Ingest the same data into each backend before comparing (`stac-api-load-testing --ingest --api-address <url>`).
29
42
 
30
43
  ## Install locally for development
31
44
  ```$ pip install -e .```
@@ -62,6 +75,16 @@ Options:
62
75
  -r, --ramp-up TEXT Ramp-up time for Taurus option.
63
76
  -n, --iterations INTEGER Number of iterations for Taurus option.
64
77
  -a, --api-address TEXT Specify the STAC API URL to test against.
78
+ Accepts a comma-separated list for --benchmark.
79
+ -b, --benchmark Run a headless locust benchmark sweep against the
80
+ STAC API.
81
+ -u, --users TEXT Comma-separated concurrency levels for
82
+ --benchmark.
83
+ --spawn-rate INTEGER Users spawned per second for --benchmark.
84
+ --run-time TEXT Duration of each --benchmark run, in locust
85
+ notation (e.g. '30s', '1m').
86
+ -o, --output PATH Write aggregated --benchmark results to a CSV
87
+ file.
65
88
  --version Show the version and exit.
66
89
  --help Show this message and exit.
67
90
  ```
@@ -72,9 +95,20 @@ Options:
72
95
  ## Run Locust Load Testing Ouside of Taurus Wrapper
73
96
  ```$ stac-api-load-testing --locust --api-address http://localhost:8084```
74
97
  - go to ```http://localhost:8089``` and start with desired settings
98
+ - or run headless: ```$ locust --locustfile stac_api_load_testing/config_files/locustfile.py --host http://localhost:8084 --headless -u 10 -r 1 -t 1m```
75
99
 
76
100
  ## Inside of Taurus Wrapper
77
101
  ```$ stac-api-load-testing --taurus --api-address http://localhost:8084```
78
102
 
103
+ ## Benchmark / compare backends
104
+ Runs headless locust sweeps at each concurrency level and prints a comparison
105
+ table. Ingest into each backend first, then run them one at a time:
106
+
107
+ ```$ stac-api-load-testing --benchmark --api-address http://localhost:8085 --users 10,50,100 --run-time 1m```
108
+
109
+ Compare multiple backends in one command (comma-separated addresses):
110
+
111
+ ```$ stac-api-load-testing --benchmark --api-address http://localhost:8085,http://localhost:8087 --users 10,50,100 --run-time 1m -o results.csv```
112
+
79
113
  ## Debugging Locust tasks using a single simulated user
80
114
  ```$ python stac_api_load_testing/config_files/locustfile.py```
@@ -1,31 +1,25 @@
1
- Metadata-Version: 2.1
2
- Name: stac_api_load_testing
3
- Version: 0.2.0
4
- Summary: Load testing for stac compliant apis
5
- Home-page: https://github.com/Healy-Hyperspatial/stac-api-load-testing
6
- Author: Jonathan Healy
7
- Author-email: jonathan.d.healy@gmail.com
8
- License: MIT
9
- Requires-Python: >=3.7
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: click>=7.1.2
13
- Requires-Dist: locust
14
- Requires-Dist: setuptools
15
- Requires-Dist: Cython
16
- Requires-Dist: bzt
17
- Provides-Extra: test
18
- Requires-Dist: pytest; extra == "test"
19
-
20
1
  # stac-api-load-testing
21
2
  Taurus and Locust load testing tools for stac-api backends.
22
3
 
23
4
  ![Alt text](readme_files/taurus-pgstac.png?raw=true "stac-fastapi-pgstac")
24
5
 
6
+ Requires Python >= 3.11.
7
+
8
+ ## Run stac-fastapi backends locally (elasticsearch|opensearch|pgstac|mongo|stac-os-rs)
9
+ ```$ docker compose up elasticsearch```
10
+ ```$ docker compose up app-elasticsearch```
11
+
12
+ Backend ports: pgstac `:8083`, elasticsearch `:8084`, opensearch `:8085`, mongo `:8086`, stac-os-rs `:8087`
13
+
14
+ | backend | app image | database image |
15
+ | --- | --- | --- |
16
+ | pgstac | stac-fastapi.pgstac 7.2.0 (built locally) | ghcr.io/stac-utils/pgstac:v0.10.0 |
17
+ | elasticsearch | ghcr.io/stac-utils/stac-fastapi-es:v8.1.1 | elasticsearch 9.3.2 |
18
+ | opensearch | ghcr.io/stac-utils/stac-fastapi-os:v8.1.1 | opensearch 3.8.0 |
19
+ | mongo | stac-fastapi.mongo 3.2.0 (built locally) | mongo 7.0 |
20
+ | stac-os-rs | ghcr.io/jonhealy1/stac-os-rs:0.2.0 | opensearch 3.8.0 |
25
21
 
26
- ## Run stac-fastapi backends locally (elasticsearch|opensearch|pgstac|mongo)
27
- ```$ docker-compose up elasticsearch```
28
- ```$ docker-compose up app-elasticsearch```
22
+ All datastores are given a ~2GB memory budget for comparable benchmarks: ES/OpenSearch heaps `-Xms2g -Xmx2g` with `bootstrap.memory_lock`, pgstac `shared_buffers=2GB`, mongo `--wiredTigerCacheSizeGB 2`. Ingest the same data into each backend before comparing (`stac-api-load-testing --ingest --api-address <url>`).
29
23
 
30
24
  ## Install locally for development
31
25
  ```$ pip install -e .```
@@ -62,6 +56,16 @@ Options:
62
56
  -r, --ramp-up TEXT Ramp-up time for Taurus option.
63
57
  -n, --iterations INTEGER Number of iterations for Taurus option.
64
58
  -a, --api-address TEXT Specify the STAC API URL to test against.
59
+ Accepts a comma-separated list for --benchmark.
60
+ -b, --benchmark Run a headless locust benchmark sweep against the
61
+ STAC API.
62
+ -u, --users TEXT Comma-separated concurrency levels for
63
+ --benchmark.
64
+ --spawn-rate INTEGER Users spawned per second for --benchmark.
65
+ --run-time TEXT Duration of each --benchmark run, in locust
66
+ notation (e.g. '30s', '1m').
67
+ -o, --output PATH Write aggregated --benchmark results to a CSV
68
+ file.
65
69
  --version Show the version and exit.
66
70
  --help Show this message and exit.
67
71
  ```
@@ -72,9 +76,20 @@ Options:
72
76
  ## Run Locust Load Testing Ouside of Taurus Wrapper
73
77
  ```$ stac-api-load-testing --locust --api-address http://localhost:8084```
74
78
  - go to ```http://localhost:8089``` and start with desired settings
79
+ - or run headless: ```$ locust --locustfile stac_api_load_testing/config_files/locustfile.py --host http://localhost:8084 --headless -u 10 -r 1 -t 1m```
75
80
 
76
81
  ## Inside of Taurus Wrapper
77
82
  ```$ stac-api-load-testing --taurus --api-address http://localhost:8084```
78
83
 
84
+ ## Benchmark / compare backends
85
+ Runs headless locust sweeps at each concurrency level and prints a comparison
86
+ table. Ingest into each backend first, then run them one at a time:
87
+
88
+ ```$ stac-api-load-testing --benchmark --api-address http://localhost:8085 --users 10,50,100 --run-time 1m```
89
+
90
+ Compare multiple backends in one command (comma-separated addresses):
91
+
92
+ ```$ stac-api-load-testing --benchmark --api-address http://localhost:8085,http://localhost:8087 --users 10,50,100 --run-time 1m -o results.csv```
93
+
79
94
  ## Debugging Locust tasks using a single simulated user
80
95
  ```$ python stac_api_load_testing/config_files/locustfile.py```
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "stac-api-load-testing"
7
+ version = "0.3.0"
8
+ description = "Load testing for stac compliant apis"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Jonathan Healy", email = "jonathan.d.healy@gmail.com" }]
13
+ dependencies = [
14
+ "click>=8.1",
15
+ "locust>=2.32,<3",
16
+ "requests>=2.31",
17
+ "pyyaml>=6.0",
18
+ "bzt>=1.17.0",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ test = ["pytest"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/Healy-Hyperspatial/stac-api-load-testing"
26
+
27
+ [project.scripts]
28
+ stac-api-load-testing = "stac_api_load_testing.cli:main"
29
+
30
+ [tool.setuptools.packages.find]
31
+ include = ["stac_api_load_testing*"]
32
+
33
+ [tool.setuptools.package-data]
34
+ stac_api_load_testing = ["config_files/*"]
35
+ "stac_api_load_testing.data_loader" = ["setup_data/*"]
@@ -0,0 +1,308 @@
1
+ """stac-api-load-testing cli tool."""
2
+
3
+ import csv
4
+ import os
5
+ import re
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import tempfile
10
+ from importlib import resources
11
+ from pathlib import Path
12
+
13
+ import click
14
+ import yaml
15
+
16
+ from .data_loader import data_loader
17
+
18
+
19
+ def _package_resource(*parts: str) -> str:
20
+ """Return the filesystem path to a resource bundled in this package."""
21
+ return str(resources.files("stac_api_load_testing").joinpath(*parts))
22
+
23
+
24
+ def _tool_path(name: str) -> str:
25
+ """Resolve a console script on PATH, falling back to this env's bin dir."""
26
+ return shutil.which(name) or str(Path(sys.executable).parent / name)
27
+
28
+
29
+ def generate_taurus_config(api_url: str, concurrency: int, ramp_up, iterations) -> str:
30
+ """
31
+ Generate a custom Taurus configuration file based on the specified settings.
32
+
33
+ Args:
34
+ api_url (str): The base URL for the STAC API to be tested.
35
+ concurrency (int): The number of concurrent users to simulate.
36
+ ramp_up (str): The duration over which to ramp up the load test.
37
+ iterations (int): The total number of iterations to perform.
38
+
39
+ Returns:
40
+ str: The path to the generated Taurus configuration file.
41
+ """
42
+ template_path = _package_resource("config_files", "taurus_locust.yml")
43
+ locustfile_path = _package_resource("config_files", "locustfile.py")
44
+
45
+ safe_api_url = re.sub(r"[^a-zA-Z0-9]+", "_", api_url)
46
+ output_path = f"taurus_config_{safe_api_url}.yml"
47
+
48
+ try:
49
+ with open(template_path, "r") as file:
50
+ config = yaml.safe_load(file)
51
+
52
+ # Setting the script path directly to where `locustfile.py` is expected to be within the package
53
+ config["execution"][0]["concurrency"] = concurrency
54
+ config["execution"][0]["ramp-up"] = ramp_up
55
+ config["execution"][0]["iterations"] = iterations
56
+ config["scenarios"]["default"]["script"] = locustfile_path
57
+ config["scenarios"]["default"]["default-address"] = api_url
58
+
59
+ with open(output_path, "w") as file:
60
+ yaml.safe_dump(config, file)
61
+
62
+ return output_path
63
+ except Exception as e:
64
+ print(f"Error creating Taurus configuration file: {e}")
65
+ return None
66
+
67
+
68
+ def _read_aggregated_stats(csv_path: str) -> dict:
69
+ """Return the 'Aggregated' row from a locust *_stats.csv file."""
70
+ with open(csv_path, newline="") as file:
71
+ for row in csv.DictReader(file):
72
+ if row.get("Name") == "Aggregated":
73
+ return row
74
+ return {}
75
+
76
+
77
+ def run_benchmark(
78
+ api_addresses: list,
79
+ concurrency_levels: list,
80
+ spawn_rate: int,
81
+ run_time: str,
82
+ output: str,
83
+ ):
84
+ """
85
+ Run headless Locust benchmark sweeps against one or more STAC APIs.
86
+
87
+ For each API address, a headless locust run is executed at every
88
+ concurrency level. Aggregated statistics are collected from locust's
89
+ CSV output and printed as a comparison table.
90
+
91
+ Args:
92
+ api_addresses (list): STAC API base URLs to benchmark, in order.
93
+ concurrency_levels (list): Concurrent user counts to sweep.
94
+ spawn_rate (int): Users spawned per second in each run.
95
+ run_time (str): Duration of each run, in locust notation (e.g. '30s').
96
+ output (str): Optional path to write aggregated results as CSV.
97
+ """
98
+ locustfile_path = _package_resource("config_files", "locustfile.py")
99
+ results = []
100
+
101
+ for address in api_addresses:
102
+ for users in concurrency_levels:
103
+ click.secho(f"\n>>> {address} @ {users} users for {run_time}", fg="cyan")
104
+ with tempfile.TemporaryDirectory() as tmpdir:
105
+ csv_prefix = os.path.join(tmpdir, "locust")
106
+ stats_csv = f"{csv_prefix}_stats.csv"
107
+ # locust exits non-zero when requests fail; under load that is
108
+ # data (backend errors), not a benchmark tool error.
109
+ subprocess.run(
110
+ [
111
+ _tool_path("locust"),
112
+ "--locustfile",
113
+ locustfile_path,
114
+ "--host",
115
+ address,
116
+ "--headless",
117
+ "-u",
118
+ str(users),
119
+ "-r",
120
+ str(spawn_rate),
121
+ "-t",
122
+ run_time,
123
+ "--csv",
124
+ csv_prefix,
125
+ "--only-summary",
126
+ ]
127
+ )
128
+
129
+ if not os.path.exists(stats_csv):
130
+ click.secho(
131
+ f"no stats produced for {address} @ {users} users",
132
+ fg="red",
133
+ )
134
+ continue
135
+
136
+ stats = _read_aggregated_stats(stats_csv)
137
+ if stats:
138
+ results.append(
139
+ {
140
+ "api": address,
141
+ "users": users,
142
+ "requests": stats.get("Request Count"),
143
+ "failures": stats.get("Failure Count"),
144
+ "median_ms": stats.get("Median Response Time"),
145
+ "p95_ms": stats.get("95%"),
146
+ "p99_ms": stats.get("99%"),
147
+ "rps": round(float(stats.get("Requests/s") or 0), 1),
148
+ }
149
+ )
150
+
151
+ if not results:
152
+ click.secho("No benchmark results collected.", fg="red")
153
+ return
154
+
155
+ header = f"{'api':<26}{'users':>7}{'reqs':>9}{'fails':>7}{'med':>8}{'p95':>8}{'p99':>8}{'rps':>9}"
156
+ click.echo("\n" + header)
157
+ click.echo("-" * len(header))
158
+ for row in results:
159
+ click.echo(
160
+ f"{row['api']:<26}{row['users']:>7}{row['requests']:>9}"
161
+ f"{row['failures']:>7}{row['median_ms']:>8}{row['p95_ms']:>8}"
162
+ f"{row['p99_ms']:>8}{row['rps']:>9}"
163
+ )
164
+
165
+ if output:
166
+ with open(output, "w", newline="") as file:
167
+ writer = csv.DictWriter(file, fieldnames=list(results[0].keys()))
168
+ writer.writeheader()
169
+ writer.writerows(results)
170
+ click.secho(f"\nResults written to {output}", fg="green")
171
+
172
+
173
+ @click.command()
174
+ @click.option(
175
+ "-i", "--ingest", is_flag=True, help="Ingest sample data into the STAC API."
176
+ )
177
+ @click.option(
178
+ "-l", "--locust", is_flag=True, help="Run Locust load tests against the STAC API."
179
+ )
180
+ @click.option(
181
+ "-t",
182
+ "--taurus",
183
+ is_flag=True,
184
+ help="Run the Taurus wrapper for performance testing against the STAC API.",
185
+ )
186
+ @click.option(
187
+ "-c",
188
+ "--concurrency",
189
+ default=10,
190
+ help="Number of concurrent users for Taurus option.",
191
+ type=int,
192
+ )
193
+ @click.option("-r", "--ramp-up", default="1m", help="Ramp-up time for Taurus option.")
194
+ @click.option(
195
+ "-n",
196
+ "--iterations",
197
+ default=100,
198
+ help="Number of iterations for Taurus option.",
199
+ type=int,
200
+ ) # Changed the flag to -n to avoid conflict
201
+ @click.option(
202
+ "-a",
203
+ "--api-address",
204
+ default="http://localhost:8080",
205
+ help="Specify the STAC API URL to test against. Accepts a comma-separated list for --benchmark.",
206
+ )
207
+ @click.option(
208
+ "-b",
209
+ "--benchmark",
210
+ is_flag=True,
211
+ help="Run a headless locust benchmark sweep against the STAC API.",
212
+ )
213
+ @click.option(
214
+ "-u",
215
+ "--users",
216
+ default="10,50,100",
217
+ help="Comma-separated concurrency levels for --benchmark.",
218
+ )
219
+ @click.option(
220
+ "--spawn-rate",
221
+ default=10,
222
+ help="Users spawned per second for --benchmark.",
223
+ type=int,
224
+ )
225
+ @click.option(
226
+ "--run-time",
227
+ default="30s",
228
+ help="Duration of each --benchmark run, in locust notation (e.g. '30s', '1m').",
229
+ )
230
+ @click.option(
231
+ "-o",
232
+ "--output",
233
+ default=None,
234
+ help="Write aggregated --benchmark results to a CSV file.",
235
+ type=click.Path(),
236
+ )
237
+ @click.version_option(version="0.3.0")
238
+ def main(
239
+ ingest: bool,
240
+ locust: bool,
241
+ taurus: bool,
242
+ api_address: str,
243
+ concurrency: int,
244
+ ramp_up: str,
245
+ iterations: int,
246
+ benchmark: bool,
247
+ users: str,
248
+ spawn_rate: int,
249
+ run_time: str,
250
+ output: str,
251
+ ):
252
+ """
253
+ Entry point for the stac-api-load-testing CLI tool.
254
+
255
+ This tool facilitates data ingestion, Locust load testing, and Taurus performance testing
256
+ against a specified STAC API endpoint.
257
+
258
+ Args:
259
+ ingest (bool): If True, ingest sample data into the specified STAC API.
260
+ locust (bool): If True, execute Locust load tests against the specified STAC API.
261
+ taurus (bool): If True, perform Taurus performance testing with custom settings against the specified STAC API.
262
+ concurrency (int): Specifies the number of concurrent users for Taurus testing. Default is 10.
263
+ ramp_up (str): Specifies the ramp-up period for Taurus testing, in Taurus notation (e.g., '1m' for 1 minute). Default is '1m'.
264
+ iterations (int): Specifies the number of iterations each virtual user will execute in Taurus testing. Default is 100.
265
+ api_address (str): The base URL of the STAC API to be tested. Comma-separated list allowed for benchmark.
266
+ benchmark (bool): If True, run headless locust runs sweeping --users levels and print a comparison table.
267
+ users (str): Comma-separated concurrency levels for benchmark. Default '10,50,100'.
268
+ spawn_rate (int): Users spawned per second in each benchmark run. Default 10.
269
+ run_time (str): Duration of each benchmark run in locust notation. Default '30s'.
270
+ output (str): Optional CSV file path for aggregated benchmark results.
271
+ """
272
+ os.environ["LOCUST_HOST"] = api_address
273
+
274
+ if ingest:
275
+ # Load data into the STAC API
276
+ data_loader.load_items(stac_api_base_url=api_address)
277
+ elif locust:
278
+ # Execute Locust load tests
279
+ locust_file_path = _package_resource("config_files", "locustfile.py")
280
+ subprocess.run(
281
+ [
282
+ _tool_path("locust"),
283
+ "--locustfile",
284
+ locust_file_path,
285
+ "--host",
286
+ api_address,
287
+ ],
288
+ check=True,
289
+ )
290
+ elif taurus:
291
+ # Generate and run a custom Taurus configuration for performance testing
292
+ config_file_path = generate_taurus_config(
293
+ api_address, concurrency, ramp_up, iterations
294
+ )
295
+ if config_file_path:
296
+ try:
297
+ subprocess.run([_tool_path("bzt"), config_file_path], check=True)
298
+ finally:
299
+ if os.path.exists(config_file_path):
300
+ os.remove(config_file_path) # Cleanup after running
301
+ elif benchmark:
302
+ concurrency_levels = [int(x) for x in users.split(",")]
303
+ api_addresses = [a.strip() for a in api_address.split(",")]
304
+ run_benchmark(api_addresses, concurrency_levels, spawn_rate, run_time, output)
305
+
306
+
307
+ if __name__ == "__main__":
308
+ main()