Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Laravel Kuyruk ve Çoklu Kiracı Modelinde Başımın Belaya Girdiği 3 Saatlik Kaybım
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Laravel Kuyruk ve Çoklu Kiracı Modelinde Başımın Belaya Girdiği 3 Saatlik Kaybım

Yazılım

Laravel Kuyruk ve Çoklu Kiracı Modelinde Başımın Belaya Girdiği 3 Saatlik Kaybım

teknomers
Son güncelleme: 15 Nisan 2026 03:24
teknomers
Paylaş
Paylaş

Bütün birim testlerimden, özellik testlerimden ve manuel testlerden geçen bir hatanın sonrasında, üretimde gerçek bir kullanıcının bir butona tıkladığında patlaması üzerinde bir postmortem.



Kurulum

Schoolytics isimli açık kaynaklı çoklu kiracı destek sistemi üzerinde çalışıyorum. Çoklu kiracılık, satır düzeyinde: tek bir Postgres veritabanı, her kiracıya özgü tabloda bir tenant_id sütunu bulunuyor ve her kiracı modeli, global kapsam ekleyen bir BelongsToTenant trait’ini kullanıyor:

trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope('tenant', function (Builder $q) {
            if ($tenantId = tenant('id')) {
                $q->where($q->getModel()->getTable().'.tenant_id', $tenantId);
            }
        });

        static::creating(function ($model) {
            $model->tenant_id ??= tenant('id');
        });
    }
}

Basit ve etkili. Her kiracının sorgusu otomatik olarak kısıtlanıyor. tenant_id’yi unuttunuz mu? Başka bir kiracının verisini sızdırmanız fiziksel olarak imkânsız.



Özellik

Bir veli, kamu portalı üzerinden bir sorun gönderdiğinde, IssueCreated olayı tetikleniyor. Bir sıraya alınmış dinleyici, bir Python mikroservisini çağırarak duygu analizini yapıyor ve ardından sonucu issue_ai_analysis satırına yazıyor. Basit bir olay → sıraya alınmış dinleyici → veritabanı yazımı.

class IssueCreated
{
    public function __construct(public Issue $issue) {}
}

class PerformAiAnalysis implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    public function handle(IssueCreated $event): void
    {
        $score = Http::post(config('services.ai.url'), [
            'text' => $event->issue->description,
        ])->json();

        IssueAiAnalysis::updateOrCreate(
            ['issue_id' => $event->issue->id],
            ['sentiment' => $score['label'], 'confidence' => $score'confidence']]
        );
    }
}

Yeşil testler. Tinker’da çalışıyor. Yayınlandı.



Çöküş

Üretimdeki ilk gerçek gönderim:

Illuminate\Database\Eloquent\ModelNotFoundException
No query results for model [App\Models\Issue].

Ancak sorun var. SELECT * FROM issues WHERE id = 189 yapabiliyorum ve onu görebiliyorum. Dinleyici, açıkça 189 id’li bir sorunu referans alan bir olayla çağrılıyor — ve sonra Laravel onu yeniden oluştururken ModelNotFoundException fırlatıyor.



Tuzak

İşte sıraya alınmış bir dinleyicinin yaşam döngüsü:

  1. Kontrolcü event(new IssueCreated($issue)) çağrısını yapar
  2. Laravel, dinleyicinin ShouldQueue olduğunu görür, olayı SerializesModels ile serileştirir
  3. Olay, tam Issue nesnesi olarak değil — yalnızca App\Models\Issue + birincil anahtar (189). Bu, SerializesModels‘in amacıdır; yükleri küçük ve her zaman taze tutar.
  4. İşçi başlar, işi alır ve (new Issue)->newQueryForRestoration(189)->firstOrFail() çağrısını yaparak modeli yeniden oluşturur.

4. aşamada hata meydana geliyor. İşçi süreci daha hiç kimse bağlamına sahip değil — henüz başlatıldı. Yani tenant('id') null döner. Bu da BelongsToTenant‘in global kapsamının aşağıdaki sorguyu oluşturmasına neden olur:

SELECT * FROM issues
WHERE id = 189
  AND issues.tenant_id IS NULL   -- 💀
LIMIT 1

Hiçbir satır. ModelNotFoundException.

Asıl sorun: bu, senkron modda asla başarısız olmaz (serileştirme gidiş dönüşü yoktur) ve testler genellikle Queue::fake() veya senkron sürücüleri kullanır. Hata, yalnızca gerçek bir kuyruk çalışanı gerçek bir kiracı isteği ile çalıştırıldığında görünmez hale gelir.

Laravel’ın stancl/tenancy paketi, çalışan üzerinde kiracı bağlamını geri yükleyen bir QueueTenancyBootstrapper ile birlikte gelir — ama bu, SerializesModels::restoreModel() çalıştıktan sonra çalışır. Çok geç. Model zaten ölmüştü.



Çözüm

Kiracıya özel bir Eloquent modelini doğrudan sıraya alınmış bir olayda veya işte koymayın. Ölçüleri kaydedin ve handle() içinde kiracılığı kendiniz başlatın:

class IssueCreated
{
    public function __construct(
        public readonly int    $issueId,
        public readonly string $tenantId,
    ) {}
}

class PerformAiAnalysis implements ShouldQueue
{
    public function handle(IssueCreated $event): void
    {
        // Kiracı modeli kendisi kiracı-özel değildir - bu nedenle güvenli bir şekilde bulunur
        $tenant = \App\Models\Tenant::find($event->tenantId);
        tenancy()->initialize($tenant);

        try {
            $issue = Issue::findOrFail($event->issueId);

            $score = Http::post(config(), [
                => $issue->description,
            ])->json();

            IssueAiAnalysis::updateOrCreate(
                [=> $issue->id],
                [=> $score'label'], => $score'confidence']]
            );
        } finally {
            tenancy()->end();
        }
    }
}

Ve bunu basit değerlerle yayınlayın:

event(new IssueCreated(
    issueId:  $issue->id,
    tenantId: tenant(),
));

Artık kod incelemede uyguladığım üç kural var:

  1. Sıraya alınmış olaylar ve işler yalnızca ölçüleri saklar. Yapılandırıcı imzalarında Eloquent modelleri yok.
  2. Kiracı verilerini etkileyen her handle() metodu, önce tenancy()->initialize() çağrısını yapmalı ve finally içinde tenancy()->end() çağrısını gerçekleştirmelidir.
  3. Tenant modeli kesinlikle BelongsToTenant kullanmamalıdır (aksi takdirde onu zaten kiracı bağlamına sahip olmadan bulamazsınız — tavuk-yumurta durumu).



Neden SerializesModels hâlâ doğru varsayılan

Tuzak gerçek, ancak trait’in iyi sebepleri vardır: küçük yükler, her zaman taze veriler, bayat özellik hataları yok. Çözüm, onu terk etmek değil — trait’in tek bir global veritabanı bağlamı varsaydığını kabul etmektir, bu da kiracı boyutu eklendiğinde bozulur.

Tek kiracı Laravel kullanıyorsanız, modelleri geçirmeye devam edin. Satır düzeyinde yalıtım sağlayan çoklu kiracı Laravel kullanıyorsanız, ölçekler + manuel tenancy()->initialize() sizin arkadaşınızdır.



Sorunu kalıcı hale getirmem

Gerçekten kuyrukları çalıştıran basit bir özellik testi ekledim:

it(, function () {
    $tenant = Tenant::factory()->create();
    tenancy()->initialize($tenant);

    Queue::connection()->... // bunu yakalamaz
    // Gerçek veritabanı kuyruk sürücüsü kullanın:
    config([=> ]);

    $issue = Issue::factory()->create();
    event(new IssueCreated(issueId: $issue->id, tenantId: $tenant->id));

    tenancy()->end(); // bağlam olmadan çalışan simülasyonu
    $this->artisan()->assertExitCode(0);

    expect(IssueAiAnalysis::withoutGlobalScopes()
        ->where(, $issue->id)->exists())->toBeTrue();
});

Kiracılığı çalıştırmadan önce bitirmek kritik bir satırdır — bu üretimde Supervisor’ın yaptığı şeyi çoğaltır.


Özet: Eğer stancl/tenancy kullanıyorsanız ve satır düzeyinde yalıtım sağlıyorsanız, asla bir kiracıya özel Eloquent modelini sıraya alınmış yükün içine koymayın. ID + kiracı ID’yi ölçüler olarak geçin, handle()’da tenancy()->initialize() çağrısını yapın. Testleriniz bunu yakalamaz — yalnızca gerçek bir kuyruk işçi bunları yakalar.

Kaynak kod: https://github.com/sharjeelz/eliflammeem-git — desen app/Listeners/PerformAiAnalysis.php içindedir.

Eğer bu size 3 saat kazandırdıysa, repo üzerinde bir ⭐ bırakın. Eğer daha önce karşılaştıysanız, çözümünüzü yorumlarda duymak isterim.

Kaynak: Orijinal Makale

Contents
  • Kurulum
  • Özellik
  • Çöküş
  • Tuzak
  • Çözüm
  • Neden SerializesModels hâlâ doğru varsayılan
  • Sorunu kalıcı hale getirmem
Tek Süreçin Ötesinde: Bulut İçin Laravel Konteynerlerinin Mimarisi
Yapay Zekanın Neden Yapılandırılmış Koda İhtiyacı Var
SM-2 Aralıklı Tekrar Algoritmasını Kart Uygulamama Nasıl Uyguladım
Laravel’da Çoklu Kiracılık: Kiracı Başına Veritabanı vs. Paylaşılan Veritabanı – Pratik Bir Kılavuz
PHP 8.4’te Tip Güvenli Koleksiyonlar: Dizilerin Sahip Olmasını İstediğim Özellikler
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Acil: Microsoft Nisan 2026 Güncellemeleri 167 Kusuru ve 2 Sıfır Günü Düzeltildi
Sonraki Makale Microsoft büyük ekran Surface Hub dokunmatik panellerden vazgeçiyor mu?

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Final Fantasy 7 Yenilemesinde En Sevdiği Unsuru Paylaştı
Oyun
Dell’in yeni XPS 14’ü neredeyse her alanda daha iyi!
Liste
MSI ve Gigabyte’tan 5K 27 inç Mini-LED monitörler geldi
Donanım
Acil! Sessiz Fidye Grubu Hukuk Firmalarını Hedef Alıyor
Siber Güvenlik
Görkemli Bir Yaratım: Japon Gotik Korku Masalı
Liste
Anycubic Photon Mono 4, 190$ altına düştü; 50$ tasarruf et!
Donanım
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?