~/Ali GÖREN

Golang’de Context Nedir? Sever Miyiz?

Ali Goren · · 5 dk okuma

Golang’de Context Nedir? Sever Miyiz?


Selamlar. Bu yazıda go dilinde yer alan context paketi hakkında anladığım kadarıyla sizlere bilgi vermeye çalışacağım.

Nedir Bu Context?

Açıklamaya girişmeden önce, context’leri öyle ya da böyle ne yaptıklarını bilmesek de kullanırız. Çoğu zaman Fiber, Mux vb. frameworklerin de bu context’ler ile çalıştıklarına şahit olmuşuzdur.

Go dilinde context paketini, contextler arası ya da scoped verileri aktarırken kullanabiliriz. Tabii ki böyle söyleyince çok basit kaçıyor. Ama yaptıkları işler çok önemli. Örnek kullanım senaryolarını şöyle açıklayabiliriz

  • Request ID, Trace ID gibi verileri taşırken
  • Goroutine’ler için normalde bir terminating keywordü yok. Contextleri bu noktada cancelation signal işleminde kullanabiliriz.
  • Deadline’ları timeoutlar ayarlamak istediğimizde kullanabiliriz.

Yani özetlemek gerekirse, contextler deadline’lar, cancelation signals ve API’lar ya da processler arası data sharing işlemleriyle ilgilenirler. Bir tık eğlenceli hale gelmedi mi ya bu konu? 😍

Context için cancelation işlemlerinden bahsettik. Fakat, bir context Cancel isimli fonksiyona sahip değildir.

Bir context, aynı anda çalışan birden fazla go routine ile güvenli bir şekilde kullanılabilir. Yani tek bir context’i aynı anda çalışan birden fazla go routine ile kullanabilirsiniz.

Context Oluşturma

Go’da contextler context.Background() ya da context.TODO() ile oluşturulur. Eğer hangi türden context’e sahip olacağınızı bilmiyorsanız TODO kullanabilirsiniz.

Request-Scoped Data

Bu, context’lerin en basit çalıştığı yapıdır diyebiliriz. Kısacası processler arası daha sharing yaparak, ilgili context’i de kullanarak yeni bir context yaratırsınız.

Örneğin 2 farklı process yürüten fonksiyonumuz olsun. Bunların trace id’lerini tutabilmeliyiz. Bu noktada go bize bu özelliği sağlıyor. İlk kullanım örneğimiz aşağıdaki gibi olsun

Yukarıdaki örnekte ilk context’imizi oluşturduk, context’leri veri tutucu olarak kullandık. Bu arada yeri gelmişken söyleyeyim, eğer context içeren bir fonksiyon oluşturuyorsanız, ilk parametre olarak context’i alması daha iyi olur. Ayrıca context’leri nil olarak paslamayın. Yani fonksiyonlar nil context almamalı

Örneğimizi inceleyecek olursak, BenNeyim isimli fonksiyon, context.WithValue kullanarak yeni bir context dönüyor. Bu WithValue fonksiyonu ise ilk parametre olarak parent context’i alır. Yani o context’i bir de gel şunu ilave edelim, seni öyle dönelim diyerek döndürüyor.

Kodun özetinde, 2 farklı process’imiz olduğunu düşünürsek, context kullanarak verilerimizi paylaşabildik. 😂

Cancelation Neden Gereklidir? Niye Kullanalım?

Bana kalırsa, request-secoped data olayından çok daha tatlı bir kavram, cancelation kavramı.

Cancelation işlemini kısaca anlatmak gerekirse, sistemin gereksiz yere yaptığı işleri durdurmak olarak anlatabiliriz.

Bu senaryoyu şöyle detaylandıralım. Örneğin bir kullanıcı zaman alan bir veri tabanı işlemi yapıyor.

Ama oldu ki o kullanıcının elektrikleri gitti, tarayıcıyı kapatası geldi vs. bu durumda uygulamanın çalıştığı sunucu ve sunucunun verileri aldığı veri tabanı sunucusu hala çalışmaya devam eder. Çünkü bildiğiniz gibi process başladıysa bitmelidir.

Fakat burada bir sorun var. Kullanıcı ortalarda yok? Yani sizi terk etmiş bir insan için çok fazla dert ediniyorsunuz… İşte boş yere harcanan sistem kaynağı böyle bir şey. Bu cancelation senaryosunun çizilmediği bir durumdu arkadaşlar.

Diyelim ki kullanıcı artık sistemde yok. Olmadığını biliyoruz. Bu durumda duran tek şeyin user request olmamasını sağlamak isteriz. Yani bekleyen bütün işlemlerin cancelled olması gerekiyor. Böylelikle, gereksiz yere çalışmaya devam eden processleri de durdurmuş oluruz. Hadi gelin bir kod örneği ile bunu görelim.

Mini Bir Cancelation Senaryosu

Cancelation işleminin neden gerekli olduğunu anladığımıza göre bunu ufak bir kod ile destekleyelim.

Başlamadan önce cancelation işlemi 2 aşamalı bir işlemdir. Bunlar şöyle açıklanabilir;

  • Cancelation event listening
  • Cancelation event firing

Yani bir yandan cancelation işleminin olmasını beklerken, bir yandan da onu fırlatmanız gerekiyor.

Cancelation Emitting ve Listening

Cancelation event’i için listening ve emitting işlemini şöyle yapabiliriz.

Şimdi bu koda bakacak olursak AmirimOlayYerine ve DinlemeYapiliyor isimli context alan 2 farklı fonksiyonumuzun olduğunu görmekteyiz.

İlk fonksiyon, yukarıda bahsettiğimiz bir anda sizi terk eden kullanıcı örneğini destekler. 2 saniyelik bir işlem var ancak biz daha 500. milisaniyesinde terk ediliyoruz.

Her neyse, context’imizi Background() ile oluşturduktan sonra, cancellable yeni bir context daha set ettik. WithCancel() fonksiyonu da yine parent bir context alır. Geriye ise, hem yeni bir context hem de CancelFunc döner.

  1. dönen fonksiyonu ise, bir şeyler ters gittiğinde çalıştırabiliriz. Örnek olarak, amirim olay yerine gidemediği için bütün süreçleri durdurduk.

Bu işlem aslında event emit işlemidir de.

Burası çok önemli. Biz işlemi AmirimOlayYerine ile başlatıyoruz, DinlemeYapiliyor isimli fonksiyon bunu bilmiyor bile. Ancak, aynı context’i dinliyor oldukları için aslında biliyorlar.

Burada ctx.Done() bir channeldır. Yani context’ler Done adında channel dönen bir fonksiyon da sağlamakta bize. Merak edip içerisine bakacak olursanız, tamamen boş bir struct döndüğünü görebilirsiniz.

Yani anlayacağımız şey, cancel() fonksiyonu, Done fonksiyonuna bir event trigger ediyor. Bu nedenle de Done işlemi veri aldığı için cancelation tamamlanmış oluyor. Bu mutextlerle güvenceye alınmış bir işlemdir.

Bu işlem ise event listening işlemidir.

Timeouts

Aslında timeoutlar da cancellation ile aynı şekilde çalışır. Bu nedenle cancellation işlemini tam olarak anlatmak istemiyorum. Fakat kodu göstereceğim.

Yukarıdaki koda bakarsak, 2 saniyelik bir işlemimiz var ancak process için 1 saniyelik bir timeout veriyoruz. Bu nedenle de 1. saniyeyi geçen işlem cancel edilebilir oluyor. Tamamen diğer örnekle aynı listening ve emitting yapısına sahip bir fonksiyon WithTimeout. Sadece timeout ile çalışan bir yapıya sahip. Bu yapı aslında WithDeadline fonksiyonunu çalıştırır. Yani WithTimeout tek başına bir işlem yapmıyor. Ekstra bir fonksiyondan faydalanıyor.

Deadline fonksiyonundan da bahsetmek istiyorum. WithTimeout, belirli bir duration ile çalışma imkanı sunar. Deadline ise time value alır.

Yukarıdaki örneğe bakacak olursak (benim için geçmişte kalan bir süreç) direkt olarak time value alır.

Son olarak cancelation detaylarına dair error verisini almak istiyorsanız bunu ctx.Err() kullanarak yapabilirsiniz. Bu size error dönecektir.

Okuduğunuz için teşekkür ederim. Umarım faydalı bir yazı olabilmiştir.

Kaynaklar

Wingie / Enuygun’un büyüyen ekibinin bir parçası olmak isterseniz buradan açık pozisyonlarımıza göz atabilirsiniz. Tech ekibimize başvurmak için ise CV’nizi kariyer@enuygun.com’a iletebilirsiniz.