Files
SkinbaseNova/docs/optimization-m13-x-accel-downloads.md
klevze e9cf754b37 Enable nginx X-Accel for original artwork downloads.
Hand originals to nginx after auth so PHP is not in the byte path. Keep DOWNLOAD_ACCEL_ENABLED off until the internal location is verified.
2026-08-29 12:25:37 +02:00

81 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M13 — Artwork Download X-Accel-Redirect Offload
Application tests and nginx snippet only. **Do not enable in production until Stage A nginx is verified.**
## Pre-M13 evidence
`/download/artwork/{id}` (M12 analyzer, skinbase.org):
| | |
| --- | --- |
| count | 2363 |
| p50 / p95 / p99 / max | 0.535 / 1.526 / 3.335 / **16.037** s |
| avg request_time | 0.7175 s |
| avg upstream_response_time | 0.3762 s |
| avg bytes | ~528 KB |
Slow requests show PHP finishing in ~0.3–0.5 s while `request_time` stays large: FPM is stuck sending the body.
After X-Accel: `upstream_response_time` is still Laravel work; `request_time` can still include slow clients. The win is **freeing PHP-FPM**, not always a tiny nginx `request_time`.
## Existing application
- Route: `GET /download/artwork/{id}` → `ArtworkDownloadController`
- Flag: `DOWNLOAD_ACCEL_ENABLED` default **false**
- Path: `DOWNLOAD_ACCEL_PATH=/internal/originals`
- Laravel originals root: `uploads.local_originals_root` (symlink `SkinbaseNova/storage` → shared)
- Fallback: `response()->download()` — **must stay**
Controller already authorizes, checks file, records analytics, then optionally sets `X-Accel-Redirect`.
## nginx mapping
Production vhost is **not** in this repo: `/etc/nginx/sites-enabled/skinbase.org.conf`.
Snippet: `deploy/nginx/download-accel.conf`
```nginx
location ^~ /internal/originals/ {
internal;
alias /opt/www/virtual/SkinbaseNova.releases/shared/storage/app/originals/artworks/;
}
```
Alias uses the **canonical shared tree** so a release switch cannot point nginx at a missing release directory. Laravel still uses `local_originals_root`; the X-Accel URI is only the relative hash path (`/27/21/….zip`).
`internal;` is mandatory: a browser GET to `/internal/originals/...` must 404.
No sendfile/tcp/buffering changes in M13.
## Rollout (operator — not executed here)
### Stage A — nginx only (`DOWNLOAD_ACCEL_ENABLED=false`)
1. Backup: `sudo cp /etc/nginx/sites-enabled/skinbase.org.conf /etc/nginx/sites-enabled/skinbase.org.conf.bak-$(date +%Y%m%d-%H%M%S)`
2. Insert the location inside the HTTPS `server { }` (before `location /`).
3. `sudo nginx -t` — stop if it fails.
4. `sudo systemctl reload nginx` — **not** restart.
5. Direct access: `curl -I https://skinbase.org/internal/originals/<relative-path>` → **404**.
6. Optional: `sudo -u www-data test -r <absolute-original> && echo READABLE`
### Stage B — Laravel flag
7. Set `DOWNLOAD_ACCEL_ENABLED=true` (path already `/internal/originals`).
8. `sudo -u skinbase php artisan config:cache` if config is cached.
9. Verify with tinker as `skinbase` (flag true, path `/internal/originals`).
10. Public: `curl -fL https://skinbase.org/download/artwork/<ID>` → 200, attachment, size matches original.
11. Range: `curl -H 'Range: bytes=0-99'` → **206**, `Content-Range: bytes 0-99/<size>`, 100-byte body.
12. Repeat direct internal URL → still 404.
Application deploy: `bash ./sync.sh`. Never `git pull` on production.
## Rollback
First: `DOWNLOAD_ACCEL_ENABLED=false` then `sudo -u skinbase php artisan config:cache` if needed. PHP fallback returns immediately. Nginx location can stay.
If nginx must be reverted: restore the `.bak-*` vhost, `nginx -t`, reload.
## Analyzer
Keep M12 `skinbase-performance.log`. After activation, `upstream_response_time` should stay near Laravel work; do not expect `request_time` to collapse for slow clients.