diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 55ec5aa60..f4a1da077 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -99,7 +99,7 @@ jobs: VITE_EAG_CLIENT_ID: "A52JhudyAPK1KdBS3NrUhNsn" VITE_FLO_CLIENT_ID: "9krYLjq1rz9icCefO6OWxoMk" VITE_MIN_CLIENT_ID: "A0DBDBB063CD800530EF01C6488B282137E0191E" - VITE_GIT_REPO: "GitHub:zkldi/Tachi3" + VITE_GIT_REPO: "GitHub:zkldi/Tachi" VITE_RECAPTCHA_KEY: "6LcsYbIpAAAAAEJffjIXmbQcxj_SBZG7BnSPjF4L" TACHI_NAME: "Tachi Dev" BUILD_OUT_DIR: /tmp/dev diff --git a/.github/workflows/seeds-webui.yml b/.github/workflows/seeds-webui.yml index 3b2392023..9b92a61d3 100644 --- a/.github/workflows/seeds-webui.yml +++ b/.github/workflows/seeds-webui.yml @@ -46,7 +46,7 @@ jobs: - name: Production build run: bun run --filter tachi-seeds-webui build env: - VITE_SEEDS_REPO: zkldi/Tachi3 + VITE_SEEDS_REPO: zkldi/Tachi BUILD_OUT_DIR: /tmp/seeds-webui - name: Bundle build output diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index c751aa8eb..b02b15183 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -41,17 +41,28 @@ jobs: env: NODE_ENV: "test" services: - tachi-postgres: + # Test Postgres mirrors `tachi-postgres-test` in docker-compose-dev.yml: + # a tmpfs-backed PG sized for ~8 long-lived worker DBs with autovacuum + # on. `PGDATA` must be a SUBDIRECTORY of the tmpfs mount, otherwise + # initdb refuses to clobber the mount root. The runtime-tunable knobs + # (synchronous_commit, WAL ceiling, autovacuum naptime, etc.) are + # applied via `ALTER SYSTEM` in a setup step below; restart-required + # knobs (fsync, full_page_writes, shared_buffers, ...) we leave at + # the postgres defaults because `services:` does not let us pass + # postgres CLI args. With tmpfs, fsync is essentially free anyway. + tachi-postgres-test: image: postgres:18 env: POSTGRES_USER: tachi POSTGRES_PASSWORD: tachi POSTGRES_DB: postgres + PGDATA: /var/lib/postgresql/data/pgdata options: >- --health-cmd "pg_isready -U tachi" --health-interval 5s --health-timeout 5s --health-retries 10 + --tmpfs /var/lib/postgresql/data:rw,size=3g tachi-redis: image: redis:7.4-alpine options: >- @@ -92,11 +103,36 @@ jobs: - name: Typecheck code run: bun run --filter tachi-server typecheck + - name: Tune test Postgres for short-lived worker DBs + # The restart-only knobs (fsync, shared_buffers, ...) cannot be set + # because the GHA service syntax does not let us pass postgres CLI + # args. Everything below is reloadable; it lines up with the + # `-c ...` flags on `tachi-postgres-test` in docker-compose-dev.yml + # so CI and local behave the same once the suite starts running. + env: + PGPASSWORD: tachi + run: | + psql -h tachi-postgres-test -U tachi -d postgres <<'SQL' + ALTER SYSTEM SET synchronous_commit = off; + ALTER SYSTEM SET max_wal_size = '128MB'; + ALTER SYSTEM SET min_wal_size = '32MB'; + ALTER SYSTEM SET checkpoint_timeout = '30s'; + ALTER SYSTEM SET autovacuum_naptime = '5s'; + ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.05; + ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.1; + SELECT pg_reload_conf(); + SQL + - name: Run tests - run: bun run --filter tachi-server test env: NODE_ENV: "test" PORT: 8080 + POSTGRES_TEST_HOST: tachi-postgres-test + POSTGRES_TEST_URL: postgresql://tachi:tachi@tachi-postgres-test + # ubuntu-latest is 4 vCPU; cap workers to leave headroom for the + # PG service container and avoid oversubscribing tmpfs. + VITEST_MAX_WORKERS: "4" + run: bun run --filter tachi-server test docker-push: runs-on: ubuntu-latest diff --git a/Justfile-db b/Justfile-db index 3bca98824..65863da07 100644 --- a/Justfile-db +++ b/Justfile-db @@ -1,4 +1,8 @@ POSTGRES_URL := "postgresql://tachi:tachi@tachi-postgres" +# Test Postgres lives on tmpfs with durability disabled (docker-compose-dev.yml, +# `tachi-postgres-test` service). Defaults to the dev Postgres so tests still run +# in environments without the dedicated -test service; override via env var. +POSTGRES_TEST_URL := env_var_or_default("POSTGRES_TEST_URL", "postgresql://tachi:tachi@tachi-postgres") DEFAULT_DB := "tachi_dev" # Open a psql shell against the local dev Postgres instance. diff --git a/Justfile-test b/Justfile-test index aa4c8432e..e2b0af51a 100644 --- a/Justfile-test +++ b/Justfile-test @@ -47,12 +47,10 @@ autofix: fmt: bun biome format --write bun prettier --write --list-different . - # cd deploy/infra && tofu fmt -diff fmt-check: bun biome format bun prettier --check . - # cd deploy/infra && tofu fmt --check -diff test FILTER="*": #!/bin/bash @@ -68,6 +66,12 @@ test-typescript FILTER="*": #!/bin/bash set -euo pipefail + # `tachi-postgres-test` (tmpfs + fsync=off, see docker-compose-dev.yml) is the + # fast path; falls back to the dev PG if the dedicated service isn't running. + export POSTGRES_TEST_HOST="${POSTGRES_TEST_HOST:-tachi-postgres-test}" + # Keep POSTGRES_TEST_URL in sync so the `*-db-test-template-reset` recipes + # (called from vitest.globalSetup.ts) hit the same host the workers will. + export POSTGRES_TEST_URL="${POSTGRES_TEST_URL:-postgresql://tachi:tachi@${POSTGRES_TEST_HOST}}" bun run --elide-lines=0 --sequential --filter '{{FILTER}}' test -- --reporter=default --reporter=junit --outputFile.junit=test-results/junit.xml # Summarize Vitest v8 coverage across workspaces (see typescript/coverage-tools). @@ -83,20 +87,24 @@ test-parity suite="": # Create (or recreate) the bot test template database with all migrations applied. # Workers clone from this template rather than re-running migrations each time. +# Targets the dedicated tmpfs `tachi-postgres-test` server (override via +# POSTGRES_TEST_URL). bot-db-test-template-reset: #!/bin/bash set -euo pipefail - export POSTGRES_URL="{{POSTGRES_URL}}/tachi_bot_test_template" + export POSTGRES_URL="{{POSTGRES_TEST_URL}}/tachi_bot_test_template" tachidb database drop tachidb database create tachidb migrate run # Create (or recreate) the server test template database with all migrations applied. # Workers clone from this template rather than re-running migrations each time. +# Targets the dedicated tmpfs `tachi-postgres-test` server (override via +# POSTGRES_TEST_URL). server-db-test-template-reset: #!/bin/bash set -euo pipefail - export POSTGRES_URL="{{POSTGRES_URL}}/tachi_server_test_template" + export POSTGRES_URL="{{POSTGRES_TEST_URL}}/tachi_server_test_template" tachidb database drop tachidb database create tachidb migrate run diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index bbc9e9b60..c04ed2095 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -51,6 +51,69 @@ services: volumes: - tachi-postgres:/var/lib/postgresql - "./dev/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql" + + # Dedicated test Postgres. Lives on tmpfs with durability disabled - every + # `CREATE DATABASE ... TEMPLATE`, every `TRUNCATE`, every commit is ~5-20x + # faster than the dev DB. Wiped on container restart, which is exactly what + # we want for tests. Use POSTGRES_TEST_HOST in vitest setup to point at it. + tachi-postgres-test: + container_name: tachi-postgres-test + image: postgres:18 + restart: unless-stopped + ports: + - "5433:5432" + # NOTE: with `pool: "threads" + isolate: false` (vitest.config.ts) the + # vitest worker DBs are long-lived (~25-30 files of writes each), so + # autovacuum + a small WAL ceiling + a working bgwriter are NEEDED to + # avoid filling the tmpfs. Earlier we shipped autovacuum=off + + # max_wal_size=1GB + bgwriter_lru_maxpages=0 (which was fine for the + # short-lived per-file DBs of isolate:true) and tests started failing + # mid-run with `No space left on device`. + command: + - postgres + - -c + - fsync=off + - -c + - synchronous_commit=off + - -c + - full_page_writes=off + - -c + - wal_level=minimal + - -c + - max_wal_senders=0 + - -c + - shared_buffers=256MB + - -c + - max_connections=200 + - -c + - max_wal_size=128MB + - -c + - min_wal_size=32MB + - -c + - checkpoint_timeout=30s + - -c + - autovacuum=on + - -c + - autovacuum_naptime=5s + - -c + - autovacuum_vacuum_scale_factor=0.05 + - -c + - autovacuum_analyze_scale_factor=0.1 + - -c + - shared_preload_libraries=pg_stat_statements + - -c + - pg_stat_statements.track=all + # Sized for ~16 concurrent worker DBs + template + WAL headroom. + # Each worker DB clone is ~150-250 MB post-migration, and `CREATE + # DATABASE ... TEMPLATE` can briefly double that during the copy. + tmpfs: + - /var/lib/postgresql/data:rw,size=6g + environment: + POSTGRES_USER: tachi + POSTGRES_PASSWORD: tachi + POSTGRES_DB: postgres + PGDATA: /var/lib/postgresql/data/pgdata + tachi-s3: container_name: tachi-s3 image: quay.io/minio/minio:RELEASE.2024-10-29T16-01-48Z diff --git a/typescript/client/src/app/pages/admin/AdminActionsPage.tsx b/typescript/client/src/app/pages/admin/AdminActionsPage.tsx index 00bffb274..7c0ba62c7 100644 --- a/typescript/client/src/app/pages/admin/AdminActionsPage.tsx +++ b/typescript/client/src/app/pages/admin/AdminActionsPage.tsx @@ -1,6 +1,6 @@ import useSetSubheader from "#components/layout/header/useSetSubheader"; import useApiQuery from "#components/util/query/useApiQuery"; -import { ADMIN_PAGE_SIZE } from "#lib/adminConstants"; +import { ADMIN_RECENT_HOURS } from "#lib/adminConstants"; import { MillisToSince } from "#util/time"; import React from "react"; import { Badge, Button, Form, Table } from "react-bootstrap"; @@ -92,7 +92,8 @@ export default function AdminActionsPage() { } const { actions, filters } = data; - const totalPages = Math.ceil(actions.total / ADMIN_PAGE_SIZE); + const pageSize = actions.pageSize; + const totalPages = Math.ceil(actions.total / pageSize); const currentPage = actions.page; function buildPageUrl(p: number) { @@ -122,6 +123,9 @@ export default function AdminActionsPage() {
+ Actions from the last {ADMIN_RECENT_HOURS} hours (up to {pageSize} per page). +
+ Showing the first {ADMIN_PAGE_SIZE} running jobs (oldest scheduled + first). +
+ )}