Rails – N+1 Sorguları ve Çözümü

Veritabanınızda 5 tane “exam_center” kaydı olduğunu varsayalım.

ve aşağıda ki gibi bir model yapınız olduğu;

Bu exam_center’ların tamamını bir index sayfasında göstermek istediğinizde, her bir exam_center için 1 tane country, 1 tanede city sorgusu yapılacaktır. Yani her bir kayıt için view’dan çağırdığınız ilişki sayısı kadar sorgu yapılacak. 5 tane veritabanı kaydımız olduğu için, toplamda 10 sorgu çekilecek ve ek olarak ilk sorgu olan exam_center’ın kendisinin sorgulanmasıyla birlikte toplamda 11 sorgu edecektir. Buna kısaca N+1 durumu ismi verilir.

Uygulamanız içerisinde controller ve view dosyalarınızın bu şekilde olduğunu varsayalım:

Bu yapıda kurgulanmış bir sistemde index metodunu çağırdığımda loglarıma şu sorgular düşüyor:

5 veritabanı kaydı için çekilen 11 sorguyu görebilirsiniz.

includes Metodu

N+1 sorgu durumundan kurtulmak için “includes” metodu kullanılır.

Controller’ı bu şekilde düzenleyerek tekrar logları kontrol edelim:

Yapılan sorgu sayısı 7’ye düştü ancak hala Country için yapılan sorgularda bir değişiklik bulunmuyor. Burada “nested” bir lişki yapısı olduğu için dökümantasyonu takip ederek controller’ı tekrar düzenlememiz gerekecek:

Tekrar loglarımızı kontrol ettiğimizde yapılan sorgu sayısının 3’e düştüğünü ve sorgular için harcanan toplam sürenin azaldığını görebilirsiniz:

Hepsi bu kadar. Detaylar için Rails dökümantasyonuna göz atabilirsiniz:

http://guides.rubyonrails.org/active_record_querying.html

GEM – Pundit ile Authorization (Cancan Alternatifi)

Neden Cancan değilde Pundit?

  • Cancan 1 yılı aşkın süredir güncellenmiyor. Pundit sık sık güncelleniyor.
  • GEM’in geliştiricisi olan Ryan Bates’in uzun zamandır kayıplara karışmış durumda. Ryan Bates ayrıca Railscasts için 2 Eylül’de dönüş yapacağını söylemişti ancak 11 Temmuz itibariyle hala dönüş yapmadı ve kendisinden haber yok. Pundit’in arkasında İsveç kökenli elabs firması var.
  • Cancan’i başka sürdüren, sahip çıkan kimsenin olmaması sebebiyle açık olan onlarca issue bulunuyor. An itibariyle cancan’de 257, pundit’te ise 20 tane açık issue var. Cancan’e yapılan pull request’ler uzun bir süredir merge edilmemiş.
  • Rails 4.1 yayında olmasına rağmen cancan hala Rails 3 üzerini ve strong parametreleri desteklemiyor. Pundit ise Rails 4 destekliyor ve pure ruby yapısından dolayı uzunca bir süre versiyon sorunu çıkarmayacak gibi duruyor.
  • Cancan’in çok magic bir yapısı var. Pundit’te işler daha sizin kontrolünüzde.
  • Pundit cancan’e göre çok daha hafif ve hızlı çalışan bir GEM.
  • Pundit’in bağımlılıkları Cancan’in bağımlılıklarından daha az.
  • Pundit sayesinde authorization yapınızı hiç modele ve controller’a bulaşmadan – dolayısıyla fat controller’lar – modeller yaratmadan kurabiliyorsunuz.

Meraklısına ilave okumalar:

  1. Cancan vs. Pundit: http://www.distilnetworks.com/cancan-vs-pundit-choose-pundit-authorization/
  2. Alternative for Cancan: http://stackoverflow.com/questions/7213927/alternative-for-cancan/10235613#10235613
  3. Cookie HQ’ın Pundit Deneyimleri: http://cookieshq.co.uk/posts/pundit/
  4. Cancan’den Pundit’e Migration: http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/

Kurulum

application_controller‘ı aşağıdaki şekilde düzenleyin:

generator’ı çalıştırarak örnek dosyaları oluşturun:

Kısa Bir Ara (Planlama)

User” isminde bir kullanıcı modeliniz olduğunu ve şöyle bir yetkilendirme sistemi hazırlamak istediğinizi varsayalım:

auth

Böyle bir yapıyı 2 şekilde kurgulayabilirsiniz:

  1. User modelinize her bir yetki ismi için boolean bir alan oluşturursunuz ve bu sayede

    gibi sorgu yaparsınız.
  2. Roller için ayrı bir model oluşturursunuz ve her bir yetkiyi orada rol olarak tanımlarsınız ve

    gibi sorgu yaparsınız. Roller ve User modelleri HABTM ilişkisine sahip olur.

Uygulamanızın ihtiyaçlarına göre herhangi bir yolu seçebilirsiniz. İlk yöntem daha basit authorization işlemleri için yeterliyken, kapsamlı ve onlarca rolün olacağı bir sistemde ikinci yöntemi tercih etmenizi öneririm.

Kullanım

Generator’ın oluşturduğu dosya/lar GEM’in özelliklerini demo etmek ve size hazır bir iskelet yapıyı sunmak için düzenlendiğinden bunları incelemek kafanızı karıştırabilir, şimdilik boşverin.

Generator tarafından app/policies dizinine oluşturulan application_policy.rb dosyası authorization sisteminiz için default ayarlarınızı tanımlayacağınız dosyadır. Burada her bir CRUD işlemi için uygulamanın nasıl davranacağını kurgulayabilirsiniz.

Koddan anlayabileceğiniz üzere her bir controller metodu için burada bir metod tanımlıyoruz ve hangi durumlarda true, hangi durumlarda ise false döndüreceğini belirtiyoruz.

Bu genellenmiş modelin işinizi görmediği durumlar için, bu modeli miras alan ayrı ayrı policy’ler oluşturabilirsiniz.

Örneğin City isminde bir modeliniz olduğunu ve burada application_policy‘nin özelliklerini yine devralmak istediğinizi ancak ek ihtiyaçlarınız olduğunu varsayalım. Bu durumda app/policies altında “city_policy.rb” isminde bir dosya oluşturmanız yeterlidir. Veya;

komutu ile bu modele özel bir policy dosyası hızlıca oluşturabilirsiniz.

edit? metodu city_policy içerisinde tekrar tanımlandığı için application_policy‘de ki kullanımı göz ardı edilecektir. Buna benzer modele özel tanımlamaları kendi policy dosyası içerisinde yapabilirsiniz.

Şimdi tanımlamalarımız tamam olduğuna göre “cities_controller.rb” dosyasına gidelim:

Ve hepsi bu kadar. Artık basit düzeyde fakat sorunsuz çalışan bir yetkilendirme sisteminiz var.

Permission Denied Hatasını Ayıklama

Tanımladığınız durumlar dışında CRUD üzerinde yetkisiz bir işlem yapıldığında uygulamanız Pundit::NotAuthorizedError hatası vererek işlemi sonlandıracaktır. Bu hatayı yakalayarak, 403 sayfamıza yönlendirmesini sağlayalım:

application_controller.rb içerisinde aşağıdaki tanımlamayı yapınız;

Scopelar ve diğer konular için GEM dökümantasyonunu takip edebilirsiniz:

https://github.com/elabs/pundit

Kolay gelsin.

Ngrok ile Localhost Tünelleme

Ngrok, yerel makinanızı tünelleyerek ve size bir subdomain vererek – yerelinizi internet üzerinde yayınlamanıza yardımcı olan bir araçtır. Bu sayede yerel makinanızda çalışan bir uygulamayı uzakta bulunan müşterinize veya ekip arkadaşınıza çalışır halde göstermek için bir yerlere deploy etmenize gerek kalmaz.

Ngrok sayesinde yazmış olduğunuz kodu kolayca demo edebilir, HTTP trafiğini takip ve replay edebilir, request’leri, form verilerini, JSON/XML datasını uygulama web arayüzü üzerinden görüntüleyebilirsiniz.

Kurulum

https://ngrok.com/ adresinden işletim sisteminize uygun dosyayı indirin. Zip arşivini uygun bir konuma (Masaüstü vb.) çıkartın.

Yardım Kılavuzu

Zip içerisinden çıkan dosyanın bulunduğu dizine terminalden düşün. Ardından;

komutu ile uygulamanın yardım sayfalarına ulaşabilirsiniz.

Doğrulama

Ngrok’un tüm özelliklerinden faydalanabilmek için websayfası üzerinden ücretsiz olarak kayıt olabilirsiniz. Daha sonra ise dashboard sayfasından edindiğiniz anahtar ile doğrulama yapın;

Kullanım

Yerel makinanızın 80.portunu internete açmak ve erişim için bir domain almak istiyorsanız kısaca aşağıdaki komutu kullanabilirsiniz;

Komutu çalıştırdığınızda internet üzerinde çalışan bir domain adresiniz anında hazır olacaktır.

ngrok-80

Forwarding” alanında yazan adresi istediğiniz kişilere göndererek, yerel makinanız üzerinde bağladığınız porta client gibi bağlanmalarını sağlayabilirsiniz.

Port Ayarları

Ngrok’a başka bir port numarası vererek (örneğin Rails geliştiricileri 3000 portunu) aynı yöntemle yerelinizi paylaşabilirsiniz.

ngrok-3000

Subdomain Ayarları

Ngrok’un size vermiş olduğu rastgele subdomain işinizi görmüyor ve kendi alan adınızı belirlemek istiyorsanız “subdomain” parametresini kullanabilirsiniz;

Bu durumda ngrok adresiniz http://ais.ngrok.com ve https://ais.ngrok.com olacaktır.

HTTP Authentication Ayarları

Ngrok’un bir diğer şahane özelliği ise HTTP authentication desteklemesi. Bu özelliği kullanabilmek için “httpauth” parametresi girmeniz yeterli:

HTTP Trafik Analizi

Gelen HTTP trafiğini analiz edebilmek için ngrok adresinize 1 kez request (URL’yi tarayıcınızda açarak sayfayı görüntüleyin!) yapın.

Daha sonra ise http://localhost:4040/http/in adresinden gelen trafiği analiz edebilir, replay edebilir ve uygulamanızın nasıl davrandığını izleyerek hatalı durumları yakalayabilirsiniz.

ngrok-requests

Rails'te CSV İşlemleri – Roo ve SmarterCSV GEM'leri

Roo GEM

CSV, XLS ve ODS’yi başarıyla okuyor, ancak tüm yaptığı bundan ibaret – başka bir özelliği yok.

Roo GEM (CSV Load)

Roo GEM (CSV Read Örneği)

SmarterCSV GEM

Smarter_CSV sadece CSV ile çalışıyor ancak özellik bakımından çok daha üstün.

SmarterCSV (CSV Read)

process redis’ten tanıdık. Bu betik tamda istenilen şekilde hash döndürür:

Burada göründüğü üzere smarter_csv bir takım normalizasyon işlemleride yapıyor. Örneğin “TC Kimlik No” başlığını downcase ve snakecase yaparak tc_kimlik_no şekline sokmuş ancak “Öğrenci No” için aynısını başarılı yapamamış “Öğrenci_no” gibi birşey ortaya çıkmış. Bu durum key_mapping yaparken önemli.

SmarterCSV (Key Mapping)

:Öğrenci_no yüzünden çalışmayacağını düşündüğüm betik sorunsuz çalışıyor ve şunu döndürüyor:

SmarterCSV (Chunk Size)

Bu parametre her bir dizide bulunacak value:key ikilisinin sayısını belirtiyor ve sonuçları buna göre döndürüyor.

SmarterCSV (Chunk Processing ve Virtual Attributes)

CSV dosyamızı bizim belirlediğimiz miktarda yığınlara (chunk) bölerek işletebiliriz.

CSV Dosyamız=>

Betik =>

Yanıt =>

SmarterCSV (Single Chunk Processing)

Aynı CSV’yi parçalara bölmeden direk tek parça olarak işletmek için;

Yanıt olarak bu döner =>

SmarterCSV (Modellere Yazmak – Toplu)

Import dosyası =>

Deneme amacıyla ilgili işlemleri Participation modelinin index controller’ına ekledim.

Bu action’dan beklenen, Participations sayfası her ziyaret edildiğinde beklenen şey CSV’nin işlenmesiydi. Sayfayı ziyaret ettiğimde kayıtlar eklenmedi, çünkü modelde bir takım validasyonlar buna engel oluyordu.

Bu kısıtlamamdan dolayı CSV işlenmesine rağmen kayıtlar işlenmedi — ancak ve ancak request arka planda asılı kalmış bekliyordu. Bir süre sonra sistemde zaten olan kayıtları temizledim ve CSV’den gelen kayıtlar tıkır tıkır veritabanına yazıldı.

Yani özetle herhangi bir sebeple process tamamlanamazsa iptal olmuyor ve askıda kalıyor, şartlar uygunlaşıncada işlem kaldığı yerden devam ediyordu. Bu hem iyi, hemde tehlikeli bir durum!

SmarterCSV (Modellere Yazmak – Parça Parça)

Aynı işlemi chunk’lar halinde parça parça veritabanına yazmak için şöyle yapılabilir =>

SmarterCSV (Modellere Yazmak – Resque ile)

Henüz bunu denemedim fakat dökümantasyona göre bu şekilde olması gerekiyor =>

BOM’lu CSV’lerde Unicode

Windows’ta aksi belirtilmediği sürece tüm dosyala BOM’lu yazılıyor. BOM’lu CSV dosyalarında unicode problem çıkarttığı için şöyle bir şey kullanılmalıymış: