Laravel Türkiye Discord Kanalı Forumda kod paylaşılırken dikkat edilmesi gerekenler!Birlikte proje geliştirmek ister misiniz?

kursatcanciger evet geçmişte remember_token ile açılan oturumlar hala geçerli fakat kullanıcı tekrar login olursa diğer cihazlardan logout oluyor.

mgsmus kötümser kilitleme yapamıyorum çünkü o cüzdan üzerinde hali hazırda başka işlemlerde yapılıyor.İyimser kilitleme çözüm gibi duruyor fakat asenkron bir ödeme sistemi kullandıgımdan bazı sorunlar var. Bana iyimser kilitleme ile ilgili temiz bir kod örneği yazma şansınız var mı ?

    drWeb Cache lock kullanırken yapılan işleme göre key adı vererek başka işlemleri etkilemesini engelleyebilirsiniz:

    $key = 'wallet:'.$request->user()->id; // wallet:5
    
    Cache::lock($ey, 10)->get(function () {
        // ...
    });

    Bu işlemi bir controller yöntemi içinde yaptığınızı düşünün. Sadece giriş yapmış kullanıcı (5), sadece o controller yöntemini kullanan bir işlem yaptığında kilitten etkilenecek.

    Veri tabanı düzeyinde ise transaction ve lock birlikte kullanacaksınız:

    DB::transaction(function() {
        $balance = Payment::sharedLock()
            ->sum('amount');
    
        //...
    });

    Böylece bakiye hesaplanırken satırların değişmesi engellenmiş olacak ve herhangi bir hata olursa transaction yapılan tüm veri tabanı işlemlerini geri alacak.

    Bu tür sistemlerle uğraşırken ayrıca firstOrCreate, updateOrCreate gibi race-condition riski taşıyan yöntemlerden de kaçınmanız lazım.

      benim alpinejs ile cozumum. Botuna tıklandıgında disabled olup butonuda istedigin gibi guncelleye bilirsin. Ben lutfen bekleyiniz seklinde buton yazısını değiştiriyorum.

        <form method="post" action="" class="" enctype="multipart/form-data"  x-data="{ buttonDisabled: false }"
          x-on:submit="buttonDisabled = true">
       <x-primary-button class="ml-4"   x-bind:disabled="buttonDisabled"
                          x-text="buttonDisabled ? 'Lütfen bekleyin...' : 'Kaydet'">
                              {{ __('Kaydet') }}
                          </x-primary-button>

        aeneas Frontend ile yapacağınız her şey sadece bir makyajdan ibaret...

        mgsmus

            try {
                // Kullanıcıya özgü bir kilidi oluştur
                $lockKey = 'wallet:' .  Auth::user()->id;
        
                // Kilidi oluştur ve en fazla 10 saniye boyunca bekleyerek diğer işlemleri engelle
                $lock = Cache::lock($lockKey, 10);
                $lock->block(10);
        
                // Kilidi aldık, şimdi diğer işlemleri gerçekleştirebiliriz
        
                return redirect()->back()->with('success', 'İşlem başarıyla gerçekleştirildi');
            } catch (LockTimeoutException $e) {
                // Timeout (süre aşımı) durumunda yapılacak işlemler buraya eklenir.
        
                return redirect()->back()->with('error', 'İşlem şu anda gerçekleştirilemiyor, lütfen daha sonra tekrar deneyin');
            } finally {
                // Kilidi serbest bırak
                optional($lock)->release();
            }

        Nerede hata yapıyorum aynı farklı tarayıcıdan cihazdan aynı kullanıcıya anda veya 1-2 saniye arayla post attığımda işlem başarıyla gerçekleşiyor.Hiç bir şekilde hata mesajı almıyorum :/

          drWeb Sadece şu şekilde deneyin:

          $lockKey = 'wallet:' .  Auth::user()->id;
          
          $lock = Cache::lock($lockKey, 10);
          
          if ($lock->get()) {
          
              // İşlemler...
              
              $lock->release();
          
              return redirect()->back()->with('success', 'İşlem başarıyla gerçekleştirildi');
          
          } else {
              return redirect()->back()->with('error', 'İşlem şu anda gerçekleştirilemiyor, lütfen daha sonra tekrar deneyin');
          }

            mgsmus
            Maalesef aynı anda veya 1-2 saniye gecikmeli de post atsam
            return redirect()->back()->with('success', 'İşlem başarıyla gerçekleştirildi');
            CACHE_DRIVER=file şeklinde ayarlı bununla alakası olabilir mi ?

              bu arada feedback olması açısından az önce laravel.gen.tr site bildirimi -1 e düştü 😃

              drWeb $lock = Cache::lock($lockKey, 10); yaptığınızda 10 saniye kilitleniyor. Kilitleyen istek $lock->get() yaptığında true, bekleyen ise false alıyor. $lock->release(); yaptığınızda ise kilidi açıyorsunuz, 10 sn geçerliliğini yitiriyor, yarıda kesiyorsunuz yani. Yapılan işlem çok kısa sürdüğü için, kilitle-aç şeklide hızlıca gerçekleştiği için diğer istek hemen kilidi sahipleniyor, o yüzden size sanki engellemiyormuş gibi geliyor ama aslında işlem aynı anda değil sırayla gerçekleşiyor. Sonuçta siz iki işlemin de gerçekleşmesini ama aynı anda gerçekleşmemesini istiyorsunuz. Bakiye örneğinden yola çıkarsak, iki istek aynı anda geldiğinde lock sayesinde bakiye sorgusu kilidi ilk sahiplenende 20, diğerinde bir öncekindeki harcama işleminden dolayı 10 verecek. Siz eğer içeride yeterli bakiye var mı kontrolü yapıyorsanız ve yoksa hata veriyorsa 3. istekte bakiye 0 olarak geleceği için ise hata verecek.

              Eğer istekteki işlem bitsin bitmesin, verdiğim süre kadar işlem yapılmasını istemiyorum, rotayı kitlesin derseniz üstteki açıklamama göre bu durumda yapmanız gereken $lock->release(); kullanmamak çünkü mesela 10 saniye verdiyseniz kilit 10 saniye sonra otomatik açılacak.

                mgsmus teşekkürler bu konuyu çözmüştüm bunu yazmak için foruma döndüğümde cevabınızı gördüm.Desteğiniz için teşekkür ederim.Bu sorunu yaşayıp çözüm arayan arkadaşlar için de yanıtınızı en iyi yanıt olarak işaretledim.

                AtomicLocks kullandık fakat sharedLock kullanmadığımız takdirde ne gibi birgüvenlik sorunu yaşarız bu konuda bizi aydınlatır mısınız ?

                  drWeb Kasıt olmadan ya da kasıtlı yapılan işlemler sonucu kayıtların istenmeyen şekilde değişmesi bazı güvenlik sorunlarına yol açabilir. Bunları önceden kestirmek mümkün değil. Kullanıcı hakkı olmayan şeyler elde edebilir, bilgiler sızabilir vs...

                  Dikkat etmeniz gereken 2 nokta var:

                  1. Veri tabanı düzeyinde yapılan lock işlemini kullanıcı bazlı ayıramazsınız. Mesela aşağıdaki PostgreSQL'de bir örnek (MySQL için sadece BEGIN yerine START TRANSACTION yazıyorsunuz)
                    BEGIN;
                    SELECT * FROM users WHERE is_active = 1 FOR UPDATE;
                    COMMIT;
                    Burada users tablosunu uygulama genelinde işlem bitinceye kadar kitlemiş oluyorsunuz. Diğer tüm sorgu istekleri bunun bitmesini bekleyecek. Bunu isteyebilirsiniz de bakiye örneğindeki gibi istemeyebilirsiniz de. Buna göre hangisini kullanacağınızı siz seçeceksiniz.
                  2. Çok fazla paralel istek gelen bir yerde veri tabanı düzeyinde kitleme kullanırsanız büyük ihtimalle performans sorunu yaşayacaksınız, kitlenen tablo değil veri tabanı/uygulama olacak. O yüzden dikkat etmek lazım.