Uzmanlık
Backend geliştirici: API'nin arkasındaki kuyruk, veritabanı ve sunucu
Senkron bir akışı RabbitMQ'ya taşıdığımda gördüm ki kullanıcının beklemesi yıllardır kazara bir sıra üretiyormuş; asıl iş, o sıra kalkınca yüzeye çıkanları düzeltmekti.
- Başlangıç
- 2010 Başlangıç
- yıl
- 16 yıl
- yazı
- 26 yazı
- proje
- 3 proje
Bu ekosistemde ben
Backend geliştirme benim için bir dil değil, bir sorumluluk sınırı: kullanıcının gördüğü ekranın arkasında kalan her şey. Sözleşme, kuyruk, veritabanı ve sunucu aynı işin dört yüzü; biri hakkında verilen karar diğer üçünü de bağlıyor.
Gözlemlenebilirlik, altyapının kod olarak yönetilmesi ve geri alma stratejisi gibi başlıkları mimari konular olarak sade.dev tarafında yazıyorum.
Backend'de ne yapıyorum
Ortak noktaları kullanıcının gördüğü ekranın arkasında kalmaları: sözleşme, kuyruk, veritabanı ve sunucu.
-
API sözleşmesi
Yanıt biçimi, hata sözleşmesi ve sürümleme kararı kod yazılmadan önce netleşiyor. Aksi hâlde istemci tarafındaki her ekip kendi varsayımını yazıyor ve o varsayım altı ay sonra sizin sorununuz oluyor.
-
Asenkron teslim hattı
Kuyruk, consumer ve outbox relay'i; senkron beklemeyi kullanıcının önünden alan yapı. Aynı disiplinin ikinci yarısı idempotency: bir isteğin yan etkisiz olarak tekrar edilebilmesi.
-
Veritabanı kararları
Index ve plan davranışı sezgiyle değil ölçümle seçiliyor; sezgi burada özellikle kötü çalışıyor. Ölçümün kendisi bu sayfada değil, yöntemi ve ortamıyla birlikte research defterinde duruyor.
-
Arama ve sunucu katmanı
İlişkiselin yetmediği sınırda Elasticsearch; ortam Docker ile, deploy hattı GitHub Actions ile, önde nginx. Kapsam uygulama seviyesinde kalıyor.
Kuyruk teslim garantisi vermez
Sık gördüğüm yanılgı, iş kuyruğa taşındığında dayanıklı hâle geldiğinin sanılması. Zincir sırayla okunur; her adım bir öncekinin açık bıraktığı yeri kapatıyor.
- 01 Yazma ile publish iki ayrı sistem
- Veritabanına INSERT ile kuyruğa publish arasında atomiklik yok. Yazma başarılı olup publish ağ takılmasıyla düşerse veritabanında kayıt vardır ve event hiç yola çıkmamıştır.
- 02 Mesaj aynı transaction'a yazılır
- Mesaj kuyruğa doğrudan basılmıyor; aynı transaction içinde bir outbox tablosuna satır olarak yazılıyor. Kayıt ile mesajın kaderi böylece tek bir işleme bağlanıyor.
- 03 Relay satırları yayımlar
- Ayrı bir relay o satırları okuyup kuyruğa veriyor. Mesajın kaybolmadığı yer burası — ama garantinin tamamlandığı yer değil.
- 04 Garantiyi idempotent tüketici tamamlar
- Outbox at-least-once verir: mesaj kaybolmaz, tekrar edebilir. İşlenen her event id'si aynı transaction içinde processed_messages tablosuna yazılınca tekrar zararsızlaşıyor.
Veri ve sunucu tarafında kapsamım
Veritabanı kararlarını sezgiyle vermiyorum; sunucu tarafında ise kapsam uygulama seviyesinde kalıyor.
-
Ölçülmüş index kararları
Index tercihleri, sorgu planları ve tablo büyüdükçe değişen davranış ölçülerek seçiliyor. Ölçüm olmadan verilen karar, bugün hızlandırdığı sorguyu yarın yavaşlatabiliyor.
-
Tam metin arama katmanı
İlişkisel deponun yetmediği yerde devreye giriyor. Kök bulma, eş anlamlı genişletme ve alakalılık sıralaması ilişkiselde de var; ayrı bir katmanı getiren şey bu işlerin varlığı değil, ölçekte ve çok dilli metinde nereye kadar taşıdığı.
-
Uygulama seviyesinde sunucu
Çalışma ortamının paketlenmesi, test ve dağıtım hattının kurulması, uygulamanın önündeki katmanın yapılandırılması.
Değişikliği güvenli kılan sıra
Arka uçta asıl zorluk yeni bir özelliği yazmak değil, çalışan bir sistemi bozmadan değiştirmek.
- 01 Şemayı eski ve yeni kodun aynı anda ayakta olduğu pencereye göre planlamak
- Eski ve yeni kod bir süre birlikte çalışıyor; şema değişikliği o pencereyi hesaba katmadan yazıldığında kırılan taraf her zaman çalışan sistem oluyor.
- 02 Veri taşımasını geri alınabilir adımlara bölmek
- Tek seferde yürüyen bir taşımanın geri dönüşü yok. Adımlara bölünmüş bir taşıma, yanlış giden adımda durabiliyor.
- 03 Test yüzeyini üç yerde ayrı tutmak
- İş kuralının doğrudan sınandığı yer, dış sınırın taklit edildiği yer ve akışın uçtan uca yürütüldüğü yer. Değişikliğin bedeli, getirdiği rahatlıktan küçük kalmalı.
Altyapıyı mimari konu olarak burada yazmıyorum
Gözlemlenebilirlik, IaC ve rollback stratejisi gibi başlıklar bu sayfada kendi başlarına durmuyor; buradaki kapsamım uygulama seviyesinde kalıyor. Altyapıyı bir mimari konu olarak yazdığım yer sade.dev.
Bu alandaki son yazılar
Partial index'i hız için seçmeyin
10 milyon ölü satırlı bir Postgres kuyruk tablosunda dört index stratejisini ölçtüm: kazanan beklediğim gerekçeyle kazanmadı ve tek bir ayar onu 1.673 kat yavaşlattı.
Aynı mesaj, farklı sonuç: event-driven mimaride determinizm
Event-driven mimaride aynı mesaj neden farklı sonuç üretir? Gerçek bir fatura akışından: determinizmi bozan gizli girdiler ve geri kazandıran dört hamle.
Dual-write'tan outbox'a: idempotent tüketim ve alan-seviyesi şifreleme günlüğü
Aynı projede dual-write yüzünden kaybolan event'ler beni transactional outbox'a, at-least-once teslim de idempotent tüketime götürdü. Bir de GDPR alanlarını x-gdpr-sensitive ile satır seviyesinde şifreledim. Gerçek bir projeden notlar.
Bu alandaki ölçümler
Yedi PHP framework'ü aynı yükte ölçtüm: fark, istek gerçek iş yaptıkça küçülüyor
Aynı donanımda, aynı PHP sürümünde ve aynı yedi rotayla, Laravel, Symfony, CodeIgniter, Yii2, Phalcon, Laminas ve Slim saniyede kaç isteği ne gecikmeyle karşılıyor?
Bulgu
Boş bir rotada en hızlı ile en yavaş arasında 4,4 kat var (Slim 25.975, Laravel 5.966 istek/sn). Ama istek gerçek iş yapmaya başlayınca fark kapanıyor: veritabanından tek satır çeken bir istekte 3,7 kata, yirmi satır çekende 3,5 kata iniyor. Phalcon boş rotada üçüncüyken veritabanı isteğinde beşinciye düşüyor — C eklentisi olmak, sorgu beklerken bir işe yaramıyor. Asıl pahalı olan şey framework seçimi değil: Laravel'in kendi varsayılan web middleware grubu, aynı yanıtı 5.858'den 2.176 istek/sn'ye düşürüyor — yani tek bir varsayılan, framework'ler arasındaki farkın çoğundan daha pahalı.
17 gün önce ölçüldü
Partial index on beş dakikada üç yüz seksen kat şişti — ve autovacuum bir kez bile gelmedi
Sürekli churn altındaki bir kuyruk tablosunda partial index'in küçüklüğü kalıcı mı, ve varsayılan autovacuum ayarları ona yetişiyor mu?
Bulgu
Canlı küme beş bin satırda sabit dururken partial index 0,1 MB'dan 38,2 MB'a çıktı — üç yüz seksen kat. Küçüklüğü canlı satır sayısından geliyor, şişme hızı iş hacminden, ve ikisi arasında hiçbir bağ yok. Composite index oransal olarak daha az şişti (%42) ama mutlak olarak daha çok (+126 MB) ve şişerken belleğe sığmayı bıraktı: gecikmesi 0,52 ms'den 61 saniyeye çıktı, kuyruğu 126 bine tırmandı. On beş dakikada 1,75 milyon ölü satır birikti ve autovacuum **bir kez bile koşmadı** — varsayılan eşik tablonun tamamına göre ölçekleniyor (50 + 0,2 × 10 milyon ≈ 2 milyon), churn ise küçük canlı kümede oluyor.
15 gün önce ölçüldü
Postgres partial index'i generic plana çevirmedi: kırk çalıştırma, kırk custom plan
Hazırlanmış bir deyimde Postgres partial index'li sorguyu kendiliğinden generic plana çeviriyor mu — yoksa bin altı yüz yetmiş üç katlık uçuruma ancak elle mi düşülüyor?
Bulgu
Postgres geçişi reddediyor. Partial index'te kırk çalıştırmanın kırkı da custom plan — sayaç 40/0. Reddetmesinin sebebi tam olarak felaketin kendisi: generic plan partial index'i kullanamaz, bu yüzden tahmini maliyeti yüksek çıkar ve planner onu seçmez. Composite index ise ders kitabındaki gibi altıncı çalıştırmada geçiyor (5/35) ve hiçbir şey kaybetmiyor. Yani bin altı yüz yetmiş üç katlık uçurum gerçek ama çitli: ona düşmek için `plan_cache_mode = force_generic_plan` yazmak gerekiyor.
15 gün önce ölçüldü
Bu alanda geliştirdiklerim
Çok hesaplı gelir-gider takibini tek modelde toplayan finans çekirdeği; web + iOS + Android'de çalışan Parantaj'a dönüştü.
Şu an ne yapıyor
Tek hesap/işlem modeli çoklu hesap yönetimini, bütçeyi, hedefleri ve raporlamayı aynı çekirdekte taşıyor; web, iOS ve Android istemcileri yayında. Kişisel ve kurumsal finansını tek yerden takip etmek isteyen kullanabilir.
Swipenor: skoru istemci hesaplarsa sunucu neye güvenir?
Kaydırmalı doğru/yanlış bilgi yarışması; asıl mesele oyun değil, istemcinin ürettiği skoru imzayla doğrulanabilir kılmak.
Şu an ne yapıyor
Kaydırmalı doğru/yanlış bilgi yarışması bir mobil uygulama olarak çekirdek döngüsüyle uçtan uca çalışıyor; skoru istemci üretse de server_nonce ve HMAC imzasıyla sunucu tarafında doğrulanıyor. swipenor.com uygulamanın tanıtım sayfasıdır, oyunun kendisi değil.
academia.sh: teori ile sahadan gelen problem aynı derste durabilir mi?
Ücretsiz ve herkese açık, metin merkezli öğrenme platformu; ders kitabı teorisiyle sektörde karşılaşılan problemleri tek ders akışında tutuyor.
Şu an ne yapıyor
Ücretsiz ve kayıt duvarı olmayan bir öğrenme platformu olarak yayında: alan-müfredat-kurs-ünite-ders hiyerarşisi, tam metin arama, ilerleme takibi ve doğrulanabilir sertifika çalışıyor. Bilgisayar bilimleri müfredatını okumak isteyen herkes bugün kullanabilir.
Bu alanda üretime çıkan işler
Parantaj
Devam ediyorOwner
Kişisel ve kurumsal finansal yönetim platformu. Gelir-gider takibi, bütçe planlama, detaylı raporlama ve çoklu hesap yönetimi sunar.
BabelQueue
Devam ediyorFounder & Developer
BabelQueue, farklı dillerde yazılmış servislerin aynı kuyruğu serileştirme kilidine takılmadan paylaşmasını sağlayan, dilden bağımsız bir mesaj kuyruğu standardıdır. PHP'nin serialize()'ı gibi dile özgü formatlar yerine; her dilin doğal olarak okuyabildiği, schema_version 1'de dondurulmuş katı bir JSON zarfı tanımlar. Redis ve RabbitMQ üzerinde, sidecar ya da broker eklentisi olmadan, %2'nin altında ek yükle çalışır.
Looplio
Devam ediyorOwner
Looplio — tekrar eden işleri, bakımları ve kontrolleri yeniden kullanılabilir şablonlar ve otomatik oluşturulan checklist'ler hâline getiren periyodik otomasyon platformu (Web + iOS + Android).
Bu alanda sorulanlar
Bu eksende 21 soru yanıtlandı.
Container'ı non-root çalıştırınca mount edilen storage dizinine yazamıyorum, izinleri nasıl çözerim?
Kullanıcıyı build-time `ARG UID` ile sabitleyin, `COPY --chown` kullanın, named volume'de entrypoint yalnız `storage` ve `bootstrap/cache`'i chown etsin.
OpenAPI şemasını önce mi yazmalıyım yoksa koddan mı üretmeliyim?
`openapi.yaml`'ı tek doğru kaynak yapıp contract-first gidin; drift'i bitiren şey yön değil, CI'da spec'i koda karşı doğrulayan yeşil contract testtir.
Sadece 'pending' satırları taranan bir kuyruk tablosunda partial index kullanmalı mıyım?
`WHERE status='pending'` partial index'ine `ORDER BY` kolonunu da katın, `FOR UPDATE SKIP LOCKED` ile eşleyin ve `'pending'`'i sorguya literal geçin.
Birlikte kullandığım teknolojiler
-
RabbitMQ
Servisler arası asenkron teslim; outbox relay'inin çıkış kapısı.
-
Redis
Önbellek, sayaç ve kısa ömürlü kilit.
-
PostgreSQL
Kuyruk tablosu ve index davranışını üzerinde ölçtüğüm veritabanı.
-
MySQL
En uzun süre birlikte çalıştığım ilişkisel depo.
-
Elasticsearch
İlişkiselin yetmediği yer: tam metin arama ve log analizi.
-
Docker
Geliştirme ortamı ve dağıtım paketi.
-
nginx
Uygulamanın önündeki katman.
-
GitHub Actions
Test ve deploy hattı; sunucuya SSH ile çıkan adımlar burada tanımlı.
Sık sorulanlar
7 soru
-
Backend tarafında kaç yıldır çalışıyorsun?
2010'den bu yana, 16 yıldır. PHP'ye başladıktan iki yıl sonra işin ağırlığı sözleşmeden kuyruğa kaydı ve orada kaldı; bu sitede kayıtlı 26 yazının çıktığı yer o dönem.
-
Backend tarafında en sık hangi problemle geliyorlar?
Çoğunlukla "kuyruğa taşıdık ama bazı işler kayboluyor" ile geliyorlar. Cevap neredeyse her seferinde aynı yerde: veritabanına yazma ile kuyruğa publish etme iki ayrı sistem ve aralarında hiçbir atomiklik garantisi yok.
-
API tasarımına nereden başlıyorsun?
Sözleşmeden. Yanıt biçimi, hata sözleşmesi ve sürümleme kararları kod yazılmadan önce netleşmezse, istemci tarafındaki her ekip kendi varsayımını yazıyor. Bu sitede 26 yazının önemli bir kısmı bu üç başlığın etrafında.
-
Kuyruk kullanmak teslim garantisi verir mi?
Hayır. Transactional outbox mesajın kaybolmamasını sağlar ama size at-least-once verir — mesaj tekrar edebilir. Garantiyi tamamlayan şey kuyruk değil, tüketicinin idempotent olmasıdır.
-
Index kararlarını nasıl veriyorsun?
Ölçerek. Partial index'i yalnızca "küçük ve hızlı" diye seçmek bir tuzak: generic plan devreye girdiği anda partial index kullanılamıyor. İyi haber, uçurumun çitli olması — Postgres bu geçişi kendiliğinden yapmıyor, oraya düşmek için ayarı elle zorlamak gerekiyor. Ölçümün tamamı research defterinde duruyor.
-
Sunucu tarafını da sen mi kuruyorsun?
Evet, uygulama seviyesinde. Docker'la ortamı, GitHub Actions'la deploy hattını ve nginx'i kendim kuruyorum; altyapının kendisini bir mimari konu olarak yazdığım yer sade.dev.
-
Kaç projede bu yığını kullandın?
Bu sitede kayıtlı projelerin 3 tanesi doğrudan bu kuyruk/veritabanı yığınının üstünde duruyor.
Birlikte çalışalım
Bu ekosistemde bir işiniz varsa, ne yaptığınızı anlatın; nasıl kurulacağını konuşalım.