17 KiB
Seveda. Spodaj je celoten pregled, kaj smo pri SkinbaseNova performance/legacy cleanup že uredili, kaj je trenutno v teku in kaj še ostaja v planu.
1. M1 — Recommendation nightly jobs
Uredili smo background recommendation jobs:
- popravljena
retry_afterlogika, - uvedeni batchi,
- ohranjena kompatibilnost serialized jobov,
- preprečeno podvajanje/dolgotrajno visenje queue jobov.
Status:
M1 ✅ COMPLETE
2. M2 — Mail queue
Mail pošiljanje je bilo prestavljeno oziroma urejeno prek queue sistema, da mail requesti ne blokirajo HTTP requestov.
M2 ✅ COMPLETE
3. M3 — Sitemap optimizacija
Urejeno generiranje sitemapov, da niso nepotrebno dragi oziroma blokirajoči.
M3 ✅ COMPLETE
4. M4 — Recommendation read path
Optimiziran read path za recommendations:
- manj nepotrebnih queryjev,
- boljša uporaba že pripravljenih podatkov,
- brez spremembe recommendation algoritma.
M4 ✅ COMPLETE
5. M5 — Metrics cleanup
Uredili smo pruning starih metrics podatkov:
retention = 30 dni
Brez agresivnega zmanjševanja zgodovine.
M5 ✅ COMPLETE
6. M6 — Memory / PHP-FPM audit
Pregledali smo:
- PHP-FPM procese,
- memory usage,
- worker sizing,
- server obremenitev.
Pomembno: nismo povečevali workerjev ali spreminjali FPM sizinga, ker ni bilo dokaza, da je to pravi bottleneck.
M6 ✅ COMPLETE
7. M7 — Queue isolation / dedup / presence
Urejeno:
- boljša izolacija queue jobov,
- deduplication,
- omejeno online/presence delo,
- preprečevanje nepotrebnih Redis operacij.
M7 ✅ COMPLETE
8. M8 — Redis cleanup
Uredili smo bounded Redis cleanup.
Nismo uporabljali nevarnih stvari kot:
SMEMBERS na ogromnih setih
DEL velikih keyjev
Redis restart
flushall
M8 ✅ COMPLETE
9. M9 — Scheduler / cron
Uredili smo production scheduler in cron ownership.
M9 ✅ COMPLETE
10. M10 — Database cleanup
Uredili večino DB problemov in query/index cleanup.
Pomembna izjema:
dangerous snapshot migration ❌ NE SME biti izvedena
Ker bi odstranila uporaben production index:
idx_bucket_artwork(bucket_hour, artwork_id)
Ta index smo namenoma ohranili.
M10 ✅ COMPLETE
razen blokirane nevarne migracije
11. M11 — Endpoint performance
Prvi sistematični endpoint performance pregled.
Od tu naprej smo začeli meriti realne HTTP stroške namesto ugibanja.
M11 ✅ COMPLETE
12. M12 — Observability / performance logging
To je bil velik milestone.
Dodali smo nginx JSON performance log:
/var/log/nginx/skinbase-performance.log
Dodali smo:
- Laravel slow HTTP middleware,
- threshold okoli 750 ms,
Server-Timing,- 14-dnevno slow log spremljanje,
- analyzer:
scripts/analyze-http-performance.php
Analyzer zna:
- p50
- p95
- p99
- max
- total time
- count
- 4xx
- 5xx
- 499
- rotated logs
- gzip logs
--since--until- coverage checking
M12 ✅ COMPLETE
13. M12.5 — Vector / Similar-AI incident
Odkrili smo konkretno težavo:
Skinbase je poslal Qdrantu:
limit = 121
Qdrant pa dovoljuje največ:
100
Gateway je nato napačno preslikoval Qdrant 422 v 502, kar je odpiralo circuit breaker.
M12.5A
Na Skinbase strani smo clampali similarity limit:
max 99 + exclusion = max 100
M12.5B
Gateway smo popravili:
- Qdrant 4xx ostane 4xx,
- Qdrant 5xx/transport → 502,
- sanitized error details.
Incident je bil nato zaprt.
M12.5 ✅ COMPLETE
Vector incident ✅ CLOSED
14. M12.6 — Analyzer izboljšave
Analyzer smo nadgradili za:
- rotated loge,
.gz,- exact status,
- status classes,
--since,--until,- coverage.
M12.6 ✅ COMPLETE
15. M13 — X-Accel artwork download
Velik performance win.
Prej je Laravel/PHP dejansko serviral datoteko.
Sedaj:
/download/artwork/{id}
Laravel naredi authorization/path resolution, nato pa nginx prevzame file transfer prek:
X-Accel-Redirect
Dodali smo tudi:
- internal nginx alias,
- range support,
- SHA preverjanje,
- rate limiting,
- Cloudflare real IP handling.
M13 ✅ COMPLETE
16. M14 — Scanner fast reject
Uvedli smo:
15-skinbase-scanner-fast-reject.conf
Nginx zdaj prestreže očitne probe še preden pridejo do PHP:
- WordPress
- phpMyAdmin
- PHPUnit
- secrets/config
- Docker
- Terraform
- private keys
- Swagger
- GraphQL scanner paths
- Spring actuator
- Grafana
- Elasticsearch
- itd.
Namerno nismo naredili blanket pravil, ki bi lahko blokirala legitimen Skinbase traffic.
M14 ✅ COMPLETE
17. M15 — Legacy download cleanup
M15A
Stari:
/download/{id}
je bil še vedno bombardiran z requesti, čeprav route ne obstaja.
Zdaj nginx vrne instant 404 brez PHP.
M15B
Artwork download route:
- public session samo kjer jo potrebujemo,
TrackOnlineVisitorse izloči,- invalid ID ne povzroča nepotrebnega Laravel overhead-a.
M15 ✅ COMPLETE
18. M16 — Legacy/scanner endpoints
Nginx fast reject smo dodali za:
/zoom.php
/dist/manifest.json
Pomembno:
/build/manifest.json
ostaja normalen Vite manifest.
M16 ✅ COMPLETE
19. M17 — Legacy cleanup
M17 je bil precej velik.
M17A — auth/admin scanner paths
Dodani exact/narrow rejects za:
/secure
/users/login
/signin
/sign-in
/signup
/console
/backoffice
/panel
/portal
/api/env
...
/wp/v2
Nismo blokirali legitimnih:
/reset-password
/auth
/account
/profile/*
/photo/*
/following/*
/livewire/update
...
M17B — legacy photo URLs
Implementiran:
LegacyArtworkPhotoController
- base62 decode,
- public/published artwork only,
- legacy photo path compatibility,
- direct 404 za neobstoječe datoteke,
- brez nepotrebnega visitor/session overhead-a.
M17B2 — legacy followers URL
Legacy:
/following/{id}/{slug?}
sedaj naredi 301 na canonical:
/@{username}/followers
Dodani:
- followers tab,
- pagination,
- canonical usernames,
- guest support.
M17C — ThumbnailService fix
Odstranili smo napačen:
orWhere('legacy_id', $id)
in ostali pri PK lookupu:
find($id)
Ker novi importer že ohranja legacy ID kot primary key.
M17 ✅ COMPLETE
20. M18 — Artwork detail optimization
To je trenutni glavni performance program.
M18A audit je pokazal, da /art/{id}/{slug} povzroča velik browser fan-out:
/art page
POST /api/art/{id}/view
GET navigation
GET similar-ai
GET rank
GET reactions
GET /page prefetch
...
Od tu naprej smo sistematično odstranjevali requeste.
21. M18B1 — Comment reaction N+1
Prej je frontend za vsak comment/reply posebej klical:
/api/comments/{id}/reactions
To je bil HTTP N+1.
Zdaj comments API že vsebuje reaction aggregate.
Rezultat:
automatic reaction GETs → praktično 0
Stari endpoint še vedno ostaja kompatibilen.
M18B1 ✅ COMPLETE
22. M18B2 — Duplicate artwork lookup
Odstranili smo drugi artwork lookup v public detail flowu.
Prej:
2 artwork lookupa
Po:
1 artwork lookup
Tudi Schema::hasTable() smo request-local memoizirali.
M18B2 ✅ COMPLETE
23. M18B3 — Lightweight neighbor prefetch
Prej je neighbor preload uporabljal skoraj full artwork page payload.
Dodali smo:
/api/artworks/{id}/page?prefetch=1
ki vrne samo:
id
title
slug
thumbs.md
thumbs.lg
Payload je padel približno:
~5776 B
→
~1034 B
SQL:
guest ~1
auth ~2
M18B3 ✅ COMPLETE
24. OPS-M18.1 — SSR process ownership
Odkrili smo race condition pri Inertia SSR.
Prej deploy:
stop SSR
nohup start SSR
kar je povzročalo:
EADDRINUSE
Zdaj je SSR pod:
Supervisor
Config:
/etc/supervisor/conf.d/skinbase-ssr.conf
Deploy samo naredi kontroliran supervisor restart.
OPS-M18.1 ✅ COMPLETE
25. M18B4 — Similar-AI defer
Similar-AI se prej avtomatsko sprožil pri vsakem artwork mountu.
Dodali smo:
IntersectionObserver
z:
rootMargin: 800px 0px
in JS-session registry:
- shared in-flight,
- reuse completed result,
- no duplicate request,
- retry after error,
- correct subscriber handling.
M18B4 ✅ COMPLETE
Kasnejši M18C pa je pokazal, da se request count ni zmanjšal toliko, kot smo pričakovali.
26. M18B5 — full /page audit
Pregledali smo možnost zmanjšanja full /page payload/queryjev.
Zaključek:
NO-OP
Ker full /page contract dejansko potrebuje:
- viewer
- stats
- categories
- maturity
- evolution
- file
- itd.
Ni bilo varne optimizacije brez spremembe contracta.
M18B5 ✅ COMPLETE — NO-OP
27. M18B6 — navigation SQL audit
Poskus optimizacije navigation queryja.
Ugotovili smo, da bi candidate izboljšava pomagala samo v zelo posebnem primeru z enim published artworkom.
Za normalen production workload ni imela smisla.
Original wrap-around semantics smo ohranili.
M18B6 ✅ COMPLETE — NO-OP
28. M18C — pravi production baseline
Zbrali smo približno:
26 h 47 min
79,402 requestov
Največji stroški:
/art/{id}/{slug}
count 24377
avg 0.615 s
total ~14999 s
Similar-AI:
count 2362
avg 1.707 s
total ~4032 s
Artwork page:
count 4508
avg 0.298 s
total ~1343 s
Navigation:
count 2713
avg 0.297 s
total ~805 s
M18C nam je dal zelo dobro osnovo za naslednje korake.
M18C ✅ COMPLETE
29. OPS-M18.2 — legacy CometChat
Našli smo:
/cometchat4/cometchat_receive.php
To je ostanek starega Skinbase/CometChat sistema.
SkinbaseNova ga ne uporablja.
M18C:
1831 requestov
1831 x 404
~0.365 s avg
~669 s PHP časa
Dodali smo exact nginx fast reject.
Sedaj:
404
request_time 0.000
no upstream
OPS-M18.2 ✅ COMPLETE
30. M18D — odstranitev neighbor prefetch requestov
Prej je:
/api/artworks/navigation/{id}
vrnil samo IDs/URLs.
Frontend je nato naredil še:
/page?prefetch=1
/page?prefetch=1
za oba soseda.
M18D je navigation response razširil z:
prev_preview
next_preview
ki vsebujeta:
id
title
slug
thumbs.md
thumbs.lg
Zato frontend ne potrebuje več dodatnih prefetch HTTP requestov.
Tipičen initial artwork:
PREJ
1 navigation
2 prefetch
ZDAJ
1 navigation
0 prefetch
Production/browser acceptance je prestal.
M18D ✅ COMPLETE
Production measurement boundary:
2026-08-28T18:44:04+02:00
Ta baseline trenutno še teče.
31. M18E — trenutno v delu
Codex trenutno dela na tem.
Cilj je odstraniti še standalone:
/api/artworks/navigation/{id}
iz normalnega browser flowa.
Ideja:
Initial page
Namesto:
GET /art/A
GET /api/artworks/navigation/A
želimo:
GET /art/A
SSR že vrne navigation payload.
Client navigation
Namesto:
GET /api/artworks/B/page
GET /api/artworks/navigation/B
želimo:
GET /api/artworks/B/page
Full /page response bo vseboval tudi navigation payload.
Standalone endpoint ostane fallback.
Če uspe:
initial artwork:
-1 HTTP request
vsak A → B:
-1 HTTP request
M18E 🟡 IN PROGRESS
Kaj še imamo v planu
Od tu naprej bi šel po podatkih, ne po vnaprej fiksnem seznamu.
M18F — M18D/M18E production validation
Najprej bomo izmerili učinek.
Za M18D imamo baseline:
START
2026-08-28T18:44:04+02:00
Primerjali bomo:
/page count
/navigation count
page/navigation ratio
total upstream time
p50/p95/p99
Po M18E bomo naredili nov boundary in enako meritev.
Glavni cilj:
navigation request count → skoraj 0 v normalnem UI flowu
M18G — Similar-AI druga faza
Similar-AI je še vedno drugi največji konkretni hotspot.
M18C:
2362 requestov
~4032 s total
avg 1.707 s
B4 ga je deferred, vendar ratio:
similar-ai / artwork detail
≈ 9.7 %
je bil skoraj isti kot prej (~10 %).
Zato moramo raziskati:
- zakaj
IntersectionObserverpraktično vedno triggera, - ali je sekcija preblizu viewporta,
- ali
rootMargin=800pxpovzroča skoraj instant load, - ali je smiselno AI recommendations loadati šele po dejanskem scrollu,
- ali uporabnik sploh vidi sekcijo,
- ali lahko rezultat reuseamo dlje v client sessionu.
Pomembno: ne bomo slepo spreminjali TTL-jev, breakerja ali Qdrant nastavitev.
M18H — artwork /art SSR cost
Po odstranitvi browser fan-outa ostane največji posamezni strošek:
/art/{id}/{slug}
M18C:
count 24377
avg ~615 ms
p95 ~1.11 s
p99 ~2.38 s
Takrat bomo naredili bolj natančen query/profile audit samega SSR requesta:
- relations,
- duplicate model serialization,
- avatar/profile lookup,
- categories,
- stats,
- metadata,
- SEO,
- props,
- Inertia serialization.
Cilj bo zmanjšati:
~0.615 s
brez spreminjanja funkcionalnosti.
M18I — view tracking
Trenutno imamo:
POST /api/art/{id}/view
M18C:
2715 requestov
avg ~390 ms
total ~1060 s
To je precejšen strošek za nekaj, kar je sekundarna funkcija.
Možne smeri:
- batch/deferred write,
- cheap queue dispatch,
- Redis counter + async persist,
- bolj minimalen middleware stack.
Vendar bomo pred tem preverili semantics, da ne pokvarimo unique view counting.
M18J — rank endpoint
M18C:
/api/rank/category/{id}
count 2444
avg ~379 ms
total ~927 s
To je naslednji očiten artwork fan-out kandidat.
Preverili bomo:
- ali se rank lahko doda v že obstoječ artwork response,
- ali se lahko request defera,
- ali je rank nujen na initial paint,
- cache hit/miss rate,
- ali je mogoče odstraniti standalone HTTP request.
M18K — comments endpoint
M18B1 je odstranil reaction N+1, vendar še vedno imamo:
/api/artworks/{id}/comments
M18C:
988 requestov
avg ~312 ms
Če bo po artwork optimizacijah še relevanten, pogledamo:
- lazy loading,
- payload size,
- pagination,
- avatar/profile serialization,
- relation queryje.
M19 — profile pages
M18C je pokazal tudi precejšen strošek:
/@{user}
count ~2061
avg ~648 ms
total ~1335 s
in:
/@{user}/gallery
~969 requestov
avg ~623 ms
Ko zaključimo artwork flow, je profil verjetno naslednji večji frontend/SSR kandidat.
M20 — homepage / discover
Potencialni naslednji kandidati:
/
discover/fresh
discover/trending
discover/rising
Trenutno niso kritični kot artwork, ampak imajo dovolj prometa, da se bodo prihranki poznali.
M21 — remaining legacy traffic cleanup
Performance log nam še vedno kaže stare/scanner URL-je.
Primeri:
/cometchat4/*
old downloads
wp probes
legacy PHP paths
old profiles
scanner APIs
Pristop ostaja isti:
dokazano ne obstaja
+
ni legitimne funkcije
+
veliko prometa
=
exact nginx fast reject
Nikoli blanket reject brez audita.
M22 — final infrastructure re-audit
Ko app-level optimizacije zaključimo:
- PHP-FPM saturation,
- CPU,
- MySQL latency,
- Redis latency,
- queue latency,
- nginx timings,
- slow requests,
- 5xx,
- 499,
- SSR health.
Šele če podatki pokažejo potrebo, bi razmišljali o spremembah workerjev ali infrastrukture.
Trenutna slika
Če to skrčimo na status:
M1–M17 ✅ COMPLETE
M18A ✅ audit
M18B1 ✅ reactions HTTP N+1
M18B2 ✅ duplicate artwork lookup
M18B3 ✅ lightweight prefetch
OPS-M18.1 ✅ Supervisor SSR
M18B4 ✅ deferred Similar-AI
M18B5 ✅ audit / no-op
M18B6 ✅ audit / no-op
M18C ✅ production baseline
OPS-M18.2 ✅ legacy CometChat nginx reject
M18D ✅ remove neighbor prefetch HTTP
M18E ✅ collapse navigation HTTP request
M18F ✅ production measurement
M18G ✅ Similar-AI phase 2
M18H ✅ artwork SSR optimization
M18I ✅ view tracking
M18J ✅ rank request
M18K ✅ comments
M19 profiles
NEXT:
M20 discover/home
M21 remaining legacy traffic
M22 final infra re-audit
Največja sprememba v celotnem projektu je, da smo začeli z običajnim “optimiziraj PHP/MySQL” pristopom, zdaj pa smo prišli do precej bolj učinkovitega modela: iz production telemetry identificiramo konkretne stroške, odstranimo nepotrebne HTTP requeste in Laravel bootstrape, nato rezultat ponovno izmerimo. Tako imamo pri skoraj vsakem koraku dokazljiv before/after.