Trendyol OMS Replatforming Serüveni — İlk Sınav

Onur Destanoğlu
Trendyol Tech
Published in
7 min readDec 16, 2019

Bu yazıda sizlere yenilenmekte olan Trendyol Sipariş Yönetim Sisteminin (Order Management System, OMS) ilk ciddi sınavı olan 9–10–11 ve Efsane Günler kampanyaları için yaptığımız geliştirmeleri ve karşılaştığımız sorunları aktarmaya çalışacağım.

Sipariş yönetim sistemleri, elektronik ticaret sitesi kullanıcılarının yapmış oldukları alışverişin oluşma anından, kargo ile teslim anına kadar olan yolculuğunu takip etmekle yükümlü olan sistemlerdir. Bu sistemler ayrıca kullanıcıların siparişlerinden herhangi bir nedenle memnun olmamaları durumunda oluşturacakları iade isteklerinin takibinden ve iade paketlerinin yönlendirilmesinden de sorumludur. Bu yapısı ile OMS ağır yük altında performanslı çalışma ihtiyacının yanı sıra, son derece tutarlı bir şekilde çalışmakla da yükümlüdür.

Bir mikroservis uygulaması olduğu için sipariş, paketleme ve iade işlerinden sorumlu üç servisten oluşan Trendyol OMS’in, ilk ciddi sınavını vereceği 9–10–11 kampanyası öncesinde servislerimizin durumu şu şekilde özetlenebilir.

  • Java 8 uygulamaları (api, event dinleme, zamanlanmış işler tek serviste)
  • Kısmi event tabanlı mimari ile tek bir eventin alıcılarına göre değiştirerek çoklandığı yapı
  • Dış servislerle yapılan yaratma, güncelleme işlemlerinde HTTP çağrılarının yanı sıra event tabanlı mimarinin de kullanılması
  • RabbitMQ üzerinden geri bildirim kontrolü yapılmaksızın event gönderimi
  • PostgreSQL üzerinde ilişkisel veri tabanı modeli kullanımı
  • Ortak RabbitMQ sunucusu kullanımı

9–10–11 Kampanyası Öncesi Durum

Kubernetes üzerinde çalışan servislerin hizmete hazır olarak ayağa kalkma süresi temelde Spring Boot kaynaklı olarak üç dakikayı bulabiliyordu. Bu süre zarfında aldığı yükün seviyesi ile orantılı olarak hafıza ve işlemci kullanımları da ciddi oranda artıyor ve akabinde servisin sürekli olarak kendini kapatıp açmasına neden olabiliyordu. PostgreSQL’in ilişkisel veritabanı modelini kullanan ve aslında servis içinde doküman yapısında bulunan veri yapılarını ilişkisel veritabanında modelleyebilmek için birçok tablonun ilişkilendirilmesi, servislerin yazma ve okuma performanslarını yük ile birlikte ciddi biçimde düşürüyordu.

Yazma işlemleri tutarlılığın korunması amacı ile transactional olarak ele alınması gereken işlemler olduğu için veritabanının sağladığı transaction yönetimi kullanılıyordu. Ancak açılan transaction içinde birçok API çağrısı yapılarak ve uzun süreler harcanabiliyordu. Bu da beraberinde uzayan transaction süreleri oluşturarak yine veritabanı performansını ciddi derecede etkiliyordu (idle in transaction).

Yazma işlemleri sonucu üretilen eventlerin RabbitMQ sistemine aktarımı farklı bir transaction yapısı üzerinden yapılıyordu. Bu da sipariş işlemleri sonucunda gerçekleştirilen işlemlerin tutarlı bir şekilde RabbitMQ’ya aktarılamamasına neden oluyordu. Bu kayıplar ciddi iletişim sorunlarına neden olmakla birlikte ekibin özellikle büyük kampanyalar sonrasında uzun süre destek hizmeti vermesine ve OMS takımının işlerinin planlanmasında zorlanmasına neden oluyordu. OMS servislerinin RabbitMQ etkileşimini, yaptığı işlemler sonucunda gerçekleşen eventler yerine, alıcı servise göre özel olarak ayarlanmış eventleri kullanması ise üretilen ve dolayısı ile iletilemeyen event sayısını da artırarak, böylelikle olası tutarsızlıkların tüm sisteme yayılmasına neden oluyordu.

Birçok servisin ortak RabbitMQ sunucusu kullanması nedeni ile çok sayıda aktif kuyruğu üzerinde bulunduran sunucular etkin bir şekilde çalışamıyordur. Ayrıca başka RabbitMQ sunucusu üzerinde çalışan servisler ile event haberleşebilmek için doğrudan ilgili servisin RabbitMQ sunucusuna bağlanılması, sorumluluğumuzdaki servislerin doğru bir şekilde çalışması için kontrolümüz dışındaki bir çok parametrenin de doğru çalışması zorunluluğunu ortaya koyuyordu.

Yapılan Geliştirmeler

11.11 öncesinde ilk olarak OMS’in gerçekleştirdiği işlemlerin sonucu oluşan eventlerin tutarlı bir şekilde çevre sistemlere iletilebilmesi hedeflendi. Bunun için farklı transaction yapılarında (PostgreSQL ve RabbitMQ) gerçekleştirilen işlemler tek bir transaction’a taşındı. Bu amaçla bir Outbox pattern yapısı kullanılarak üretilen tüm eventlerin aynı transaction içinde PostgreSQL üzerindeki bir tabloya kaydedilmesi sağlandı. Bu işlem feature toggle yapısı ile geliştirilerek gerek görüldüğü takdirde eski event gönderim yapısının da kullanılabilir halde olması amaçlandı.

Veritabanına kaydedilen event kayıtlarının RabbitMQ sistemine gönderiminin sağlanması için düşük hafıza tüketimi ve yüksek eş zamanlı çalışma performansı sunan GoLang dilinde bir uygulama yazıldı. İlgili veritabanı tablosunu belirli aralıklarla sorgulayan uygulama ile garantili bir şekilde event gönderimini sağlandı.

Oracle Hotspot JVM yerine Eclipse Open J9 JVM kullanımına geçildi. Böylelikle daha az bellek kaynağı tükettiği gibi servisin ayağa kalkma süresi de kısalmış oldu.

Veritabanında bulunan tabloların statik veri içeren bölümü servislerin yerel hafızaları üzerinde cachelenerek sorgu performanslarının artırılması hedeflendi. Ayrıca uzun süren transaction bloklarının parçalanması ve dış servis çağrılarının transaction işlemlerinden bağımsız yapılabilmesi için geliştirmeler yapıldı.

İade servisi Domain Driven Design (DDD) kullanılarak yeniden tasarlandı. Java 11 kullanılarak tekrar geliştirilen uygulamanın tamamiyle event tabanlı mimaride olması sağlandı. Böylelikle dış servisler ile etkileşimin en aza indirgenerek, dış servislerin ihtiyaçlarından soyutlanmış bir yapı kuruldu. İlişkisel veri modeli yerine, doküman veri modeli tercih edilerek yazma ve okuma anındaki veritabanı seviyesinde gerçekleştirilen işlem sayısı düşürüldü. Doküman veri modelinin depolanması için dağıtık yapıda çalışması ve yüksek ölçeklenebilirliği nedeni ile Couchbase veritabanı tercih edildi. Bu tercihin ayrıntılı açıklamasını Erdem Erbas tarafından hazırlanan makalede bulabilirsiniz.

İade servisinin event üretimi işlemi için veritabanı logları kullanılmıştır. Doğrudan Kafka’ya aktarılan event verileri GoLang dilinde yazılmış çeşitli uygulamalar aracılığı ile RabbitMQ’ ya aktarılmakta ve böylelikle Trendyol servislerinin değişiklik yapmadan kullanabilmesi sağlanmıştır. Kafka kullanımı OMS servisleri genelinde planlanmakta olup, OMS servisleri arasındaki event tabanlı iletişimin tekrarlanabilir ve hızlı bir şekilde yapılmasını amaçlamaktadır.

Ortak RabbitMQ kullanılmasının önlenmesi amacı ile OMS servisleri için üç node’lu bir cluster kurulumu sistem ekiplerince gerçekleştirildi. Ayrıca diğer servislerin RabbitMQ clusterları ile federasyon yapısı kurularak diğer sistemler ile olan doğrudan bağı dolaylı hale getirildi. Böylelikle OMS servislerinin sağlıklı çalışabilmesi için diğer RabbitMQ clusterlerının ayakta olması zorunluluğu ortadan kalkmış oldu. Çalışan sistem üzerinde RabbitMQ clusterı geçişi kayıpsız bir şekilde gerçekleştirildi.

9–10–11 Dönemi ve Karşılaşılan Sorunlar

Daha önceki kampanya dönemlerindeki en büyük sorunumuz olan tutarsız yapı ve uzun süren destek sürecinin çözülmesini hedeflediğimiz 9–10–11 kampanyası Trendyol’un hem kullanıcı sayısı hem de anlık/genel sipariş oluşturma sayılarında kendi rekorunu kırdığı bir dönem oldu.

Bu dönemde özellikle RabbitMQ üzerinden gönderilen eventlerin kaybolmadığı gözlendi. Böylelikle servisler arası tutarsızlık önlenmiş oldu, beraberinde de süreç sonrası destek işlemi en aza indirgenmiş oldu.

Transaction yapıdaki işlemlerin iyileştirilmesi ile veritabanı üzerinde transaction işlemlerinin oluşturduğu performans sorununda iyileşmeler gözlendi. Ancak bu sefer de servislerin filtreleme ile ilgili sundukları API’lerin veritabanı üzerindeki etkisi ön plana çıktı. Problem olarak, ilişkisel veritabanı üzerinde sıklıkla gerçekleştirilen JOIN ve DISTINCT sorguları veritabanı uzmanları tarafından işaret ediliyordu.

Bu dönemde ortaya çıkan bir diğer yazılım problemi de OMS servislerinin sipariş ve paketleme kısımlarında herhangi bir versiyon kontrolü (optimistic locking) yapılmamış olmasıydı. Bu problem nedeni ile aynı anda gelen isteklerin işlenmesi yine tutarsız sonuçların ortaya çıkmasına neden olmaktaydı.

Son olarak karşılaştığımız ve sistem üzerinde ciddi bir sorun yaşamamıza neden olan problem ise veritabanı kaynaklı olarak yaşandı. Yoğun kullanım anında paketleme servisinin kullandığı PostgreSQL ciddi kaynak tüketimi yapmaya başladı. Veritabanı uzmanlarının incelemesi sonucunda indexlerin yeterli performansta çalışmadığı belirlendi ve gün içine sorgulara uygun yapıda yeni indexler oluşturuldu ve hem sipariş hem de paketleme servisinin veritabanı kaynakları artırılarak performans sorunu giderilmiş oldu.

Veritabanı kaynaklarının tüketilmesindeki en temel nedenlerden birinin açılan çok fazla sayıda bağlantı olduğu belirlendi. Ayrıca servisler arası tutarlılığı sağlamak amacı ile oluşturduğumuz Outbox pattern uygulamasının veritabanı üzerinde yoğunluk oluşturduğuna da dikkat çekildi. Bu noktada Debezium gibi alternatif outbox uygulamalarının da değerlendirilmesine karar verildi.

İade servisinin ise beklendiği gibi çok daha az veritabanı yükü oluşturduğu ve tasarım aşamasından dahil edilen versiyonlama ve kontrollü event gönderimi yöntemleri ile tutarlı yapıda veri oluşturup servis edebildiği gözlendi. Servisin API performansı ise artan yük ile birlikte beklenen şekilde ölçeklenerek hızlı bir şekilde istekler karşılandı.

Yapılan Geliştirmeler

Event kaybı nedeni ile problem yaşanmaması sevindirici olsa da özellikle veritabanı kaynaklı sorunlar ile çok da başarılı bir kampanya geçiremeyen OMS de öncelikli hedef olarak sipariş ve paketleme servislerinin veritabanı etkileşimlerinin iyileştirilmesi olarak belirlendi.

Alternatif Outbox pattern uygulamalarının veritabanı log dosyalarını kullanmasına rağmen çoğunlukla Kafka üzerinden event paylaşımı yaptıkları belirlendi. İade sistemi dışında tüm OMS sistemlerinin ve iletişimde olunan diğer sistemlerin RabbitMQ kullanmaları ve geliştirme için yeterince vakit olmaması nedeni bu konuda bir geliştirme yapmamaya karar verildi. Veritabanı indexlerinin ve sunucu altyapısının güçlendirilmiş olması bu konudaki kararın alınmasında yardımcı oldu.

Veritabanı üzerinde yük oluşturduğu tespit edilen filtreleme API’lerinin kural tabanlı olarak ve sadece gerekli olduğu durumlarda JOIN ve DISTINCT sorgularını kullanması sağlandı. Bu geliştirmenin canlı ortama alınması ile birlikte veritabanı üzerindeki etkisi çeşitli monitörleme uygulamalarından gözlemlendi.

Sipariş servisi üzerinde herhangi bir versiyon kontrolü olmaması nedeni ile yaşanan tutarsızlıkların önlenmesi için JPA versiyonlama işlemi uygulandı. Neredeyse iki senelik sipariş verisi üzerinde kademeli olarak versiyon alanı ataması yapılarak versiyon kontrollü yapıya geçiş sağlandı. Bu işlem sonucunda alınan versiyon kontrol hatalarına baktığımızda ise ne denli çok sayıda siparişin bu sorun nedeni ile tutarsız bir hal alabildiği gözler önüne serilmiş oldu (24 saatte yaklaşık 500K).

Efsane Günler Dönemi

9–10–11 kampanyası döneminde alınan yoğun trafiği kaldırmasına rağmen veritabanı kullanımı nedeni ile sorunlar yaşayan OMS servislerinin bu seneki son sınavı olan efsane günler kampanyası, bir önceki Efsane Günlerin çok üzerinde trafik ve sipariş ile gerçekleşti.

Kampanya boyunca sipariş ve paketleme servisleri, güçlendirilmiş veritabanı ve iyileştirilmiş sorguları ile çok daha stabil bir görüntü sergiledi. Yük artışlarına daha iyi tepki veren servisler sipariş yapısındaki versiyon kontrolü ile son derece tutarlı bir hale kavuştu. Böylelikle uzun süren etkinlik boyunca OMS servisleri içindeki bir tutarsızlık kaynaklı destek hizmetine ihtiyaç duyulmadı.

Filtreleme iyileştirmesi öncesi durum
Filtreleme iyileştirmesi sonrası durum

Hem 9–10–11 kampanyasının paketlerinin ulaşmış olması, hem de Efsane Günlerin ilk günlerinde verilen siparişlerin müşterilere ulaşması ile iade servisinin kullanımı ciddi derecede artış gösterdi. Bu noktada Couchbase veritabanından aldığımız tepki sürelerinde dalgalanmalar yaşanmaya başlandı. Bu noktada sistem yöneticileri ve veritabanı uzmanlarının desteği ile Couchbase sunucularının atanmış kaynak modeline geçirilmesi sağlandı. Bu işlem sonrasında ise servis çok daha stabil bir yapı aldı.

Önümüzdeki Dönem Hedeflerimiz

Elektronik ticaret şirketlerinin özellikle teknoloji departmanlarının neredeyse tüm yıl süren çalışmalarının hem test edildiği hem de meyvelerinin toplandığı bu iki kampanya dönemi boyunca, yeniden yapılandırma sürecine girdiğimiz OMS servislerinin şimdiki (sipariş ve paketleme) ve yenilenmiş (iade) hallerini izleme fırsatı bulduk.

İade servisinin mimari yapısındaki doğru kararlar kadar, özellikle monitör işlemlerindeki eksiklerimizi ve ağır yük altındaki veritabanı tercihimizi sorgulama şansını elde ettik. Bu sorgulama ile önümüzdeki dönem geliştireceğimiz servislerin altyapıları konusunda yeni bakış açıları kazanmış olduk.

Önümüzdeki dönemde sipariş ve paketleme servislerinin yenilenmesinde yine DDD kullanımı başta olmak üzere, event tabanlı mimari ve Kafka sisteminin kullanımı konularında çalışmaya devam edeceğiz. Doküman yapısındaki veri modelinin sunum ve yazma kolaylıklarından faydalanarak yazma ve okuma performanslarının ayrı ayrı değerlendirebileceğimiz CQRS (Command Query Segregation) yapısının kullanımını değerlendireceğiz.

Bu süreçte bizlerle birlikte yol almak ve yüksek hedefleri olan bir OMS yapısı ortaya koymak isteyen siz değerli meslektaşlarımızı aramızda görmekten büyük mutluluk duyarız. Bunun için işinin ustası Trendyol insan kaynakları departmanı ile ya da doğrudan bizlerle iletişime geçebilirsiniz.

--

--

Onur Destanoğlu
Trendyol Tech

Working as Head of Engineering at Trendyol Order Management Team