diff --git a/README.md b/README.md index 84a66283..db8d8cc9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PostgreSQL-native job processing and event bus for Rails, built on [PGMQ](https: 📖 **Documentation:** [pgbus.zoolutions.llc](https://pgbus.zoolutions.llc) — guides, flow diagrams, and a full configuration reference. (This README stays the canonical GitHub reference.) -[![Ruby](https://github.com/mhenrixon/pgbus/actions/workflows/main.yml/badge.svg)](https://github.com/mhenrixon/pgbus/actions/workflows/main.yml) +[![Ruby](https://github.com/zoolutions/pgbus/actions/workflows/main.yml/badge.svg)](https://github.com/zoolutions/pgbus/actions/workflows/main.yml) ## Table of contents @@ -1220,9 +1220,9 @@ pgbus-health --port 9394 # or PGBUS_HEALTH_PORT=9394 pgbus-health pgbus-health --port 9394 --path /livez --timeout 2 ``` -### Rolling restarts (Kamal, docker) +### Rolling restarts (dash, docker) -Kamal distributions with per-role health checks (for example the [`dash` branch](https://github.com/mhenrixon/kamal)) can rolling-restart a non-proxied job role: start the new container, poll its docker `HEALTHCHECK` until healthy, and only then `docker stop` the old one. Wire the pgbus container into that gate: +[dash](https://github.com/zoolutions/dash) (per-role health checks) can rolling-restart a non-proxied job role: start the new container, poll its docker `HEALTHCHECK` until healthy, and only then `docker stop` the old one. Wire the pgbus container into that gate: ```yaml # config/deploy.yml @@ -1253,7 +1253,7 @@ If the orchestrator's stop grace period is *shorter* than `shutdown_timeout`, do **The overlap window is safe by construction.** Between "new container healthy" and "old container stopped", two supervisors run against the same database. Nothing double-fires: queue claims use `FOR UPDATE SKIP LOCKED`, `single_active_consumer` queues arbitrate via session-level advisory locks (released the instant a killed process's connection dies), two live recurring schedulers dedup on the `(task_key, run_at)` unique record, and dispatcher maintenance is idempotent. "One scheduler per deployment" is a steady-state rule; a deploy window may briefly violate it without consequence. -**What a hard kill still costs.** Jobs killed past the drain window are redelivered after their visibility timeout (at-least-once holds) — but PGMQ's `read_ct` increments exactly like a logical failure, so a long-running job that straddles *repeated* deploy kills can be pushed to the DLQ without its code ever raising. `zombie_detection` logs exactly this pattern (`read_ct > 1` with no recorded failure). Keep jobs shorter than `drain_timeout`, or raise it (and `stop_timeout`) for queues that can't be. For `idempotent!` event handlers there is a separate crash-window caveat tracked in [#385](https://github.com/mhenrixon/pgbus/issues/385). +**What a hard kill still costs.** Jobs killed past the drain window are redelivered after their visibility timeout (at-least-once holds) — but PGMQ's `read_ct` increments exactly like a logical failure, so a long-running job that straddles *repeated* deploy kills can be pushed to the DLQ without its code ever raising. `zombie_detection` logs exactly this pattern (`read_ct > 1` with no recorded failure). Keep jobs shorter than `drain_timeout`, or raise it (and `stop_timeout`) for queues that can't be. For `idempotent!` event handlers there is a separate crash-window caveat tracked in [#385](https://github.com/zoolutions/pgbus/issues/385). ### Boot diagnostics banner diff --git a/docs/.kamal/secrets b/docs/.dash/secrets similarity index 81% rename from docs/.kamal/secrets rename to docs/.dash/secrets index a56f371e..8dff689c 100644 --- a/docs/.kamal/secrets +++ b/docs/.dash/secrets @@ -4,7 +4,7 @@ # credential Kamal needs is the registry token. # # Prefer the env var, else fall back to the gh CLI token: -# - CI: the deploy workflow sets KAMAL_REGISTRY_PASSWORD from the built-in +# - CI: the deploy workflow sets DASH_REGISTRY_PASSWORD from the built-in # GITHUB_TOKEN, so the env branch is taken and `gh` is never called. # - Local: the env var is unset, so it falls back to `gh auth token` — a bare # `kamal deploy` / `kamal setup` works with no manual export. @@ -13,4 +13,4 @@ # `${VAR:-default}` (that resolves to a malformed `:-}` → `denied`). # dotenv DOES run `$(...)` command substitution, so do the env-or-gh choice inside # a shell command rather than with dotenv's unsupported `:-` operator. -KAMAL_REGISTRY_PASSWORD=$(test -n "$KAMAL_REGISTRY_PASSWORD" && printf %s "$KAMAL_REGISTRY_PASSWORD" || gh auth token) +DASH_REGISTRY_PASSWORD=$(test -n "$DASH_REGISTRY_PASSWORD" && printf %s "$DASH_REGISTRY_PASSWORD" || gh auth token) diff --git a/docs/.dockerignore b/docs/.dockerignore index 86cac7a8..076b4c86 100644 --- a/docs/.dockerignore +++ b/docs/.dockerignore @@ -40,6 +40,7 @@ # CI / deploy / Docker meta — not part of the runtime image. /.github/ +/.dash/ /.kamal/ /Dockerfile* /.dockerignore diff --git a/docs/config/deploy.yml b/docs/config/deploy.yml index 3990f04b..7560356f 100644 --- a/docs/config/deploy.yml +++ b/docs/config/deploy.yml @@ -1,9 +1,21 @@ -# Kamal deploy → the oss-infrastructure server (Cloudflare Tunnel + kamal-proxy). +# dash deploy → the oss-infrastructure server (Cloudflare Tunnel + dash-proxy). +# `dash docs` / `dash docs proxy` is the always-current reference for every key. # service/image = the repo OWNER/REPO so the ghcr package auto-links to the # repo and GITHUB_TOKEN can push + pull it (no PAT). See docs-kit's README. service: pgbus image: zoolutions/pgbus +# dash 4 renamed the on-host proxy (kamal-proxy → dash-proxy) and migrates a +# host in place; an older CLI must not deploy this config. +minimum_version: 4.0.0 + +# A stateless docs site never rolls back far — keep the host tidy. +retain_containers: 2 + +# Status-named pages (public/502.html, 503, 504) the proxy serves in place of +# the app's during a deploy gap — paired with `proxy.intercept_errors` below. +error_pages_path: public + servers: web: hosts: @@ -15,21 +27,55 @@ ssh: proxy: host: <%= ENV["DEPLOY_DOMAIN"] %> app_port: 3000 + # TLS terminates at Cloudflare; the tunnel reaches the proxy over plain HTTP. ssl: false healthcheck: path: /up interval: 5 timeout: 30 + # --- dash-proxy per-app features ------------------------------------------ + # zstd / br / gzip negotiated at the edge; responses the app already encoded + # (Thruster) pass through untouched. + compress: true + + # RFC 9111 shared cache. Only responses the app marks `Cache-Control: public, + # max-age` are stored (Propshaft assets, /llms*.txt) — HTML carrying a session + # cookie is refused by design. `dash proxy cache stats` shows what it holds. + cache: + enabled: true + max_ttl: 300 + + # Security headers set once here instead of per app; drop server fingerprints. + headers: + response: + set: + X-Content-Type-Options: nosniff + Referrer-Policy: strict-origin-when-cross-origin + remove: + - Server + - X-Powered-By + + # Serve public/.html instead of a bare "Bad Gateway" while a + # container is swapped or unhealthy. + intercept_errors: + - 502 + - 503 + - 504 + + # Keep the health probe out of the request histograms. + exclude_metrics_paths: + - /up + registry: server: ghcr.io username: mhenrixon password: - - KAMAL_REGISTRY_PASSWORD + - DASH_REGISTRY_PASSWORD # The build context is the REPO ROOT (gem + app): the docs app depends on the # gem via `gem "pgbus", path: ".."`, which the Docker build can only resolve if -# the whole repo is in the context. Kamal runs from docs/, so `dockerfile` is +# the whole repo is in the context. dash runs from docs/, so `dockerfile` is # relative to docs/ (just "Dockerfile") while `context: ..` points at the root. builder: arch: amd64 diff --git a/docs/public/502.html b/docs/public/502.html new file mode 100644 index 00000000..e4eb18a7 --- /dev/null +++ b/docs/public/502.html @@ -0,0 +1,135 @@ + + + + + + + We're sorry, but something went wrong (500 Internal Server Error) + + + + + + + + + + + + + +
+
+ +
+
+

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

+
+
+ + + + diff --git a/docs/public/503.html b/docs/public/503.html new file mode 100644 index 00000000..e4eb18a7 --- /dev/null +++ b/docs/public/503.html @@ -0,0 +1,135 @@ + + + + + + + We're sorry, but something went wrong (500 Internal Server Error) + + + + + + + + + + + + + +
+
+ +
+
+

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

+
+
+ + + + diff --git a/docs/public/504.html b/docs/public/504.html new file mode 100644 index 00000000..e4eb18a7 --- /dev/null +++ b/docs/public/504.html @@ -0,0 +1,135 @@ + + + + + + + We're sorry, but something went wrong (500 Internal Server Error) + + + + + + + + + + + + + +
+
+ +
+
+

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

+
+
+ + + + diff --git a/lib/generators/pgbus/templates/initializer.rb.erb b/lib/generators/pgbus/templates/initializer.rb.erb index 0182387e..ab3b07f3 100644 --- a/lib/generators/pgbus/templates/initializer.rb.erb +++ b/lib/generators/pgbus/templates/initializer.rb.erb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Pgbus configuration — https://github.com/mhenrixon/pgbus +# Pgbus configuration — https://github.com/zoolutions/pgbus # # This is the real config surface. Every setting has a sensible default, so an # empty block gives you a working install; uncomment and edit what you need. diff --git a/lib/pgbus/web/authentication.rb b/lib/pgbus/web/authentication.rb index 00828155..f9a14f89 100644 --- a/lib/pgbus/web/authentication.rb +++ b/lib/pgbus/web/authentication.rb @@ -44,7 +44,7 @@ def warn_unauthenticated_dashboard Pgbus.logger.warn do "[Pgbus] Dashboard is accessible without authentication. " \ "Configure Pgbus.configuration.web_auth to restrict access. " \ - "See: https://github.com/mhenrixon/pgbus#dashboard-authentication" + "See: https://github.com/zoolutions/pgbus#dashboard-authentication" end Pgbus::Web::Authentication.auth_warned = true end