mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
46 lines
3.8 KiB
Markdown
46 lines
3.8 KiB
Markdown
# OpenCode Stats
|
|
|
|
Stats is a separate site from the console. Runtime, database, and domain services live in `core`; the SolidStart website lives in `app`; the Cloudflare Workers that sync stats live in `server`.
|
|
|
|
## Packages
|
|
|
|
- `app`: SolidStart frontend/site.
|
|
- `core`: Effect services, app config, Drizzle schema/migrations, and stats domains.
|
|
- `server`: the `StatsSyncWorkflow` Workflow, the `StatsSyncCron` Worker that starts it, and a local sync script.
|
|
|
|
## Commands
|
|
|
|
- `bun run dev:stats` from the repo root starts the SolidStart app.
|
|
- `bun run --cwd packages/stats/app typecheck` typechecks the site.
|
|
- `bun run --cwd packages/stats/core typecheck` typechecks the Effect/database package.
|
|
- `bun run --cwd packages/stats/server typecheck` typechecks the sync Workers.
|
|
|
|
## Stats sync
|
|
|
|
The sync reads aggregates from R2 SQL and writes them to the stats PlanetScale database. It runs on the `dev` and `production` stages as a Cloudflare Workflow, `StatsSyncWorkflow`, defined in `infra/stats.ts`.
|
|
|
|
Each run has a `plan` step, then one step per stat period (`week 2026-W45`, `day 2026-11-03`) and one per retention cohort (`retention 2026-10-05`). The plan snapshots the model catalog once, so every step attributes models the same way. Steps run one at a time and retry twice with exponential backoff. A rejected query (HTTP 400, 401 or 403) or a result over the 10,000-row limit fails its step after one attempt, since a retry repeats the same R2 SQL spend. A failed step does not stop the others, but the run ends as errored and logs each failed step with its error. Because queries run per period, expect an hourly run to take about 15 to 25 minutes and a history run about 2 to 3.5 hours. These are estimates until measured on a deployed stage.
|
|
|
|
The `StatsSyncCron` Worker starts runs on two schedules:
|
|
|
|
- `5 * * * *`: an hourly run of the current ISO week and the most recent complete retention cohort, with instance ID `stats-hourly-YYYY-MM-DDTHH`.
|
|
- `20 3 * * *`: a daily history run of the rest of the 56-day display window, with instance ID `stats-history-YYYY-MM-DD`.
|
|
|
|
Times and IDs are in UTC. Before starting a run, the Cron checks this slot's ID and the recent IDs of the same kind: the five previous hours for an hourly run, the two previous days for a history run. It skips the run if any of them is queued, running, waiting or paused, or if this slot's instance already exists, and logs the reason. Hourly and history runs can overlap, since they cover different periods and their writes are idempotent.
|
|
|
|
To start a run by hand, find the Workflow's name with `bunx wrangler workflows list` (it is `opencode-<stage>-statssyncworkflowworkflow-<suffix>`), then trigger it with an ID from the Cron's convention so scheduled runs of the same kind see it and wait:
|
|
|
|
```sh
|
|
bunx wrangler workflows trigger <workflow-name> '{"history":true}' --id stats-history-2026-11-03
|
|
```
|
|
|
|
Omit `history` for an hourly run. If the ID already exists, that slot has run; wait for the next one. You can also trigger a run from the Workflow's page in the Cloudflare dashboard; without params it runs as an hourly pass.
|
|
|
|
To run one sync pass on your machine against a stage's resources, run `bun sst shell --stage=<stage> -- bun run --cwd packages/stats/server sync`, adding `--history` for a history pass. `sst dev` also offers it as the `StatsSync` command.
|
|
|
|
## Public page caching
|
|
|
|
Stats HTML, JSON, and Markdown use a one-minute browser cache and a five-minute shared cache. Expired responses must revalidate instead of serving stale snapshots.
|
|
|
|
The Worker has no custom domain. `infra/stats.ts` routes `/data*` and `/<locale>/data*` on the main host straight to it, so its responses skip the Cloudflare cache and reach browsers with these headers unchanged. Static assets are uploaded under `/data`, and the build moves Nitro's `_headers` to the assets root so they stay immutable.
|