PLAN v3 Kapsamlı Optimizasyon

Muzibu Ana Sayfa Performans Optimizasyonu

2864ms → ~400ms hedefi

26 Aralık 2025 - v3 (Dinamik güncellemeler dahil)

📊 Optimizasyon Özeti

284
Mevcut Query
~30
Hedef Query
2864ms
Mevcut Süre
~400ms
Hedef Süre
P1

Favorites N+1 Çözümü

(174 query → 1 query)

📝 Basit Anlatım

Şu anda her şarkı kartı için "Bu favorilerde mi?" diye ayrı ayrı soruyoruz. Bunun yerine sayfa açılırken tüm favorileri tek seferde çekip JavaScript'e vereceğiz. Kullanıcı favori eklerse anında güncellenir, sayfa yenilemeye gerek kalmaz.

🔧 Teknik Özet

  • • FavoriteService: Tek query ile tüm favorite ID'ler
  • • Layout'ta window.userFavorites inject
  • • favorites.js: Server data ile init (API call yok)
  • • Blade'deki isFavoritedBy() kaldırılacak
Kazanç: ~80ms, 173 query azalma
P2

songs_count + total_duration Cache

(Subquery → Cached Field)

📝 Basit Anlatım (Herkes İçin)

Şu anda her playlist/albüm için "Kaç şarkı var? Toplam süre ne?" diye ayrı ayrı hesaplıyoruz. Bu çok yavaş. Bunun yerine bu bilgiyi önceden hesaplayıp saklayacağız.

Örnek: Bir playlist'te 50 şarkı var. Her seferinde saymak yerine, tabloya "songs_count: 50, total_duration: 10800" yazacağız. Yeni şarkı eklenince bu sayıları otomatik güncelleyeceğiz.

🔧 Teknik Plan

1. Migration (Tenant Only)

database/migrations/tenant/

// muzibu_playlists tablosu

$table->unsignedInteger('songs_count')->nullable();

$table->unsignedInteger('total_duration')->nullable();

// NULL = henüz hesaplanmamış (eski veri)

// 0 = hesaplandı, gerçekten 0

Not: Central'a eklemeye gerek yok çünkü muzibu_playlists/albums sadece tenant DB'de.

2. Model Accessor (Lazy Calculation)

Playlist.php, Album.php

// getSongsCountAttribute()

if ($this->attributes['songs_count'] === null) {

$count = $this->songs()->count();

$this->updateQuietly(['songs_count' => $count]);

return $count;

}

return $this->attributes['songs_count'];

Nasıl çalışır: İlk çekildiğinde null ise hesaplar ve DB'ye yazar. Sonraki isteklerde direkt DB'den okur (query yok).

3. Song Observer (Otomatik Güncelleme)

Modules/Muzibu/app/Observers/SongObserver.php

created() → Album songs_count++, total_duration += duration
deleted() → Album songs_count--, total_duration -= duration
updated() → duration değiştiyse total_duration güncelle
restored() → Soft delete'den geri gelirse ekle

4. Playlist Pivot Observer

Playlist-Song pivot tablosu için

attach() → Playlist songs_count++, total_duration += song.duration
detach() → Playlist songs_count--, total_duration -= song.duration

Not: Playlist many-to-many ilişkisi olduğu için pivot event'lerini yakalamalıyız.

5. Artisan Recalculate Command

İlk çalıştırma + eski veri fix

php artisan muzibu:recalculate-counts

// Tüm playlist ve album'leri tarar

// songs_count ve total_duration hesaplar

// Progress bar ile gösterir

Ne zaman kullanılır:
• Eski DB'den veri import edilince
• Manuel düzeltme gerekince
• Tutarsızlık tespit edilince

🔄 Dinamik Güncellemeler (Senin Sorduğun)

Senaryo 1: Yeni şarkı yükleniyor

  1. 1. Admin panelden şarkı yüklenir → Song::create()
  2. 2. SongObserver::created() tetiklenir
  3. 3. Album->increment('songs_count')
  4. 4. Album->increment('total_duration', $song->duration)
  5. 5. ✅ Anında güncellendi!

Senaryo 2: Playlist'e şarkı ekleniyor

  1. 1. Kullanıcı "Playlist'e ekle" tıklar
  2. 2. $playlist->songs()->attach($songId)
  3. 3. Playlist model event tetiklenir
  4. 4. songs_count ve total_duration güncellenir
  5. 5. ✅ Anında güncellendi!

Senaryo 3: Eski DB'den veri geldi (NULL)

  1. 1. Import sonrası songs_count = NULL
  2. 2. İlk sayfa yüklemesinde getSongsCountAttribute() çağrılır
  3. 3. NULL görünce → $this->songs()->count() hesaplar
  4. 4. updateQuietly() ile DB'ye yazar
  5. 5. ✅ Sonraki isteklerde query yok!
Kazanç: ~100ms, subquery'ler kaldırılıyor
P3

Diğer Optimizasyonlar

Subscription Session Cache

3 query → Session

Aynı kullanıcının subscription'ı 3 kez sorgulanıyor. Session'a cache'le.

Corporate Account Check

27ms → Session

Kurumsal hesap kontrolü de session'a alınabilir.

📋 Uygulama Sırası

  1. 1

    P1: Favorites N+1 Çözümü

    En kolay, en büyük kazanç (174 query → 1)

  2. 2

    P2: Migration oluştur ve çalıştır

    songs_count + total_duration nullable field'lar

  3. 3

    P2: Model accessor'ları ekle

    Lazy calculation - null ise hesapla

  4. 4

    P2: Observer'ları oluştur

    Song + Playlist pivot event'leri

  5. 5

    P2: Recalculate command

    Mevcut verileri hesapla

  6. 6

    Test & Karşılaştırma

    Telescope'da query sayısı kontrolü

📁 Oluşturulacak/Güncellenecek Dosyalar

Yeni Oluşturulacak:

  • • Modules/Favorite/app/Services/FavoriteService.php
  • • database/migrations/tenant/xxx_add_cache_fields.php
  • • Modules/Muzibu/app/Observers/SongObserver.php
  • • Modules/Muzibu/app/Console/RecalculateCounts.php

Güncellenecek:

  • • Modules/Muzibu/app/Models/Playlist.php
  • • Modules/Muzibu/app/Models/Album.php
  • • resources/views/themes/muzibu/layouts/app.blade.php
  • • public/themes/muzibu/js/player/features/favorites.js
  • • ~15 blade component (isFavoritedBy kaldır)

✅ Onay Bekleniyor

Bu plan onaylanırsa P1'den başlayarak uygulamaya geçeceğim.

Garantiler:

  • ✓ Mevcut sistem çalışmaya devam edecek
  • ✓ Geriye dönük uyumluluk korunacak
  • ✓ NULL değerler otomatik hesaplanacak
  • ✓ Dinamik güncellemeler çalışacak

Beklenen Sonuç:

  • • 284 query → ~30 query
  • • 2864ms → ~400ms
  • • ~7x performans artışı
  • • Daha iyi kullanıcı deneyimi