Resiliency etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Resiliency etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

10 Kasım 2022 Perşembe

Fault Tolerance ve Resiliency İçin Dead-Letter Queue Örüntüsü

Giriş
Bir mesaj bir kaç defa denendikten sonra (Retry Örüntüsü) halen işlenemiyorsa muhtemelen insan müdahalesi gerekir. Dead Letter Channel aynı zamanda Dead Letter Queue olarak ta bilinir. Şeklen şöyle
Açıklaması şöyle
1. Under normal circumstances, the application processes each event in the source topic and publishes the result to the target topic
2. Events that cannot be processed, for example, those that don’t have the expected format or are missing required attributes, are routed to the error topic
3. Events for which dependent data is not available are routed to a retry topic where a retry instance of your application periodically attempts to process the events
Eğer birbirine bağımlı mesajlar varsa bunların da sırayı muhafaza etmek için aynı kuyruğa gönderilmesine dikkat etmek gerekir

Dead-Letter Kelime Anlamı Nedir?
Açıklaması şöyle.
What Is a Dead Letter Queue?
In English vocabulary, dead letter mail is undeliverable mail that cannot be delivered to the addressee. A dead-letter queue (DLQ), sometimes known as an undelivered-message queue, is a holding queue for messages that cannot be delivered to their destinations due to something.

According to Wikipedia — In message queueing the dead letter queue is a service implementation to store messages that meet one or more of the following failure criteria:

- Message that is sent to a queue that does not exist
- Queue length limit exceeded
- Message length limit exceeded
- Message is rejected by another queue exchange
- Message reaches a threshold read counter number because it is not consumed. Sometimes this is called a “back out queue”
İnsan Müdahalesi İçin Bazı Örnekler
Örnek
Bir örnek şöyle
A message arriving in the error queue can trigger an alert and the support team can decide what to do. And this is important: You don't need to automate all edge cases in your business process. What's the point in spending a sprint to automate this case, if it only happens once every two years? The costs will definitely outweigh the benefits. Instead, we can define a manual business process for handling these edge cases.

In our example, if Bob from IT sees a message in the error queue, he can inspect it and see that it failed with a CannotShipOrderException. In this case, he can notify the Shipping department and they can use another shipment provider. But all of this happens outside of the system, so the system is less complex and easier to build.
Örnek
Hatalı alan için bir örnek şöyle
However, if the error is not ever possible to solve via a retry process (such as a never ever unhandled case, maybe a corrupt field value, e.g.), you should create an “error topic” which is called a “dead-letter queue”.

25 Ocak 2021 Pazartesi

Fault Tolerance ve Resiliency İçin Timeout Örüntüsü

Giriş
Timeout kelimesi "Zaman Aşımı" anlamına gelir. Sistem içinde hata olsa bile, hatalı kısmı kullanmaya çalışan alt sistem sonsuza kadar takılıp kalmaz. Belli bir müddet cevap bekledikten sonra hata kodu döner

Timeout yöntemi ile mitigating action yani B planı devreye sokulur.

Timeout Değeri Nasıl Hesaplanır?
Şöyle bir yöntem izlenebilir. 1000 tane isteğin cevap verme süresi bulunur. Tüm cevapların %95'ini kapsayacak bir değer hesaplanır. Bu değer timeout süresidir.

Örnek - Resilience4j
Şu satırı dahil ederiz.
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot2</artifactId>
  <version>1.1.0</version>
</dependency>
Elimizde şöyle bir dosya olsun.
resilience4j.timelimiter:
  instances:
    ratingService:
      timeoutDuration: 3s
      cancelRunningFuture: true
    someOtherService:
      timeoutDuration: 1s
      cancelRunningFuture: false
---
rating:
  service:
    endpoint: http://localhost:7070/ratings/
Şöyle yaparız
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;

@Service
public class RatingServiceClient {

  private final RestTemplate restTemplate = new RestTemplate();

  private String ratingService = ...;

  @TimeLimiter(name = "ratingService", fallbackMethod = "getDefault")
  public CompletionStage<ProductRatingDto> getProductRatingDto(int productId){
    Supplier<ProductRatingDto> supplier = () ->
      this.restTemplate.getForEntity(this.ratingService + productId,
ProductRatingDto.class)
        .getBody();
      return CompletableFuture.supplyAsync(supplier);
    }

  private CompletionStage<ProductRatingDto> getDefault(int productId, Throwable throwable){
    return CompletableFuture.supplyAsync(() ->
ProductRatingDto.of(0, Collections.emptyList()));
  }
}

29 Aralık 2020 Salı

Fault Tolerance ve Resiliency İçin Circuit Breaker Örüntüsü

Giriş
Bu yazı aslında Fault Tolerance ve Resiliency İçin Bazı Yazılım Çözümleri serisinin bir parçası. 

Bu yöntem microservice mimarisinde kullanılıyor. Eğer bir sistem cevap vermiyorsa ona sürekli erişmeye çalışmak yerine sigorta (circuit breaker) devreye girer ve direkt hata mesajı/kodu döndürür. Açıklaması şöyle.
In a nutshell — Do not hammer a service with additional requests which is already down. Please give the service time to recover.
Açıklaması şöyle.
Circuit Breaker monitors API calls. When everything is working as expected, it is in the state closed. When the number of fails, like timeout, reaches a specified threshold, Circuit Breaker will stop processing further requests. We call it the open state. As a result, API clients will receive instant information that something went wrong without waiting for the timeout to come.

The Circuit is opened for a specified period of time. After timeout occurs, the circuit breaker goes into the half-opened state. Next, the API call will hit the external system/API. After that, the circuit will decide whether to close or open itself.
Sigorta 3 durumda olabilir. Şeklen şöyle
CLOSED Durumu - Her şey Yolunda
Açıklaması şöyle. Belli bir sayıda hata aldıktan sonra OPEN durumuna geçer.
The circuit breaker being in a CLOSED state means that everything is working fine and all calls pass through to the remote services. Once the number of failures exceeds a predetermined threshold, the circuit breaker trips and enters into the open state.
OPEN Durumu - Sigorta Atmıştır
Açıklaması şöyle. Direkt hata kodu döndürür. Belli bir süre sonra HALF-OPEN durumuna geçiş yapılır
Once the number of timeouts reaches a predetermined threshold in the circuit breaker, it trips the circuit breaker to the OPEN state. In the OPEN state, the circuit breaker returns an error for all calls to the service without making the calls to the remote service.
HALF-OPEN Durumu
Açıklaması şöyle. Halen hata kodu döndürür, ancak servisleri kontrol etmek için deneme çağrıları yapar.
After a certain duration, the circuit switches to a HALF-OPEN state to test if the underlying problem still exists. The circuit breaker uses a mechanism to make a trial call to the remote service periodically to check if it has recovered. If the call to the Remote service fails, the circuit breaker remains in the OPEN state. If the call returns success, then the circuit switches to the CLOSED state. 
State Geçişi
Açıklaması şöyle
There are 2 types of circuit breaker patterns, Count-based and Time-based.

1. Count-based: the circuit breaker switches from a closed state to an open state when the last N requests have failed or timeout.
2. Time-based: the circuit breaker switches from a closed state to an open state when the last N time unit has failed or timeout.

In both types of circuit breakers, we can determine what the threshold for failure or timeout is. Suppose we specify that the circuit breaker will trip and go to the Open state when 50% of the last 20 requests took more than 2s, or for a time-based, we can specify that 50% of the last 60 seconds of requests took more than 5s.

After we know how the circuit breaker works, then we will try to implement it in the spring boot project.
Circuit Breaker Kütüphaneleri
- Netflix’s Hystrix


28 Aralık 2020 Pazartesi

Fault Tolerance ve Resiliency İçin Retry Örüntüsü

Giriş
Bu yazı aslında Fault Tolerance ve Resiliency İçin Bazı Yazılım Çözümleri serisinin bir parçası. 

Retry-After Parametresi
REST çağrılarında sunucu HTTP 429 (Too Many Requests) veya 503 (Service Unavailable) gelse bile
Http cevabına Retry-After parametresini atayabilirHttp Cevap Parametreleri yazısına bakabilirsiniz. Açıklaması şöyle
HTTP 429 (Too Many Requests) or 503 (Service Unavailable) should trigger backoff, not immediate retry. The Retry-After header tells you explicitly when to try again — actually respect it. A 500 might be transient or might be a bug that'll fail identically every time; you need application context to tell the difference.
Retry İşlemini Kim Yapmalı
1. Her servis kendisi Retry işlemini gerçekleştirebilir
2. Ortak bir Retry servisi olabilir. Açıklaması şöyle
... it removes the retry complexity from all microservices, and places it in a single retry microservice. The retry microservice’s job is to track and action all retries. This microservice receives an event, writing it to its own topics with both the event to retry and the timestamp to retry that event. It then pushes out these retry events once their timestamp has been reached.

Retry Verisi Nerede Saklanmalı
1. Kafka, AMQP gibi bir kuyrukta saklanabilir
2. Veri tabanında saklanabilir

Retry Detayları
Sistem içinde hata olsa bile işlem belli bir süre sonra tekrar denenir. Retry yönteminde dikkat edilecek noktalar şöyle
1. Retry sayısı
2. Retry aralığı (back off süresi)
3. Tüm denemeler başarısız olursa ne yapılacağı
4. Retry işleminin stateless veya stateful olacağı
5. Retry 'ın çağırdığı şilemin Idempotent Receiver olması. Idempotency Nedir yazısına bakabilirsiniz.

Bazen Retry alt yapısını bizim kodlamamız gerekir, eğer şanslıysak kullandığımız alt yapı bu yeteneği bütünüyle veya kısmen de sağlıyor olabilir.

Not : Bu konuda okunması gereken bir yaz burada

Retry Storms
Elimizde bir servis çağrısı dizisi olsun
A -> B -> C -> D
ve D çalışmıyor olsun. Bu durum Retry Storm sebebidir. Açıklaması şöyle
Now imagine that each service has a retry policy installed which performs up to 3 retries on failed calls (a total of 3+1 requests). Think about what happens if D goes down, and this failure is propagated all the way back up the call chain:
C calls D 4x more
B calls C 4x more
A calls B 4x more

The traffic D receives might be as much as 64 times (!!) that of normal levels. And this is happening when D is already unhealthy — likely impairing it further.

Sounds bad, but the real problem is that these traffic increases grow exponentially. If K is the number of attempts made per call at each node, then the magnitude of the increase in call volume at depth N in the call graph is K^N. This behavior is known as a retry storm, and it can compound minor outages into major cascading failures.

Retry İle İlgili Parametreler
Açıklaması şöyle. Eğer bu parametreleri atlarsak failure durumu outage durumuna dönebilir.
Retries can amplify failures into outages. Use backoff, circuit breakers, idempotency, load shedding, and observability to keep systems stable under pressure.
1. Retry Sayısı
Mantıklı bir üst sınıf vermekte fayda var. Yoksa işlem/mesaj sonsuz döngü şeklinde sürekli tekrar tekrar kaynak tüketir.

2. Retry Aralığı
Açıklaması şöyle. Sabit, rastgele veya artan aralıklar kullanılabilir.
You retry few times hoping to get a response, with a fixed, random or exponential wait period in between each attempt and eventually give up to try in the next poll cycle.
Burada Exponential backoff with jitter öneriliyor. Açıklaması şöyle
First retry after 100ms, second after 200ms, third after 400ms. The jitter — randomizing each delay by ±25% or so — prevents the thundering herd where all clients retry in lockstep. Without jitter, you've built a periodic self-DDoS that hits your own service like a metronome.
Örnek
Java kullanıyorsak elimizde şöyle bir kod olsun .
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
Şöyle yaparız.
service.execute(task);
...
service.schedule(task, 20, TimeUnit.SECONDS);//retry
Örnek
Java kullanıyorsak elimizde şöyle bir kod olsun . java.util.Timer kullanıyoruz
Timer timer = new Timer();
Şöyle yaparız.
timer.schedule(task, 20);
3. Tüm denemeler başarısız olursa ne yapılacağı
Aslında iki tane seçenek var
1. Dead Letter Queue veya benzeri bir yere bir mesaj gönderilebilir.
2. İşlem/mesaj tamamen çöpe atılır

4. Retry işleminin stateless veya stateful olacağı
- Stateless retry işlemlerinden retry mekanizması aynı thread içinde N defa işlemi dener. Thread bu N deneme süresince başka bir işle uğraşmaz.

- Stateful retry işlemlerinden retry mekanizması aynı thread içinde işlemi dener. Eğer başarısız ise, thread başka bir işe geri döner. Yani bloke olmaz. Retry zamanı gelince tekrar dener.

Örnek - Resilience4j
Şöyle yaparız
public List<CompanyDto> searchCompanyByName(String name) {
  RetryConfig retryConfig =
    RetryConfig.custom().maxAttempts(4).waitDuration(Duration.of(2, SECONDS)).build();

  RetryRegistry retryRegistry = RetryRegistry.of(retryConfig);

  Retry retryConfiguration = retryRegistry.retry("companySearchService", retryConfig);

  Supplier<List> companiesSupplier = () -> companyRepository.findAllByName(name);

  Supplier<List> retryingCompaniesSearch =
    Retry.decorateSupplier(retryConfiguration, companiesSupplier);

  List<CompanyDto> companyDtos = new ArrayList<>();
  List companies = retryingCompaniesSearch.get();

  for(Company company : companies) {
    CompanyDto companyDto = new CompanyDto(company.getName(), company.getType(),
      company.getCity(), company.getState(), company.getDescription());
      companyDtos.add(companyDto);
  }

  return companyDtos;
}
Açıklaması şöyle
While using resilience4j-retry library, you can register a custom global RetryConfig with a RetryRegistry builder. Use this registry to build a Retry.

In the above method, we first create RetryConfig. We create a RetryRegistry and add RetryConfig in this registry. Then when we create our call to fetch a list of companies. We decorate this call with retryConfiguration.
RetryConfig açıklaması şöyle
Customizations with Resilience4j-Retry
RetryConfig offers different customization:
1. maxAttempts — 3 is the default number of attempts for retries.
2. waitDuration — a fixed wait duration between each retry attempt.
3. intervalFunction — a function to modify the waiting interval after a failure.
4. retryOnResultPredicate — configures a predicate that evaluates if a result should be retried.
5. retryExceptions — Configures a list of throwable classes that are used for retrying
6. ignoreExceptions — Configures a list of throwable classes that are ignored
7. failAfterMaxRetries — A boolean to enable or disable throwing of MaxRetriesExceededException when the Retry has reached the configured maxAttempts

Retry Design Pattern ve Circuit Breaker Pattern
Aslında birbirlerini tamamlıyorlar. Circuit Breaker eğer bir sistemin çalışmadığını anlarsa Retry işlemine girmeden direkt bir cevap döner. Açıklaması şöyle
I would like to mention, a subtle difference with "Circuit Breaker" pattern, which is actually one level up. It has an implicit Retry but also prevents further communication until the remote service is available and responds with a test call. This strategy is recommended when you expect services to be unavailable for a longer duration whereas "Retry" is recommended for transient failures (short duration or temporary failures).
Idempotency 
Açıklaması şöyle. Eğer bu parametreleri atlarsak failure durumu outage durumuna dönebilir.
You need idempotency keys, client-generated request IDs that let the server recognize duplicates. Without this, your retry logic becomes a data corruption engine. I've seen double charges, duplicate emails, inventory counts that drifted further from reality with each retry.
The Retry Budget
Açıklaması şöyle
SRE teams talk about error budgets — how much failure you can tolerate before breaking SLOs. Same logic applies to retries. You need a retry budget: a system-wide cap on in-flight retries.
When Retries Aren't Enough
Açıklaması şöyle
Uncomfortable question: what happens when the system is genuinely overloaded and backing off won't help?

You have to shed load.

The server needs to signal "I cannot handle this" instead of accepting requests into a bottomless queue. Return errors. Return them fast. Let clients fail quickly and show degraded UI or route to a fallback. This requires killing the instinct to "handle everything" — sometimes dropping requests is the right move to keep the system functional for anyone at all.
Doğası Gereği Retry Olması Gereken İşlemler
Bazı sistemlerin doğasında Retry vardır. Karşı sistemden bir cevap dönmüyorsa, kullanılan alt yapı, iletişim ağı güvenilir değilse ister istemez Retry benzeri bir çözüm sistemde kullanılmaya başlanıyor.

Örnek
Bir sistemde UDP kullanıldığı için mesajlar kaybolabiliyordu. "Transmission Queue" bileşeninde her mesaj aynı birincil anahtar ile N defa tekrar edilecek şekilde bir çözüm geliştirildi. Böylece Retry örüntüsü aslında bir nevi gerçekleştirilmiş oldu.

16 Temmuz 2020 Perşembe

Resiliency - Dirençlilik

Resiliency (Dirençlilik) ve Robustness (Sağlamlık) Farkı
Resiliency bazen robustness ile eş anlamlı gibi kullanılır. Aslında resilience biraz daha geniş düşünülebilir.

Robustness bence şöyle düşünülmeli. Örneğin hatalı girdi varsa, sistemin bununla başa çıkabilmesidir.

Resiliency ise bence şöyle düşünülmeli. Sistem içinde kendiliğinden bir hata/kopukluk olursa (network kopması, veri tabanına erişilememesi gibi) sistemin kendi kendine bu hata ile anlamlı bir şekilde başa çıkabilmesidir. Başa çıkma yöntemi bir hata kodu döndürmek basit bir şey bile olsa, durum ortadan kaybolunca sistem tekrar eski haline dönebiliyorsa, yani hastalıktan kurtulabiliyorsa dirençlidir.

Ekip Açısından Resiliency 
Sistemi işleten insanlar olduğu için insanların dirençliliği aynı zamanda sistemin de direncini etkiler. Açıklaması şöyle.
Resiliency doesn’t just exist in your processes — it also exists in your people. Capacity planning is an important part of having a resilient system because in thinking about the allocation of resources, your team members matter. They need time off for holidays, personal vacations, and the obligatory annual cold. When you fail to plan for time off, you won’t have enough hands on deck to handle incidents as they occur. Denying people time off is obviously not the answer, as that will only lead to burnout and churn. So it’s important to develop a capacity plan that can accommodate people being, well, people.

Kavramlar
Önemli kavramlar şöyle

1. Rebound 
Sistem bir travmadan sonra ne kadar hızlı kendini toparlıyor

2. Graceful extensibility
Sistem ne kadar kolay genişleyebilir

3. Robustness - Dayanıklılık
Robustness yazısına bakınız

4. Adaptive capacity
Sistem değişen koşullara uymak için ne kadar kolay değiştirilebilir.

Dirençlilik (Resiliency) İçin Kullanılabilecek Bazı Yöntemler
Fault Tolerance ve Resiliency İçin Bazı Yazılım Çözümleri yazısına taşıdım.

14 Mayıs 2020 Perşembe

Fault Tolerance ve Resiliency İçin Bazı Yazılım Çözümleri (Design Pattern)

Giriş
Bu yazıdaki kavramlar
 1. Fault Tolerance - Arızaya Dayanıklılık
 2. Resiliency - Dirençlilik
için kullanılabilir.

1. Cache Pattern
Açıklaması şöyle. Yavaş bir sistemi daha da yavaşlatmamak için bazı cevaplar önbelleğe alınabilir.
Slow backend systems are often misunderstood beasts, a bit like Ogres. The reason for their slowness or unavailability is often down to their business criticalness. These systems are bombarded with requests more than they are provisioned to handle, resulting in these systems being slow or unavailable. The solutions I’ve proposed here are based on two factors. 

- One, reduce the number of requests hitting the backend. 
- Two, avoid loading the backend service when it’s already overloaded (avoid peak times).

Although you might not be able to get rid of the beast, you might learn to live with it.
Bir backend sisteme önbellek ilave ederken izlenebilecek akışlar farkı farklı olabilir.
1. 1 Soru Soran Sadece Cache Sistemi Bilir
Açıklaması şöyle.
The first option would be the simplest and has no dependency on the backend service. When the cache node is requested for information it does not have, it will fetch it from the slow backend, update the cache and return it to the requester.
1. 2 Soru Soran Hem Cache Hem de Backend Sistemi Bilir
Bu akışta soru soran istediği cevabı cache sistemde bulamazsa, backend sisteme sorar.

1.3 Her iki akışta da şuna dikkat etmek lazım
Açıklaması şöyle. Yani backend ise cache arasında bir bağlantı olmalı
... the cache gets updated whenever the backend data gets updated. This requires an integration with the backend service, and the backend service should be equipped to either call an API on the cache node or should have logs that could be processed to extract the required information.

2. Retry Design Pattern
Retry Design Pattern yazısına taşıdım

3. Timeout Yöntemi
Timeout Pattern yazısına taşıdım

4. Load Balancing and Failover
Bu konuyu High Availability - Yüksek Süreklilik yazısına taşıdım

5. Circuit Breaker Pattern
Circuit Breaker Pattern yazısına taşıdım

6. Dead Letter Channel Yöntemi
Dead-Letter Channel Örüntüsü yazısına taşıdım.

7. Saga Yöntemi
Saga Örüntüsü yazısına taşıdım.

8. Bulkhead Yöntemi
Şeklen şöyle. Burada geminin bölmelere ayrıldığı görülebilir. Bölme duvarlarına bulkhead deniliyor. Böylece eğer gemi bir bölmeden yara alsa bilebölmeler sayesinde batmıyor.


Gemideki bu kullanıcımı yazılım dünyasına taşıyan bir örnek şöyle. A servisi hem kendi başına çalışabilen hem B'yi kullanan servisler sunuyor. Eğer B yavaşlarsa, tüm A'nın yavaşlamaması gerekir. Bu yüzden B'ye ayrılan kaynaklar sınırlandırılıyor.
let's assume that there are 2 services A and B. Some of the APIs of A depends on B. For some reason, B is very slow. So, When we get multiple concurrent requests to A which depends on B, A’s performance will also get affected. It could block A’s threads. Due to that A might not be able to serve other requests which do NOT depend on B. So, the idea here is to isolate resources / allocate some threads in A for B. So that We do not consume all the threads of A and prevent A from hanging for all the requests!
Bulkhead için Resilience4j @BulkHead Anotasyonu, Hystrix kullanılabilir.

Örnek
Resilience4j kullanan bir örnek burada

9. Rate Limiter Yöntemi