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_after` logika, * uvedeni batchi, * ohranjena kompatibilnost serialized jobov, * preprečeno podvajanje/dolgotrajno visenje queue jobov. Status: ```text M1 ✅ COMPLETE ``` # 2. M2 — Mail queue Mail pošiljanje je bilo prestavljeno oziroma urejeno prek queue sistema, da mail requesti ne blokirajo HTTP requestov. ```text M2 ✅ COMPLETE ``` # 3. M3 — Sitemap optimizacija Urejeno generiranje sitemapov, da niso nepotrebno dragi oziroma blokirajoči. ```text 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. ```text M4 ✅ COMPLETE ``` # 5. M5 — Metrics cleanup Uredili smo pruning starih metrics podatkov: ```text retention = 30 dni ``` Brez agresivnega zmanjševanja zgodovine. ```text 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. ```text M6 ✅ COMPLETE ``` # 7. M7 — Queue isolation / dedup / presence Urejeno: * boljša izolacija queue jobov, * deduplication, * omejeno online/presence delo, * preprečevanje nepotrebnih Redis operacij. ```text M7 ✅ COMPLETE ``` # 8. M8 — Redis cleanup Uredili smo bounded Redis cleanup. Nismo uporabljali nevarnih stvari kot: ```text SMEMBERS na ogromnih setih DEL velikih keyjev Redis restart flushall ``` ```text M8 ✅ COMPLETE ``` # 9. M9 — Scheduler / cron Uredili smo production scheduler in cron ownership. ```text M9 ✅ COMPLETE ``` # 10. M10 — Database cleanup Uredili večino DB problemov in query/index cleanup. Pomembna izjema: ```text dangerous snapshot migration ❌ NE SME biti izvedena ``` Ker bi odstranila uporaben production index: ```text idx_bucket_artwork(bucket_hour, artwork_id) ``` Ta index smo namenoma ohranili. ```text 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. ```text M11 ✅ COMPLETE ``` --- # 12. M12 — Observability / performance logging To je bil velik milestone. Dodali smo nginx JSON performance log: ```text /var/log/nginx/skinbase-performance.log ``` Dodali smo: * Laravel slow HTTP middleware, * threshold okoli 750 ms, * `Server-Timing`, * 14-dnevno slow log spremljanje, * analyzer: ```text 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 ```text M12 ✅ COMPLETE ``` --- # 13. M12.5 — Vector / Similar-AI incident Odkrili smo konkretno težavo: Skinbase je poslal Qdrantu: ```text limit = 121 ``` Qdrant pa dovoljuje največ: ```text 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: ```text 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. ```text 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. ```text M12.6 ✅ COMPLETE ``` --- # 15. M13 — X-Accel artwork download Velik performance win. Prej je Laravel/PHP dejansko serviral datoteko. Sedaj: ```text /download/artwork/{id} ``` Laravel naredi authorization/path resolution, nato pa nginx prevzame file transfer prek: ```text X-Accel-Redirect ``` Dodali smo tudi: * internal nginx alias, * range support, * SHA preverjanje, * rate limiting, * Cloudflare real IP handling. ```text M13 ✅ COMPLETE ``` --- # 16. M14 — Scanner fast reject Uvedli smo: ```text 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. ```text M14 ✅ COMPLETE ``` --- # 17. M15 — Legacy download cleanup ## M15A Stari: ```text /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, * `TrackOnlineVisitor` se izloči, * invalid ID ne povzroča nepotrebnega Laravel overhead-a. ```text M15 ✅ COMPLETE ``` --- # 18. M16 — Legacy/scanner endpoints Nginx fast reject smo dodali za: ```text /zoom.php /dist/manifest.json ``` Pomembno: ```text /build/manifest.json ``` ostaja normalen Vite manifest. ```text M16 ✅ COMPLETE ``` --- # 19. M17 — Legacy cleanup M17 je bil precej velik. ## M17A — auth/admin scanner paths Dodani exact/narrow rejects za: ```text /secure /users/login /signin /sign-in /signup /console /backoffice /panel /portal /api/env ... /wp/v2 ``` Nismo blokirali legitimnih: ```text /reset-password /auth /account /profile/* /photo/* /following/* /livewire/update ... ``` ## M17B — legacy photo URLs Implementiran: ```text 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: ```text /following/{id}/{slug?} ``` sedaj naredi 301 na canonical: ```text /@{username}/followers ``` Dodani: * followers tab, * pagination, * canonical usernames, * guest support. ## M17C — ThumbnailService fix Odstranili smo napačen: ```php orWhere('legacy_id', $id) ``` in ostali pri PK lookupu: ```php find($id) ``` Ker novi importer že ohranja legacy ID kot primary key. ```text 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: ```text /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: ```text /api/comments/{id}/reactions ``` To je bil HTTP N+1. Zdaj comments API že vsebuje reaction aggregate. Rezultat: ```text automatic reaction GETs → praktično 0 ``` Stari endpoint še vedno ostaja kompatibilen. ```text M18B1 ✅ COMPLETE ``` --- # 22. M18B2 — Duplicate artwork lookup Odstranili smo drugi artwork lookup v public detail flowu. Prej: ```text 2 artwork lookupa ``` Po: ```text 1 artwork lookup ``` Tudi `Schema::hasTable()` smo request-local memoizirali. ```text M18B2 ✅ COMPLETE ``` --- # 23. M18B3 — Lightweight neighbor prefetch Prej je neighbor preload uporabljal skoraj full artwork page payload. Dodali smo: ```text /api/artworks/{id}/page?prefetch=1 ``` ki vrne samo: ```text id title slug thumbs.md thumbs.lg ``` Payload je padel približno: ```text ~5776 B → ~1034 B ``` SQL: ```text guest ~1 auth ~2 ``` ```text M18B3 ✅ COMPLETE ``` --- # 24. OPS-M18.1 — SSR process ownership Odkrili smo race condition pri Inertia SSR. Prej deploy: ```text stop SSR nohup start SSR ``` kar je povzročalo: ```text EADDRINUSE ``` Zdaj je SSR pod: ```text Supervisor ``` Config: ```text /etc/supervisor/conf.d/skinbase-ssr.conf ``` Deploy samo naredi kontroliran supervisor restart. ```text OPS-M18.1 ✅ COMPLETE ``` --- # 25. M18B4 — Similar-AI defer Similar-AI se prej avtomatsko sprožil pri vsakem artwork mountu. Dodali smo: ```text IntersectionObserver ``` z: ```text rootMargin: 800px 0px ``` in JS-session registry: * shared in-flight, * reuse completed result, * no duplicate request, * retry after error, * correct subscriber handling. ```text 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: ```text NO-OP ``` Ker full `/page` contract dejansko potrebuje: * viewer * stats * categories * maturity * evolution * file * itd. Ni bilo varne optimizacije brez spremembe contracta. ```text 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. ```text M18B6 ✅ COMPLETE — NO-OP ``` --- # 28. M18C — pravi production baseline Zbrali smo približno: ```text 26 h 47 min 79,402 requestov ``` Največji stroški: ```text /art/{id}/{slug} count 24377 avg 0.615 s total ~14999 s ``` Similar-AI: ```text count 2362 avg 1.707 s total ~4032 s ``` Artwork page: ```text count 4508 avg 0.298 s total ~1343 s ``` Navigation: ```text count 2713 avg 0.297 s total ~805 s ``` M18C nam je dal zelo dobro osnovo za naslednje korake. ```text M18C ✅ COMPLETE ``` --- # 29. OPS-M18.2 — legacy CometChat Našli smo: ```text /cometchat4/cometchat_receive.php ``` To je ostanek starega Skinbase/CometChat sistema. SkinbaseNova ga ne uporablja. M18C: ```text 1831 requestov 1831 x 404 ~0.365 s avg ~669 s PHP časa ``` Dodali smo exact nginx fast reject. Sedaj: ```text 404 request_time 0.000 no upstream ``` ```text OPS-M18.2 ✅ COMPLETE ``` --- # 30. M18D — odstranitev neighbor prefetch requestov Prej je: ```text /api/artworks/navigation/{id} ``` vrnil samo IDs/URLs. Frontend je nato naredil še: ```text /page?prefetch=1 /page?prefetch=1 ``` za oba soseda. M18D je navigation response razširil z: ```text prev_preview next_preview ``` ki vsebujeta: ```text id title slug thumbs.md thumbs.lg ``` Zato frontend ne potrebuje več dodatnih prefetch HTTP requestov. Tipičen initial artwork: ```text PREJ 1 navigation 2 prefetch ZDAJ 1 navigation 0 prefetch ``` Production/browser acceptance je prestal. ```text M18D ✅ COMPLETE ``` Production measurement boundary: ```text 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: ```text /api/artworks/navigation/{id} ``` iz normalnega browser flowa. Ideja: ## Initial page Namesto: ```text GET /art/A GET /api/artworks/navigation/A ``` želimo: ```text GET /art/A ``` SSR že vrne navigation payload. ## Client navigation Namesto: ```text GET /api/artworks/B/page GET /api/artworks/navigation/B ``` želimo: ```text GET /api/artworks/B/page ``` Full `/page` response bo vseboval tudi navigation payload. Standalone endpoint ostane fallback. Če uspe: ```text initial artwork: -1 HTTP request vsak A → B: -1 HTTP request ``` ```text 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: ```text START 2026-08-28T18:44:04+02:00 ``` Primerjali bomo: ```text /page count /navigation count page/navigation ratio total upstream time p50/p95/p99 ``` Po M18E bomo naredili nov boundary in enako meritev. Glavni cilj: ```text 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: ```text 2362 requestov ~4032 s total avg 1.707 s ``` B4 ga je deferred, vendar ratio: ```text similar-ai / artwork detail ≈ 9.7 % ``` je bil skoraj isti kot prej (~10 %). Zato moramo raziskati: * zakaj `IntersectionObserver` praktično vedno triggera, * ali je sekcija preblizu viewporta, * ali `rootMargin=800px` povzroč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: ```text /art/{id}/{slug} ``` M18C: ```text 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: ```text ~0.615 s ``` brez spreminjanja funkcionalnosti. --- # M18I — view tracking Trenutno imamo: ```text POST /api/art/{id}/view ``` M18C: ```text 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: ```text /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: ```text /api/artworks/{id}/comments ``` M18C: ```text 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: ```text /@{user} count ~2061 avg ~648 ms total ~1335 s ``` in: ```text /@{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: ```text / 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: ```text /cometchat4/* old downloads wp probes legacy PHP paths old profiles scanner APIs ``` Pristop ostaja isti: ```text 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: ```text 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 NEXT: M18H artwork SSR optimization M18I view tracking M18J rank request M18K comments M19 profiles 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.