Otomasyon QAKıdemli Otomasyon QA Mühendisi

Dağıtılmış REST API'lerde idempotent tekrar mekanizmalarını doğrulamak için, devre kesici durumu geçişlerinin simüle edilmiş ağ bölünmesi senaryoları altında doğru gerçekleştiğinden emin olarak, üstel geri dönüş ve jitter ile otomatik test çerçevesini nasıl mimarlarsınız?

Hintsage yapay zeka asistanı ile mülakatları geçin
  • Sorunun cevabı.

Soru tarihi

Tekrar mantığı, mikro hizmet mimarileri monolitlerin yerini alırken temel bir dayanıklılık deseni olarak ortaya çıktı ve sistemleri geçici ağ hatalarına ve zamansal erişilemezliğe maruz bıraktı. Erken uygulamalar, kurtarma sırasında felaket yaratan "gürültülü sürüler" oluşturan naif anlık tekrarları kullandı ve zaten zor durumda olan hizmetleri bunaltıyordu. Sanayi, istemci tekrar fırtınalarını desenkronize etmek için üstel geri dönüş algoritmalarına (düzenlenmemiş, eşit ve tam jitter) doğru evrildi. Ancak, bu deterministik olmayan zamanlama davranışlarını test etmek, idempotens anahtarlarının tekrar zincirinde sürdürülebilirliğini doğrulamak ve devre kesici durum makinelerini (Kapalı, Açık, Yarı-Açık) doğrulamak, çoğu otomasyon paketinde kritik bir kör nokta olmaya devam ediyor, çünkü geleneksel senkron test doğrulamaları değişken gecikme pencereleri veya dağıtılmış durum doğrulamasını ele alamıyor.

Sorun

Temel zorluk, istemci niyeti ile sunucu algısı arasındaki gözlemlenebilirlik açığındadır. Bir istemci başarısız bir ödeme talebini tekrar ettiğinde, otomasyon çerçevesinin dört eşzamanlı endişeyi doğrulaması gerekir: (1) istemci, sunucuya saldırmadan önce denemeler arasında uygun değişken bir süre (jitter) bekler; (2) sunucu, tekrar eden idempotens anahtarlarını tanır ve işlemeden geri dönmeden orijinal yanıtı döndürür; (3) devre kesici, bir hata eşiğine ulaştıktan sonra Açık'a geçer ve kaynakların tükenmesini önlemek için hızlı bir şekilde başarısız olur; ve (4) Yarı-Açık durumda, tam olarak bir prob isteği arka uca ulaşır, geri kalanı ise hemen reddedilir. Standart taklit araçları başarısız olur çünkü gerçekçi TCP düzeyinde davranışları (paket kaybı, bağlantı sıfırlamaları, değişken gecikme) simüle edemez veya bu olayları uygulama katmanı metrikleriyle ilişkilendiremez.

Çözüm

Toxiproxy veya Envoy yan yanları kullanarak Programlanabilir Proxy Mimarisi uygulayın, bunlar test orkestratörü tarafından doğrudan kontrol edilir. Bu, test istemcisi ile test altındaki hizmet (SUT) arasında bir "kaos katmanı" oluşturur.

  1. Dayanıklılık Proxy Kontrolü: Toxiproxy'i yan yan olarak dağıtın. Test paketi, belirli zaman damgalarında latency, timeout veya reset_peer gibi "zehirli" (hata modları) ekleyip çıkarmak için Toxiproxy HTTP API'sini dinamik olarak kullanır.

  2. Telemetri Korrelasyonu: SUT'yi, yeniden deneme girişimleri için span/metrik yayınlamak üzere OpenTelemetry veya Micrometer ile enstrümante edin. Test çerçevesi, izlemeden olayları uygulama spanları ile izleyici kimlikleri kullanarak ilişkilendirir ve zehirli etkinliklerin yalnızca zehirli aktif pencerelerde gerçekleştiğini doğrular.

  3. İdempotens Doğrulama: İlk istekte bir UUIDv4 idempotens anahtarı oluşturun. Bunu bir iplik yerel bağlamında saklayın. İlk iki denemeyi başarısız olacak şekilde yapılandırılmış proxy üzerinden isteği verin. Nihai başarılı yanıtın X-Idempotency-Replay: true başlığını içerdiğini (veya bu anahtar için yalnızca bir defter kaydının mevcut olduğunu veritabanı sorgusu ile doğrulayın) doğrulayın.

  4. Durum Makinesi Doğrulaması: Proxy'nin devre kesici eşiğine ulaşana kadar (ör. 10 saniyede 5 hata) 503 hataları döndürmesini zorlayın. Devre kesicinin sağlık uç noktası (veya metrikleri inceleyerek) üzerinden Açık'a geçtiğini doğrulayın. Ardından zehirli kesip, yarı açık zaman aşımını bekleyin ve dağıtılmış izleme yoluyla tam olarak bir prob isteğinin arka uca ulaştığını ve paralel isteklerin hemen 503 Servis Kullanılamaz yanıtını aldığını doğrulayın.

Kod örneği

import requests import toxiproxy import time import statistics from assertpy import assert_that class ResilienceTest: def test_retry_jitter_and_circuit_breaker(self, proxy_client): # Setup: Configure proxy to inject 500ms latency then timeout proxy = proxy_client.get_proxy("payment_service") # Phase 1: Idempotency with retries idem_key = "idem-12345" proxy.add_toxic("slow", "latency", attributes={"latency": 500}) start = time.time() r = requests.post( "http://localhost:8474/proxy/payment_service", headers={"Idempotency-Key": idem_key}, json={"amount": 100}, timeout=10 ) duration = time.time() - start # With base 0.5s, exponential backoff 2^attempt + jitter # Attempt 1: 0.5s (fail), Attempt 2: 1.0s + jitter (fail), Attempt 3: 2.0s (success) assert_that(duration).is_between(3.0, 4.5) # Jitter allows variance # Phase 2: Circuit breaker threshold proxy.add_toxic("error", "timeout", attributes={"timeout": 0}) failure_times = [] for i in range(7): # Exceed threshold of 5 try: requests.get("http://localhost:8474/proxy/payment_service/health", timeout=1) except: failure_times.append(time.time()) # Verify fast-fail (no retry delay) after circuit opens if len(failure_times) >= 2: gap = failure_times[-1] - failure_times[-2] assert_that(gap).is_less_than(0.1) # No backoff delay = circuit open
  • Hayattan bir durum

Bağlam ve sorun açıklaması

Bir fintech şirketinde, ödeme ağ geçidimiz, bir REST ile eski bir banka API'si ile entegre oldu. Black Friday satışları sırasında banka, 503 hataları veren 30 saniyelik bir dalgalanma yaşadı. Hizmetimiz, naif anlık tekrarlarla yapılandırıldığında (3 deneme, 0 ms gecikme), 2,000 geçerli ödeme talebini, bankanın kurtarma uç noktasına çarpan 6,000 isteğe dönüştürdü. Bu "tekrar fırtınası", bankanın altyapısını çökertti, 45 dakikalık bir kesintiye ve 2 milyon dolarlık kaybedilen işleme yol açtı. Mevcut otomasyon paketimiz, tüm testleri geçen ancak gürültülü sürü davranışlarını tamamen yakalayamayan WireMock kullanıyordu çünkü değişken ağ gecikmesini simüle etmiyor ve tekrar deneme girişimleri arasındaki zamanlamayı ölçmüyordu.

Düşünülen farklı çözümler

Çözüm A: Sabit Hata Senaryoları ile Statik Taklit Sunucusu

WireMock kurulumumuzu ilk N isteği için 503 hataları döndürüp, ardından 200 döndürmek üzere genişletmeyi düşündük. Bu yaklaşım belirleyici doğrulamalar ve alt-saniye test yürütme sağladı. Ancak, TCP düzeyinde ağ bölünmelerini simüle etme veya istemcinin tekrar aralıklarının üstel geri dönüş eğrisini jitter ile takip ettiğini doğrulama yeteneğinden yoksundu. Artıları basitlik ve hız; eksileri düşük çevresel doğruluk ve devre kesici eşiklerini test edememe, bunun belirli sayımlar yerine süre boyunca sürekli hata oranları gerektirmesiydi.

Çözüm B: Konteyner Seviyesinde Kaos Mühendisliği

Docker daemon düzeyinde ağ gecikmesi tanıtmak için Pumba'yı değerlendirdik (ör. pumba netem --duration 1m delay --time 5000). Bu, gerçekçi ağ bozulması sağladı, ancak cerrahi kesinlikten yoksundu. Belirli API uç noktalarına yönelik hedefleme yapamazdık veya hata enjekte etmeyi belirli test eylemleriyle senkronize edemezdik, bu da tekrar zamanlamaları hakkında doğrulama yapmayı neredeyse imkansız hale getiriyordu. Artıları yüksek gerçekçilik; eksileri kötü test izolasyonu (tüm konteynerleri etkilemesi), belirsiz yürütme ve idempotensliği doğrulama yeteneğindeki yetersizlikti çünkü trafik kesintisini yakalayamazdık.

Çözüm C: Dağıtılmış İzleme ile Programlanabilir Proxy (Seçilen)

Toxiproxy'yi test ortamımızda yan yan olarak uyguladık, REST API aracılığıyla pytest yapı taşlarımızdan kontrol edildi. Bu, isteklerin yapıldığı test anında belirli zehirli davranışların (ör. timeout, reset_peer) uygulanması için hizmetimiz ile bir sahte banka konteyneri arasında enjekte edilmesine izin verdi. Bunu, her tekrar girişiminin kesin zaman damgalarını yakalamak için Jaeger izlemeyle birleştirdik. Artıları, hata zamanlaması üzerinde ayrıntılı kontrol, dağıtılmış izleme üzerindeki doğrulama yeteneği (geri dönüş aralıklarını doğrulama) ve tekrarlanabilir senaryolar; eksileri ek altyapı karmaşıklığı ve yöneticilerin proxy yapılandırmalarını anlaması için öğrenme eğrisiydi.

Hangi çözüm seçildi ve neden

Çözüm C'yi seçtik çünkü bunun gerekli gözlemlenebilirliği ve kontrolü sağladığına inanıyorduk. Programlanabilir proxy, üretimden tam olarak "503 dalgalanması ve ardından gürültülü sürü" senaryosunu yeniden üretmemizi sağladı. Proxy zehirli etkinliklerini uygulama günlükleri ile ilişkilendirerek, "Tam Jitter" (0 ile üstel değer arasında rastgele gecikme) uygulanmasının zirve tekrar yükümüzü 6,000 req/s'den 340 req/s'ye (yüzde 94'lük bir azalma) indirdiğini gösterdik. Belirleyici kontrol, bu testlerin CI'da belirsizlik olmadan çalıştırılmasına olanak tanıdı, yangın çıkmaması için güven verirken.

Sonuç

Otomatik paket, Yarı-Açık durum doğrulamasında kritik bir hata tespit etti: devre kesici, başarılı prob kurtarıldığında hata sayıcıyı sıfırlamıyordu, bu da bir sonraki küçük aksaklıkta Açık'a geri dönmesine neden oluyordu. Durum makinesi mantığını düzeltikten sonra, sistem bir sonraki banka API olayı sırasında kademeli olarak bozuldu, tamamen başarısız olmak yerine önbelleğe alınmış ödeme onaylarını sundu. Test paketi artık her bir çekme isteği olarak 4 dakikada çalışıyor ve tekrar ve devre kesici yapılandırmalarının regresyonunu engelliyor.

  • Adayların genellikle kaçırdığı şey

Jitter, üstel geri dönüşte gürültülü sürüleri nasıl önler ve bunu sabit uyku doğrulamaları kullanmadan otomatik bir testte nasıl istatistiksel olarak doğrularsınız?

Jitter, tekrar aralıklarına rastgelelik ekleyerek (ör. delay = random_between(0, min(cap, base * 2^attempt))), istemci tekrarlarının kurtarma sunucularını bunaltmasını önler (gürültülü sürüler). Bunu otomasyonda doğrulamak için, 3 tekrar denemesi olan bir başarısız uç noktaya karşı 100 eş zamanlı istek yürütün. Her tekrar girişiminin zaman damgalarını dağıtılmış izleme veya proxy günlükleri aracılığıyla yakalayın. Kesin değerler üzerinde doğrulama yapmak yerine, sunucudaki varlık zamanlaması standart sapmasını hesaplayın. Standart sapmanın bir eşik değerini (ör. 1 saniyelik temel gecikme için >800ms) aştığını doğrulayın, bu da desenkronizasyonu kanıtlıyor. Alternatif olarak, iki tekrarın da birbirini takip eden 100ms penceresi içinde gerçekleşmediğini doğrulayın, bu da etkin rastgeleleşmeyi onaylıyor. Sabit uyku doğrulamaları başarısız olur çünkü jitter'in olasılıksal doğasını göz ardı eder ve yavaş, belirsiz testler oluşturur.

Tekrarlar arasında idempotens anahtar rotasyonu yapmak neden tehlikelidir ve test çerçeveleri sunucu tarafı deduplikasyonunu düzgün bir şekilde doğrulamak için idempotens anahtar depolamasını nasıl ele almalıdır?

Tekrarlar arasında idempotens anahtarını döndürmek (yeniden oluşturmak), güvenlik garantisini bozar ve sunucu her isteği ayrı bir işlem olarak algıladığı için çift masraflara veya çift envanter tahsisine neden olabilir. Anahtar, tek bir mantıksal işlem için tüm tekrar zinciri boyunca aynı kalmalıdır. Test otomasyonunda anahtarı, tekrar döngüsüne girmeden önce UUIDv4 kullanarak oluşturun ve bir iplik yerel veya test kapsamlı bağlamda depolayın. Yarış koşullarını test etmek için, aynı anahtar ile uç noktaya erişmek üzere 10 iplik başlatın. Tam olarak bir ipliğin HTTP 200 aldığını ve diğerlerinin 409 Çatışma veya benzer bir başarılı yanıt gövdesi aldığını doğrulayarak sunucu tarafı deduplikasyonunu onaylayın. Tekrar döngüsündeki catch bloğunda yeni bir anahtar oluşturmayın.

Devre kesicilerdeki "Yarı-Açık" durumu ile ilgili özel risk nedir ve bu durumu otomatik paketlerde test etmek neden zordur?

Yarı-Açık durumu, devre kesici zaman aşımı süresi sona erdikten sonra (örneğin, Açık durumda 60 saniye) ortaya çıkar ve aşağı akış hizmetinin kurtarıldığını test etmek için sınırlı sayıda prob isteğine (genellikle 1) izin verir. Risk, bu pencerede birden fazla isteğin geçiş sağlaması veya probun arka plan sağlık kontrolleri tarafından kirlenmesi durumunda, devre kesicinin hizmet hala başarısızken Kapalı'ya yanlış geçiş yapabilmesi veya kurtarma durumunda Açık kalabilmesidir. Bunu test etmek zordur çünkü zamansal hassasiyeti ve trafik izolasyonunu gerektirir. Paylaşılan ortamlarda, arka plan süreçleri veya diğer testler prob sayısını etkileyen istekler gönderebilir. Çözüm, programlanabilir bir proxy kullanarak yarı açık penceredeki tek prob isteği dışında tüm trafiği engellemektir veya test sırasında zamanlı beklemelere ihtiyaç duymaksızın iç durum makinesini doğrulamak için SUT'de bir devre kesici kontrol uç noktası (örneğin, /actuator/circuitbreakers) açmaktır.