fix(deploy,traffic): clear caches before restarts, hide the token, widen the lock

- update.sh restarted the long-lived services before clearing the caches, so
  they booted from the old cached config and kept it for the life of the
  process — the release's configuration never reached the workers.
- install.sh put the Gitea token in the clone URL, which is visible in the
  process list to every local user while the clone runs. It goes through a
  short-lived askpass helper now.
- The collector's uniqueness lock expired at exactly the collection interval,
  so a delayed queue could run two collectors on the same baseline and count
  the same delta twice — throttling a customer for traffic they never used.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 23:42:12 +02:00
parent 0135f114d3
commit 2415dde288
3 changed files with 29 additions and 8 deletions

View File

@ -32,7 +32,14 @@ class CollectInstanceTraffic implements ShouldBeUnique, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $uniqueFor = 900; /**
* Comfortably longer than the 15-minute cadence. At exactly one interval the
* lock would expire as the next run is dispatched, and two collectors
* sharing a baseline would add the same delta twice throttling a customer
* for traffic they never used. The lock is released when the job finishes,
* so a longer expiry only covers the crash case.
*/
public int $uniqueFor = 3600;
public int $tries = 1; public int $tries = 1;

View File

@ -129,10 +129,21 @@ if [[ -d "$INSTALL_DIR/.git" ]]; then
else else
log "Cloning into $INSTALL_DIR" log "Cloning into $INSTALL_DIR"
install -d -o "$APP_USER" -g "$APP_USER" -m 0750 "$INSTALL_DIR" install -d -o "$APP_USER" -g "$APP_USER" -m 0750 "$INSTALL_DIR"
# The token is used for this one command and never written to disk or into
# the git remote — later pulls use a deploy key or ask. # The token goes through an askpass helper rather than into the URL: a URL
auth_url="${REPO_URL/https:\/\//https://oauth2:${GIT_TOKEN}@}" # with credentials shows up in the process list for every local user while
as_app git clone --quiet --branch "$BRANCH" "$auth_url" "$INSTALL_DIR" # the clone runs, and would end up in the remote afterwards.
askpass="$(mktemp)"
printf '#!/bin/sh\nprintf %%s "%s"\n' "$GIT_TOKEN" > "$askpass"
chmod 500 "$askpass"
chown "$APP_USER" "$askpass"
trap 'rm -f "$askpass"' EXIT
GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 \
as_app git clone --quiet --branch "$BRANCH" "${REPO_URL/https:\/\//https://oauth2@}" "$INSTALL_DIR"
rm -f "$askpass"
trap - EXIT
as_app git -C "$INSTALL_DIR" remote set-url origin "$REPO_URL" as_app git -C "$INSTALL_DIR" remote set-url origin "$REPO_URL"
fi fi
cd "$INSTALL_DIR" cd "$INSTALL_DIR"

View File

@ -114,15 +114,18 @@ in_app php artisan migrate --force
log "Rebuilding assets" log "Rebuilding assets"
in_app npm run build in_app npm run build
# Before the restarts, not after: a service that starts while the old cache is
# still on disk loads it and keeps those values for the life of its process.
log "Clearing caches"
in_app php artisan config:clear >/dev/null
in_app php artisan view:clear >/dev/null
log "Restarting services" log "Restarting services"
docker compose up -d docker compose up -d
# Workers hold their PHP classes for the life of the process; without this they # Workers hold their PHP classes for the life of the process; without this they
# keep running the code from before the update. # keep running the code from before the update.
docker compose restart queue queue-provisioning scheduler reverb docker compose restart queue queue-provisioning scheduler reverb
in_app php artisan config:clear >/dev/null
in_app php artisan view:clear >/dev/null
log "Leaving maintenance mode" log "Leaving maintenance mode"
in_app php artisan up >/dev/null in_app php artisan up >/dev/null
down=0 down=0