Skip to content

Commit 2424a35

Browse files
committed
wip
1 parent a49dc73 commit 2424a35

13 files changed

Lines changed: 342 additions & 33 deletions

File tree

docker-compose.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ services:
1010
expose:
1111
- 5432
1212
volumes:
13-
- db:/var/lib/postgresql/data
13+
- ./db:/var/lib/postgresql
1414
profiles: ["server"]
1515

1616
### MinIO for flow storage
@@ -76,7 +76,6 @@ services:
7676

7777
volumes:
7878
prefect:
79-
db:
8079
minio:
8180
networks:
8281
default:

docs/developer/oct_events_and_state.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ config block (`PSOCTScanConfig`); the payload may override individual keys
8282
(see `PROCESS_MOSAIC_FLOW_KWARGS_KEYS` in `opticstream.flows.psoct.utils`), same
8383
idea as LSM strip flows.
8484

85-
Emitters include `mosaic_ident` (e.g. `state_management_flow`, `tile_batch_update_flow` via `emit_mosaic_psoct_event`).
85+
Emitters include `mosaic_ident` (e.g. `state_management_flow`, `check_mosaic_ready_hook` via `emit_mosaic_psoct_event`).
8686

8787
### Event payload structure
8888

docs/getting-started/quickstart.md

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Quickstart
2+
title: Quickstart - LSM
33
---
44

55
# Quickstart
@@ -34,12 +34,36 @@ opticstream --help # or: ops --help
3434

3535
## 2. Configure an LSM project
3636

37-
Create and save an `LSMScanConfig` block (see the configuration page for details),
38-
ensuring `project_base_path`, `info_file`, and `output_path` are set.
37+
```bash
38+
ops lsm setup --help
39+
```
40+
41+
```bash
42+
ops lsm setup testscan
43+
```
44+
45+
you should be able to see a block in prefect ui named testscan-lsm-config
46+
47+
in there you will need to complete the detailed setup, each field will have doc in there
48+
49+
in one window, start
50+
```bash
51+
ops lsm serve 3
52+
```
53+
which will start 3 local workers to process lsm flows
54+
55+
in another windows, start watcher
56+
```bash
57+
ops lsm watch testscan E:/temp/testscan
58+
```
59+
60+
## 3. somethings goes wrong and restart a slice
61+
in most case, the watcher doesn't need to be restarted if the configuration is going to stay the same
62+
make sure the flow run for this slice are done/stopped , we dont want two flow run for the same strip running at the same time
63+
```bash
64+
ops lsm state reset testscan --slice SLICE_TO_RESET
65+
```
3966

40-
## 3. Run a strip processing flow
67+
the watcher will emit events for this slice again and reprocess them
4168

42-
Once your scan configuration block is saved, you can trigger the
43-
`process_strip_event` flow via Prefect by emitting the appropriate event
44-
payload (see the LSM workflow user guide for a full example).
4569

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
1. start the venv
3+
4+
5+
2.
6+
```bash
7+
ops oct setup
8+
```

docs/getting-started/setup.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
title: Setup
3+
---
4+
5+
# Prefect Server Setup
6+
7+
This guide walks through setting up the Prefect server for the first time on a
8+
host that already has Docker and Docker Compose installed.
9+
10+
## Prerequisites
11+
12+
- Docker Engine and **Docker Compose v2** installed on the host machine.
13+
- The OpticStream source repository cloned on the host (see [Installation](installation.md)).
14+
- Network access to the host from any machine that will use the Prefect UI or
15+
submit flows (if running remotely).
16+
17+
## 1. Configure the server URL
18+
19+
The `docker-compose.yaml` in the repository root uses a `HOST_IP` variable to
20+
set the Prefect API and UI URLs. By default it falls back to `127.0.0.1`, which
21+
only allows access from the Docker host itself.
22+
23+
If you need to reach the Prefect UI or API from **other machines on the
24+
network** (e.g. workers or browsers on different hosts), set `HOST_IP` to the
25+
host's LAN IP address or resolvable hostname.
26+
27+
### Option A — create a `.env` file (recommended)
28+
29+
Copy the provided example and edit the IP:
30+
31+
```bash
32+
cd opticstream # repository root
33+
cp .env.example .env
34+
```
35+
36+
Then open `.env` and replace the default value:
37+
38+
```dotenv
39+
# Use the host's LAN IP so other machines can reach the Prefect server.
40+
# Use 127.0.0.1 if you only access the server from the Docker host.
41+
HOST_IP=192.168.1.100
42+
```
43+
44+
Docker Compose automatically loads `.env` from the same directory as the
45+
compose file, so no extra flags are needed.
46+
47+
### Option B — export the variable in your shell
48+
49+
```bash
50+
export HOST_IP=192.168.1.100
51+
```
52+
53+
This is useful for one-off launches or CI environments where you don't want a
54+
persisted `.env` file.
55+
56+
### What the variable controls
57+
58+
Inside `docker-compose.yaml`, the `server` service references `HOST_IP` in two
59+
environment variables:
60+
61+
```yaml
62+
- PREFECT_UI_URL=http://${HOST_IP:-127.0.0.1}:4200/api
63+
- PREFECT_API_URL=http://${HOST_IP:-127.0.0.1}:4200/api
64+
```
65+
66+
`PREFECT_API_URL` tells Prefect clients where to send API requests, and
67+
`PREFECT_UI_URL` tells the browser-based dashboard where to find the API
68+
backend. Both must be reachable from whatever machine is connecting.
69+
70+
## 2. Start the Prefect server
71+
72+
From the repository root, bring up the server profile (which starts the
73+
PostgreSQL database and the Prefect server):
74+
75+
```bash
76+
docker compose --profile server up -d
77+
```
78+
79+
This pulls the required images on first run (`postgres:alpine` and
80+
`prefecthq/prefect:3-python3.12`), creates the `prefect-network` Docker
81+
network, and starts both containers in detached mode.
82+
83+
### Verify the services are running
84+
85+
```bash
86+
docker compose --profile server ps
87+
```
88+
89+
You should see two containers (`database` and `server`) with status **Up**.
90+
91+
Check the server logs to confirm a healthy start:
92+
93+
```bash
94+
docker compose --profile server logs server
95+
```
96+
97+
Look for a line similar to:
98+
99+
```
100+
Configure Prefect to communicate with the server with:
101+
102+
prefect config set PREFECT_API_URL=http://…:4200/api
103+
```
104+
105+
## 3. Open the Prefect dashboard
106+
107+
In a browser, navigate to:
108+
109+
```
110+
http://<HOST_IP>:4200
111+
```
112+
113+
Replace `<HOST_IP>` with the value you configured (or `127.0.0.1` / `localhost`
114+
if accessing from the Docker host). You should see the Prefect UI dashboard.
115+
116+
## 4. Point local clients at the server
117+
118+
On any machine that will submit or monitor flows, tell Prefect where the server
119+
lives:
120+
121+
```bash
122+
prefect config set PREFECT_API_URL=http://<HOST_IP>:4200/api
123+
```
124+
125+
Or set it as an environment variable in your shell:
126+
127+
```bash
128+
export PREFECT_API_URL=http://<HOST_IP>:4200/api
129+
```
130+
131+
You can verify the connection with:
132+
133+
```bash
134+
prefect version # prints client version
135+
prefect server health # should return healthy
136+
```
137+
138+
## 5. Stopping and restarting
139+
140+
Stop the server (containers are removed but volumes persist):
141+
142+
```bash
143+
docker compose --profile server down
144+
```
145+
146+
Restart later with the same command used to start:
147+
148+
```bash
149+
docker compose --profile server up -d
150+
```
151+
152+
All flow-run history and configuration is stored in the `db` Docker volume and
153+
survives restarts.
154+
155+
## Troubleshooting
156+
157+
| Symptom | Likely cause | Fix |
158+
|---|---|---|
159+
| Browser cannot reach `http://<HOST_IP>:4200` | `HOST_IP` is wrong or firewall blocks port 4200 | Verify `HOST_IP` matches the host's actual LAN IP; open port 4200 in the firewall |
160+
| `prefect server health` fails from another machine | `PREFECT_API_URL` on the client doesn't match `HOST_IP` | Re-run `prefect config set PREFECT_API_URL=…` with the correct address |
161+
| Database connection errors in server logs | The `database` container isn't running | Run `docker compose --profile server up -d` and check `docker compose --profile server ps` |
162+
| Containers exit immediately | Port 4200 or 5432 already in use on the host | Stop the conflicting service or change the port mapping in `docker-compose.yaml` |
163+
164+
## Next steps
165+
166+
- Continue with the [Quickstart](quickstart.md) to configure a scan and run
167+
your first flow.
168+
- See [Configuration](configuration.md) for scan and project settings.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
title: LSM Acquisition Host
3+
---
4+
5+
# LSM acquisition host
6+
7+
The LSM acquisition host is the machine that runs both the LSM microscope acquisition software and the OpticStream real-time processing pipeline simultaneously. Because the acquisition software is latency-sensitive — a backlogged acquisition process will drop frames — the host must be tuned to ensure sufficient CPU headroom is reserved exclusively for acquisition.
8+
9+
## Host responsibilities
10+
11+
- Runs the vendor acquisition software, which writes raw strip folders to a local or network path.
12+
- Runs the OpticStream LSM watcher (`opticstream lsm watch`) and Prefect worker, which detect new strips and trigger processing flows in real time.
13+
- Performs CPU-intensive work such as Zarr compression, MIP generation, and optional strip archival while acquisition is ongoing.
14+
15+
## Performance optimisations
16+
17+
### 1. Disable hyperthreading (BIOS)
18+
19+
Hyperthreading should be disabled in the server BIOS. With hyperthreading enabled, two logical cores share one physical core's execution resources, which causes unpredictable latency spikes under the mixed real-time / batch load typical of an acquisition session. Disabling it gives each logical core exclusive access to its physical core and makes CPU scheduling more deterministic.
20+
21+
This setting is found under the processor or advanced CPU configuration section of the BIOS/UEFI, often labelled **Hyper-Threading**, **Simultaneous Multithreading (SMT)**, or similar. It must be set before the OS boots; no OS-level change is needed.
22+
23+
### 2. Limit CPU affinity for processing workers
24+
25+
Even with hyperthreading disabled, the processing flows must not be allowed to compete with the acquisition software for CPU time. OpticStream enforces this by pinning its Prefect worker processes to a subset of CPU cores using the `cpu_affinity` field in the `LSMScanConfig` block.
26+
27+
**How it works:** the `cpu_affinity` setting is passed directly to the underlying worker processes (e.g. via `os.sched_setaffinity` or equivalent), restricting them to the listed cores. The acquisition software runs on the remaining cores and is never preempted by processing work.
28+
29+
**Example — host Willow (28 physical cores, 0-indexed):**
30+
31+
Cores 0–13 are reserved for the acquisition software. The processing workers are pinned to cores 14–27:
32+
33+
```yaml
34+
cpu_affinity: [14, 28]
35+
```
36+
37+
Set this in the project's `LSMScanConfig` Prefect block via the Prefect UI:
38+
39+
1. Open the Prefect dashboard at `http://<HOST_IP>:4200`.
40+
2. Navigate to **Blocks** and open the `LSMScanConfig` block for the project.
41+
3. Set the `cpu_affinity` field to the list of core indices the processing workers should use, e.g. `[14, 28]`.
42+
4. Save the block.
43+
44+
As a rule of thumb, give the acquisition software roughly half the physical cores and let the processing pipeline use the other half. Adjust the split based on observed acquisition CPU usage — if the acquisition process starts queueing or dropping frames, expand the reserved range.
45+
46+
## Monitoring
47+
48+
Use **Windows Task Manager** to verify CPU load is distributed as expected during an active acquisition session:
49+
50+
1. Open Task Manager (`Ctrl+Shift+Esc`) and go to the **Performance** tab.
51+
2. Click **CPU**, then right-click the CPU graph and select **Change graph to → Logical processors**. Each core is shown as a separate tile.
52+
3. Confirm that the acquisition software cores (0–13 on Willow) stay at low-to-moderate utilisation, while the processing cores (14–27 on Willow) are free to spike to 100 % during Zarr compression without affecting acquisition.
53+
54+
If frame drops are observed despite correct affinity settings, check whether any other system process (e.g. Windows Update, antivirus, background services) is also running on the cores reserved for acquisition and reschedule or disable those tasks if possible.
55+
8
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Storage and data flow
2+
3+
Acquisition hosts **Willow** and **Cedar**, and processing host **Zircon**, connect to the storage system over **SAS** (host HBAs to the storage server or expander fabric).
4+
5+
```
6+
┌──────────────────────────┐
7+
│ Storage server │
8+
└───────────┬──────────────┘
9+
10+
SAS (HBAs / multipath as deployed)
11+
12+
┌──────────────────────┼──────────────────────┐
13+
│ │ │
14+
│ SAS │ SAS │ SAS
15+
▼ ▼ ▼
16+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
17+
│ Willow │ │ Cedar │ │ Zircon │
18+
│ (acquisition) │ │ (acquisition) │ │ (processing) │
19+
└─────────────────┘ └─────────────────┘ └─────────────────┘
20+
```
21+
22+
## Host networking (25G vSwitch on Zircon, 10Gb wall)
23+
24+
**Willow**, **Cedar**, **Zircon**, and **** (fourth host, such as Maple) all **connect to Zircon** for east–west traffic: a **25 Gbps virtual switch** runs **on Zircon**, and every host has a **25 Gbps** leg into that vSwitch (Zircon’s own participation is on-box). Separately, **each host** has its own **10 Gbps** link to the building **wall** (patch / upstream).
25+
26+
```
27+
┌──────────────────────────────────┐
28+
│ 10Gb wall (building uplink) │
29+
└───┬────────┬────────┬────────┬───┘
30+
│ │ │ │
31+
10Gb 10Gb 10Gb 10Gb
32+
│ │ │ │
33+
Willow Cedar Zircon …
34+
│ │ │ │
35+
└──25G───┴──25G───┴──25G───┴──25G──┘
36+
37+
┌────────┴────────┐
38+
│ Zircon │
39+
│ vSwitch 25 Gbps │
40+
└─────────────────┘
41+
```
42+
43+
East–west traffic between hosts uses the **25 Gbps vSwitch on Zircon** (hub: all hosts → Zircon). North–south: **10 Gbps** to the **wall** per host, independent of the vSwitch path (exact VLANs or ports per your network plan).
44+
45+
## SMB file sharing (Zircon reads data on other hosts)
46+
47+
**Zircon** reaches filesystems on the other hosts **over the same east–west path** (the **25 Gbps** fabric to Zircon’s vSwitch, not over SAS). Hosts that hold data **publish folders as SMB shares** (Windows file sharing: SMB/CIFS). **Zircon** mounts or accesses those shares as an **SMB client** so pipelines and services on Zircon can read (or write, if permitted) files as if they were local or UNC paths, without moving bulk data off the acquisition machines first.
48+
49+
Typical pattern: **Willow** / **Cedar** (and other data hosts as needed) run an **SMB server** and share the relevant disk or directory; **Zircon** connects with credentials and uses Windows UNC paths (for example `\\Willow\Acquisition`) or Linux `//Willow/Acquisition` mounts (`cifs`, `smbclient`) over **25 Gbps**, which keeps latency and throughput suitable for large sequential reads compared to routing that traffic out the **10 Gbps wall** uplink.
50+
51+
Operational notes: align **firewall** rules and **SMB signing** policy with your site; for Linux servers use **Samba** for SMB shares; ensure **service accounts** and **share permissions** match what processing jobs on Zircon expect.
52+
53+
## Shared volume on acquisition and processing (dual attach, storage-mapped RO on Zircon)
54+
55+
A less common option is to **present the same volume** (same LUN or equivalent) to **both** an acquisition host (**Willow** / **Cedar**) and **Zircon**. In many environments that is **discouraged or disallowed** when two hosts could **both write** the same filesystem.
56+
57+
Here the **storage server** defines two **mappings** for that volume: the **acquisition host** sees it **read–write** so capture can write **NTFS** (or the host OS) normally. **Zircon** is given a **read-only** mapping to the **same** volume—enforced **below the OS** by the **array / presentation** (SCSI permissions, LUN mask, or equivalent), so **Zircon cannot write** even if a client tried. Only **one** host mutates data; **Zircon** is a **reader**.
58+
59+
**Upside:** Reads on **Zircon** go over **SAS/block** with **lower overhead** and often **better throughput** than **SMB** over the 25 Gbps network.
60+
61+
**Downside:** **Zircon** does **not** see changes in **real time** the way a network share can; metadata and cache may lag. Operators sometimes need to **unmount and remount** on **Zircon** (or refresh the mount) after new data lands on the acquisition side.
62+
63+
**Protect acquisition:** Even read-heavy workloads on **Zircon** can **contend** with the array or paths the acquisition host relies on. **Throttle** sequential readers, **limit parallelism**, and **cap IOPS/bandwidth** on the processing host so **Zircon** does not **read fast enough to interfere** with ongoing capture (latency spikes, dropped frames, or storage saturation). Treat this as a **shared resource budget**, not “read as fast as possible.”
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
on windows we setup storage pool for the ssd, so it survive single drive failure,

docs/user-guides/troubleshooting.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ Symptoms:
1616

1717
Checks:
1818

19-
- run `opticstream init blocks`
2019
- run `opticstream lsm setup PROJECT_NAME` or `opticstream oct setup PROJECT_NAME`
2120
- verify block names follow project naming conventions used by setup commands
2221

opticstream/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from .oct import oct_cli
1515
from .lsm import lsm_cli # noqa: F401
1616
from .utils import utils_cli # noqa: F401
17+
from .process_priority import process_priority_cli # noqa: F401
1718

1819

1920
def main() -> None:

0 commit comments

Comments
 (0)