Files
SkinbaseNova/docs/optimization-plan.md
T
klevze fe1054aa32 Update the optimization roadmap through the latest milestones.
Keep the production optimization plan aligned with the work already landed on develop.
2026-09-20 14:51:12 +02:00

17 KiB
Raw Blame History

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:

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,
  • TrackOnlineVisitor se 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 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:

/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
M20             ✅ discover/home
M21             remaining legacy traffic

NEXT:
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.