Lacerate ile çalıştıysanız, muhtemelen spatie/laravel-permission kütüphanesini daha önce kullanmışsınızdır. Uygulamanıza roller ve izinler eklemek için bu kütüphane, doğru bir seçimdir. İyi bir şekilde bakım yapılmıştır, iyi belgelenmiştir ve pratikte test edilmiştir.
Peki neden laravel-permissions-redis kütüphanesini oluşturdum?
Çünkü büyük ölçeklerde veritabanı darboğaz haline gelebiliyor.
Sürekli Karşılaştığım Sorun
Sürekli Karşılaştığım Sorun
200 istek/saniye hizmet veren çok kiracılı bir SaaS uygulaması inşa ediyordum. Her bir istek, 5 ila 30 arasında izin kontrolü tetikliyordu — middleware, politikalar, Blade direktifleri, inline kapılar. Spatie ile, bu kontrollerin her biri önbellek sürücüsünü etkiliyordu ve tam bir izin dizisini deseralize edip, doğrular ile tarıyordu.
Soğuk önbellek durumunda? Tam veritabanı yeniden yüklenmesi. Önbellek geçersizliği durumunda? Her şeyi unut, bir sonraki istekte baştan inşa et. 50,000’den fazla kullanıcı varken, o “bir sonraki istek” gerçekten yavaş oluyordu.
Sayılar şöyle görünüyordu:
| Senaryo | DB sorguları (Spatie) | DB sorguları (Redis yaklaşımı) |
|---|---|---|
| 1 istek, 33 kontrol | 5 | 1 |
| 10 istek | 14 | 10 |
| 50 istek | 54 | 50 |
İlk önbellek ısınmasından sonra, Redis ile yapılan tüm izin kontrolleri ek bir veritabanı sorgusu olmadan çözülüyor. “Daha az sorgu” değil, sıfır.
Neden Redis’i Laravel önbellek sürücüsü olarak kullanmayalım?
Neden Redis’i Laravel önbellek sürücüsü olarak kullanmayalım?
Benim de ilk düşüncem buydu. CACHE_DRIVER=redis ayarını yaparak Spatie’nin mevcut önbellek katmanından Redis’in hızından yararlanmayı düşünüyorum.
Bu yardımcı oluyor, ama mimari sorunları çözmüyor:
O(n) aramalar. Spatie tüm izinleri tek bir önbellek dizisine serileştiriyor. Her
hasPermissionTo()çağrısı, bu diziyi deseralize edip tarıyor. Kullanıcı başına 200 izin varsa, her kontrol için 200 karşılaştırma yapılıyor.Nükleer geçersizlik. Herhangi bir izin değiştiğinde, Spatie
forgetCachedPermissions()çağrısını yaparak tüm önbelleği siler. Her kullanıcı, bir sonraki istekte soğuk başlangıç cezasını öder.İstek başına bellekte önbelleğe alma yok. Redis, önbellek sürücüsü olarak kullanılsa dahi, Spatie aynı istekte her kontrol için Redis’e gidiyor. Eğer middleware 5 izni kontrol ediyorsa, bu 5 Redis gidiş-dönüş yapılıyor.
laravel-permissions-redis Mimarisi
laravel-permissions-redis Mimarisi
Redis SET veri yapıları kullanarak köklü bir yaklaşım benimsedim:
auth:user:{userId}:permissions -> SET {"posts.create", "posts.edit", "users.view"}
auth:user:{userId}:roles -> SET {"admin", "editor"}
auth:role:{roleId}:permissions -> SET {"posts.create", "posts.edit"}
Her izin kontrolü tek bir SISMEMBER komutu ile yapılıyor — O(1) zaman karmaşıklığı, izin sayısına bakılmaksızın. Deserialization yok, tarama yok.
İki katmanlı önbellek
İki katmanlı önbellek
Request hits middleware
-> Check in-memory PHP array (zero I/O)
-> Miss? Check Redis SET (one SISMEMBER)
-> Miss? Query database, warm Redis, populate in-memory cache
Aynı istek içindeki ilk kontrol Redis’e hit edebilir. Ancak, aynı kullanıcı için sonraki her kontrol, PHP dizisinden bellekte çözülüyor — hiç I/O olmadan.
Cerrahi geçersizlik
Cerrahi geçersizlik
Bir kullanıcının izinleri değiştiğinde, her şeyi silmiyoruz. Sadece etkilenen kullanıcının Redis SET’lerini yeniden ısıtıyoruz. Diğer tüm kullanıcıların önbelleği sıcak kalıyor. Soğuk başlangıç tramvayı yok.
// Spatie: her şeyi sil
Cache::forget('spatie.permission.cache');
// laravel-permissions-redis: sadece değişenleri ısıt
$repository->warmUserCache($userId);
Özellik Eşitliği (ve Ötesi)
Özellik Eşitliği (ve Ötesi)
Bu kütüphaneyi “hızlı ama sınırlı” bir alternatif olmasını istemedim. Amacım, Spatie ile tam özellik eşitliği sağlamak ve sürekli ihtiyaç duyduğum ek özellikleri eklemekteydi:
API uyumluluğu
API uyumluluğu
// Bunlar Spatie ile aynı şekilde çalışır
$user->assignRole('admin');
$user->givePermissionTo('posts.create');
$user->hasPermissionTo('posts.edit');
$user->hasAnyRole('admin', 'editor');
Aynı yöntem isimleri, aynı Blade direktifleri (@role, @hasanyrole), aynı middleware (permission:posts.create). Geçiş yapmak çoğunlukla bir trait ve bir konfigürasyon dosyası değiştirmeyi gerektirir.
Spatie’de Olmayan Özellikler
Spatie’de Olmayan Özellikler
| Özellik | Açıklama |
|---|---|
| Octane desteği | Uzun ömürlü işçiler arasında otomatik bellekte önbellek sıfırlama |
| Çok kiracılılık | Kiracı başına Redis anahtar izolasyonu, Stancl/Tenancy resolver ile birlikte |
| Wildcard izinleri | posts.* posts.create, posts.edit vb. ile fnmatch() ile eşleşir |
| Üst düzey yönetici rolü | Tüm izin kontrollerini atlatmak için bir yapılandırma değeri |
| Test etme traiti | actingAsWithPermissions($user, ['posts.create']) — tek satır test kurulumu |
| Önbellek ısıtma CLI | php artisan permissions-redis:warm ile tüm kullanıcıları dağıtımda önceden ısıt |
| Seed komutu | php artisan permissions-redis:seed yapılandırmadan okur, --fresh desteği ile |
| Spatie’den geçiş yapma | php artisan permissions-redis:migrate-from-spatie ile --dry-run |
| Akan koruma kapsamı | $user->forGuard('api')->hasPermissionTo('posts.create') |
Spatie’den Otomatik Geçiş
Spatie’den Otomatik Geçiş
Bu benim için önemliydi. Eğer mevcut verilerle birlikte Spatie kuruluysa, geçiş acısız olmalıdır:
# Hiçbir şeyi değiştirmeden ne olacağını görün
php artisan permissions-redis:migrate-from-spatie --dry-run
# Geçişi çalıştır
php artisan permissions-redis:migrate-from-spatie
Bu komut, Spatie’nin tablolarını okur, yeni şemadaki eşdeğer kayıtları oluşturur ve Redis önbelleğini ısıtır. Tüm özelliklerin eşleştiği bir geçiş rehberi ile birlikte davranış farklılıklarını gösteren bir tablo bulunmaktadır.
Eğlence Olmadan Çok Kiracılılık
Eğlence Olmadan Çok Kiracılılık
Spatie’nin “takım” özelliği çalışıyor ama her pivot tablosuna bir team_id sütunu ekliyor ve dikkatli sorgu kapsamı gerektiriyor.
Redis ile, kiracı izolasyonu daha basit — anahtarları ön ekleyin:
auth:user:t:{tenantId}:{userId}:permissions
Yapılandırma iki satır:
// config/permissions-redis.php
'tenancy' => [
'enabled' => true,
'resolver' => 'stancl', // veya herhangi bir callable
],
Tam veri izolasyonu. Unutulacak sorgu kapsamı yok. Kiracılar arası veri sızıntısı yok.
Octane: Kimsenin Konuşmadığı Problem
Octane: Kimsenin Konuşmadığı Problem
Laravel Octane, uygulamanızı istekler arasında bellekte tutar. Performans için harika, PHP özelliklerinde durum önbelleği bağlantılı bir şey kullananlar için kötü.
Spatie izinleri statik özelliklerde önbellekleniyor. Octane’da, Kullanıcı A’nın izinleri Kullanıcı B’nin isteğine sızabilir. Spatie ekibi bunun farkında ve çözüm öneriyor, ancak gömülü bir çözüm yok.
laravel-permissions-redis, Octane’ın RequestReceived olayını dinleyerek tüm iç durumu otomatik olarak sıfırlar:
// config/permissions-redis.php
'octane' => [
'reset_on_request' => true,
],
Hiçbir sızdırma durumu yok. Çalışma etabı yok. Sadece çalışıyor.
Dürüst Takaslar
Dürüst Takaslar
Bu paket herkes için uygun değil. Aşağıdaki durumlar için bu paketi kullanmamalısınız:
- Redis çalıştırmıyorsanız. Bu paket Redis gerektiriyor. Eğer yığınınız MySQL + dosya önbelleği ise, Spatie doğru seçimdir.
-
getDirectPermissions()vsgetPermissionsViaRoles()ihtiyacınız varsa. Bu paket tüm izinleri düz bir küme olarak birleştiriyor. Eğer “bu izin doğrudan mı yoksa bir rol aracılığıyla mı verildi?” ayırmak gerekiyorsa, Spatie bunu daha iyi sağlıyor. - PHP 8.0 veya Laravel 8’i desteklemek zorundasınız. Bu paket PHP 8.3+ ve Laravel 11+ gerektiriyor.
- Yetkilendirme darboğazınız yoksa. Eğer saniyede 10 istek yapıyorsanız ve her biri 3 izin kontrol ediyorsa, Spatie yeterince hızlıdır. Ölçülebilir bir kazanım olmadan Redis karmaşıklığını eklemeyin.
Başlarken
Başlarken
composer require scabarcas/laravel-permissions-redis
php artisan vendor:publish --provider="Scabarcas\LaravelPermissionsRedis\LaravelPermissionsRedisServiceProvider"
php artisan migrate
# İsteğe bağlı: tüm mevcut kullanıcılar için önbelleği ısıt
php artisan permissions-redis:warm
Kullanıcı modelinize trait ekleyin:
use Scabarcas\LaravelPermissionsRedis\Traits\HasRedisPermissions;
class User extends Authenticatable
{
use HasRedisPermissions;
}
Hepsi bu kadar. API kasıtlı olarak tanıdık. Eğer Spatie kullandıysanız, bunu nasıl kullanacağınızı zaten biliyorsunuz.
Sonraki Adımlar
Sonraki Adımlar
Paket v3.0.0 sürümünde, PHP 8.3/8.4, Laravel 11/12/13 ve Redis 6/7 destekleri ile bulunmaktadır. Test paketi UUID/ULID modelleri, Octane işçileri, çok kiracılı izolasyonu ve gerçek Redis örnekleri ile entegrasyon testlerini kapsıyor.
Geri bildiriminizi, sorunlarınızı ve PR’lerinizi duymak isterim. Repo: github.com/scabarcas17/laravel-permissions-redis.
Veritabanı destekli izinlerde performans sınırlarına ulaşıyorsanız, bir deneyin. Redis sunucunuz zaten orada, şöyle ki onu çalıştırmanın zamanı geldi.
Kaynak: Orijinal Makale


