Skip to content

Commit 1939851

Browse files
vshekardanielballangwbischof
authored
Websocket endpoint (#999)
* Added endpoint to router * Refactored get_entry() to not use dependencies * Formatting * get_entry only returns an entry * Refactored get_current_principal for websockets * Initial websocket endpoint implementation * Added: - config schema def for redis client - CLI - redis client * Added: - redis client init - Removed client config from service config schema * chore: stripping trailing whitespace * chore: black formatting * Added redis_client to CatalogAdapter * Refactored deserialization * Connect with websockets * Fixed get_entry() calls to match signature * Added code to push data to redis * Satisfy linter * redis must be optional * Missing API should return 401 (not 500) * Do not decode the payload. * Handle msgpack and JSON serialization correctly. * Fix exit logic? * Added stop stream endpoint * Changed post /stream/close to a delete verb * Closing stream should be idempotent * Set seq_num:node_id to expire when stream is closed * Satisfy linters * Namespace /stream to /stream/single * Remove vestigial parameter. * Closing works and updates database. * Add search on is_streaming. * Expose DELETE /stream/close in client. * Use py39-compat usage. * Validate input for envelope_format * Decode *after* streaming, and give more metadata * Implement streaming for write_block * Implement streaming for PATCH array * Clean up errors on put_data_source codepath. * No mimetype for data_source * Handle data_source or payload * Use out-of-band signaling. * Separated websocket handling and redis interaction * Default content-type for arrays * Support streaming container, composite. * Move data_source into metadata. Close explicitly. * Rename ?seq_num to ?start and include sequence in metadata. * Add client Subscription object based on caproto. * Refine Subscription API * Renamed encoder to envelope_formatter for consistency * Bug fixes and variable name clarification * Fix ws/wss conditional * Handle weakrefs correctly * Revoke API key after use. * Add types and docstring to Subscription. * Added more metadata * Changed order of metadata * Added patch * Fix Patch scheme: tuple of multiple ints * Added uri of slices to stream * checking if i can commit * update pyproject from rebase * is_streaming migration * update test_write_array_internal_direct * Changed command line flag --redis to --cache * add test_websockets * trying to get the TestClient to connect to websocket * touch up root_tree * websocket test touch up * use context manager for TestClient * the test is working finally * touch ups websocket test * make a tiled_websocket_context fixture * add the rest of the tests from test-redis-ws * last test is hanging waiting for historical message * basic websocket tests passing * add redis for ci * skip websocket tests on windows * skip the whole module on win * test Subscription, need a better way to close the thread in _receive * test_subscription is passing * add WebSocketWrapper * tests working with wrapper * add more tests from Subscription * add socket_timeout and socket_connect_timeout cache_settings * test the close endpoint * I think there is a minor problem with the close endpoint * first pass at adding locust websocket tests * change cache_ttl to integer from float * update the websocket header to include 'Apikey', close endpoint returns 404 if node doesn't exist or is already closed * update Subscription header to be prefixed by Apikey * locust streaming tests are working, but only in headless mode * update locust readme * Fix zarr declaration * remove SpecialUser * update websocket wrapper classes * Add redis dep for pixi too * The websockets library is a client-side dep. * Fix mistake introduced in rebase * Missed purge of 'composite' in rebase * Disambiguate between single-user and anonymous * rebase mistake * more tests are passing * websocket tests all passing * remove is_streaming * update adapter streaming logic to use redis * add auth tests * clear state between subcription tests * Apply move API key to HTTP (not WS) requests. * fixes based on dans comments * load cache settings from config file * fix duplicate config key name * Parse streaming_cache from config. * add cache_settings to in_memory * Supply TILED_TEST_REDIS explicitly. Remove randomness. * Address pydantic deprecation warning on dict() * Remove TODO; this looks good * The referenced issues has been closed, types are tightened * Disable LDAP tests for now #1109 * Properly detect request scopes and access_tags. * Drop commented-out vestigial parameters. * Properly integrate access tags, scopes with WS endpoint --------- Co-authored-by: Dan Allan <[email protected]> Co-authored-by: gbischof <[email protected]>
1 parent 12557da commit 1939851

File tree

23 files changed

+1770
-128
lines changed

23 files changed

+1770
-128
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ jobs:
4444
- name: Install tiled
4545
run: uv sync --all-extras
4646

47-
- name: Start LDAP service in container.
48-
shell: bash -l {0}
49-
run: source continuous_integration/scripts/start_LDAP.sh
47+
# TODO Find a new image to use.
48+
# https://github.com/bluesky/tiled/issues/1109
49+
# - name: Start LDAP service in container.
50+
# shell: bash -l {0}
51+
# run: source continuous_integration/scripts/start_LDAP.sh
5052

5153
- name: Download SQLite example data.
5254
shell: bash -l {0}
@@ -56,6 +58,10 @@ jobs:
5658
shell: bash -l {0}
5759
run: source continuous_integration/scripts/start_postgres.sh
5860

61+
- name: Start Redis service in container.
62+
shell: bash -l {0}
63+
run: source continuous_integration/scripts/start_redis.sh
64+
5965

6066
- name: Ensure example data is migrated to current catalog database schema.
6167
# The example data is expected to be kept up to date to the latest Tiled
@@ -75,10 +81,13 @@ jobs:
7581
uv run coverage report -m
7682
uv run coverage xml -o cov.xml
7783
env:
78-
# Provide test suite with a PostgreSQL database to use.
84+
# Provide test suite with PostgreSQL and Redis databases to use.
7985
TILED_TEST_POSTGRESQL_URI: postgresql://postgres:secret@localhost:5432
80-
# Opt in to LDAPAuthenticator tests.
81-
TILED_TEST_LDAP: 1
86+
TILED_TEST_REIDS_URI: redis://localhost:6379
87+
# TODO Reinstate after finding a new image to use
88+
# https://github.com/bluesky/tiled/issues/1109
89+
# # Opt in to LDAPAuthenticator tests.
90+
# TILED_TEST_LDAP: 1
8291

8392
- name: Upload coverage to Codecov
8493
uses: codecov/codecov-action@v4

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,6 @@ pixi.lock
8282
# uv environments
8383
uv.lock
8484
.venv
85+
# pixi environments
86+
.pixi/*
87+
!.pixi/config.toml
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
set -e
3+
4+
docker run -d --rm --name tiled-test-redis -p 6379:6379 docker.io/redis:7-alpine
5+
docker ps

locust/README.md

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,107 @@
11
# Tiled Load Testing with Locust
22

3-
Simple load testing for Tiled using the `reader.py` file.
3+
Load testing for Tiled using Locust. Two test files are available:
4+
- `reader.py` - Tests HTTP read operations and search endpoints
5+
- `streaming.py` - Tests streaming data writes and WebSocket delivery latency
46

57
## Quick Start
68

79
```bash
8-
# Install dependencies (dev environment includes locust)
9-
pixi install -e dev
10+
# Install dependencies (locust should already be available in the environment)
11+
# If not installed, add it to your requirements or install with:
12+
# uv add locust
1013
```
1114

15+
## Starting Test Server
16+
17+
Before running locust tests, start a Tiled server:
18+
19+
```bash
20+
# Basic server (works for most tests)
21+
uv run tiled serve catalog \
22+
--host 0.0.0.0 \
23+
--port 8000 \
24+
--api-key secret \
25+
--temp \
26+
--init
27+
```
28+
29+
For streaming tests with Redis cache (optional):
30+
```bash
31+
# Start Redis first
32+
redis-server
33+
34+
# Start Tiled server with Redis cache
35+
uv run tiled serve catalog \
36+
--host 0.0.0.0 \
37+
--port 8000 \
38+
--api-key secret \
39+
--cache "redis://localhost:6379" \
40+
--cache-ttl 60 \
41+
--temp \
42+
--init
43+
```
44+
45+
This creates a temporary catalog with:
46+
- API key authentication (key: "secret")
47+
- Temporary writable storage (automatically cleaned up)
48+
- Optional Redis cache for enhanced streaming performance
49+
- Server running on http://localhost:8000
50+
51+
## Reading Performance Tests (`reader.py`)
52+
53+
Tests various HTTP endpoints for reading data, metadata, and search operations.
54+
1255
### Examples
1356
Run with default localhost server (uses default API key 'secret'):
1457
```bash
15-
pixi run -e dev locust -f reader.py --host http://localhost:8000
58+
uv run locust -f reader.py --headless -u 100 -r 10 -t 60s --host http://localhost:8000
1659
```
1760

1861
Run with custom API key:
1962
```bash
20-
pixi run -e dev locust -f reader.py --host http://localhost:8000 --api-key your-api-key
63+
uv run locust -f reader.py --headless -u 100 -r 10 -t 60s --host http://localhost:8000 --api-key your-api-key
2164
```
2265

2366
Run with custom container name (defaults to locust_testing):
2467
```bash
25-
pixi run -e dev locust -f reader.py --host http://localhost:8000 --container-name my_test_container
68+
uv run locust -f reader.py --headless -u 100 -r 10 -t 60s --host http://localhost:8000 --container-name my_test_container
69+
```
70+
71+
## Streaming Performance Tests (`streaming.py`)
72+
73+
Tests streaming data writes and WebSocket delivery with end-to-end latency measurement.
74+
75+
**Note:** The `--node-name` parameter is required for streaming tests to avoid conflicts when multiple test runs create nodes with the same name.
76+
77+
### Examples
78+
Run with required node name:
79+
```bash
80+
uv run locust -f streaming.py --headless -u 10 -r 2 -t 120s --host http://localhost:8000 --node-name my_test_stream
2681
```
2782

28-
## Headless Mode
29-
Run without the web interface:
83+
Run with custom API key:
3084
```bash
31-
pixi run -e dev locust -f reader.py --headless -u 100 -r 10 -t 60s
85+
uv run locust -f streaming.py --headless -u 10 -r 2 -t 120s --host http://localhost:8000 --api-key your-api-key --node-name my_test_stream
3286
```
33-
- `-u 100`: 100 concurrent users
34-
- `-r 10`: Spawn 10 users per second
35-
- `-t 60s`: Run for 60 seconds
87+
88+
Control user types with environment variables:
89+
```bash
90+
# 2 writers for every 1 streaming reader
91+
WRITER_WEIGHT=2 STREAMING_WEIGHT=1 uv run locust -f streaming.py --headless -u 10 -r 2 -t 120s --host http://localhost:8000 --node-name my_test_stream
92+
```
93+
94+
### Streaming Test Components
95+
- **WriterUser**: Writes timestamped array data to streaming nodes
96+
- **StreamingUser**: Connects via WebSocket to measure write-to-delivery latency
97+
98+
## Parameters
99+
- `-u N`: N concurrent users
100+
- `-r N`: Spawn N users per second
101+
- `-t Ns`: Run for N seconds
102+
- `--headless`: Run without web interface (required for automation)
103+
104+
## Notes
105+
- All examples use `--headless` mode for reliable automation
106+
- For streaming tests, `--node-name` is required to avoid conflicts
107+
- Use environment variables `WRITER_WEIGHT` and `STREAMING_WEIGHT` to control user distribution

0 commit comments

Comments
 (0)