29 Ocak 2021 Cuma

Data Distribution Service (DDS) Nerede Kullanılır ve Kullanılmaz

Giriş
Bu yazıda bazı genel prensipler var. Bu prensipler bana "doğal" gelen kullanımlara göre yazıldı. Bazı durumlarda farklı teknolojileri kullanarak aynı sonuca erişmek mümkün olsa da, "doğal" hissiyatını vermiyor. 

DDS Hangi İşlere Uygun Değil
Bence DDS şu durumlara uygun değil

1. Merkezi bir broker gerektiren durum
2. Event Replay gerektiren durum
3. Consumer Group gerektiren durum
4. Çok yoğun mesaj geldiği durum
5. Remote Procedure Call gerektiren durum

1. Merkezi Broker Gerektiren Durum - Mesaj Kaybı Olmamalı
Genellikle bir event'in mutlaka bunu işleyen tüketiciye ulaşması durumudur. Tüketici taraf event'in oluştuğu anda, çalışıyor veya çalışmıyor olabilir. 

Örneğin bir para transferi yapmak isteyelim. Burada para örneğini kaybolursa çok yaygara çıkacağı için seçtim :) Merkezi bir broker varsa - mesela RabbitMQ - bu broker üzerindeki durable (kalıcı) kuyruklar sayesinde, hem kuyruğun kendisini, hem de içindeki event'leri saklayabiliyor.

Tüketici tarafın mesajı işlediğine dair acknowledgement (onay) gelinceye kadar, mesaj broker'da saklanabilir, hata oluşması durumunda tekrar gönderilebilir. 

DDS mesaj kaybı olmayacağını "Durability Service" kullanılmıyorsa tam olarak garanti edemiyor.

"Durability Service" kullanılmayan bir durumda, eğer DataWriter ve DataReader birbirlerini bulmadıysa (discover) veya eşleşmediyse mesajlar rahatlıkla kaybolabilir. Ve hatta discovery için bir miktar da beklemek gerekebilir. DataWriter yazına bakabilirsiniz.

2. Transaction Gerektiren Durum
Açıklaması şöyle
JMS offers some capabilities not offered by DDS. Distinctive JMS capabilities include point-to-point delivery to exactly one of many consumers, message priority, and enterprise specific features such as full transactional support, and application level acknowledgements. 
3. Event Replay Gerektiren Durum
Event replay gerektiren durumlar, genellikle bir merkezi broker'ın event'leri belli bir süre boyunca saklaması şeklinde çözülüyor. Örneğin Apache Kafka bu şekilde çalışıyor. Eğer saklama süresi sonsuz olarak seçilirse, aslında sürekli ekleme yapılan bir veri tabanına dönüşebilir. Bu teknoloji Time Series Database olarak anılıyor

4. Consumer Group Gerektiren Durum
Consumer group, elimizde çok fazla sayıda event varsa ve bu event'leri tek bir uygulama ile tüketemiyorsak gerekiyor. Amacımız bu event'leri tüm tüketicilere dağıtmak. Yine Apache Kafka ve benzeri teknolojiler Consumer Group oluşturmaya, bu grubun otomatik olarak yönetilmesini imkan tanıyor. 

5. Çok yoğun mesaj Gerektiren Durum
Dakikada gelen mesaj sayısı 20K-30K ve üstü ise bana göre yoğun mesaj geliyordur. Bu sayılar benim gözlemlerim. Yoğun mesajları işleyebilmek için, sharding yapmak gerekiyor. Yani yükün birden fazla broker'a dağıtılması. 

5. Remote Procedure Call Gerektiren Durum
 Remote Message Call (RPC) karşı sisteme bir çağrı yapmak ve cevap beklemek anlamına gelir. Dağıtık ve senkron çalışan sitemlerde RPC yapmak gerekiyor. Asenkron çalışan mesaj kuyrukları bu işlevi yerine getirebiliyor. Örneğin JMS broker'ları bu yöntemi destekliyor.

DDS Hangi İşlere Uygun
Bence DDS şu durumlara uygun
1. Mesaj broadcast edildiği durum
2. QoS parametresi gerektiği durum

1. Mesaj Broadcast Edildiği Durum
Bu kullanım şekline özellikle "broadcast" dedim ama "fire and forget" olarak ta isimlendirilebilir. 

Elimizde bir alıcı yani sensör olsun. Bu alıcı ürettiği bilgiyi belli aralıklarda yayınlıyor olsun. Kapalı bir sistemde - gemi, uçak vs - bir çok başka birim bu alıcının gönderdiği bilgiye ihtiyaç duyar. 

Bu gibi sistemlerde merkezi bir broker kullanılmak istenmeyebilir. Çünkü broker sistemin tam kalbine yerleşecek ve "Single Point of Failure" oluşturacaktır. 

Bu kullanım şeklinde iletişim yine merkezi bir broker olmaması yüzünden, daha da hızlı olacaktır. Zaten bu yüzden DDS için "Near Realtime" deniliyor. Tabii Realtime çok genel bir gelime :) 
Ancak buradaki vurgu iletişimde, broker'ın getirdiği maliyetlerin azalması.

2. QoS Parametresi Gerekiyorsa
DDS'in tanımladığı bir sürü QoS var. Eğer bunlardan bir tanesi lazımsa, tabii ki DDS kolay çözümler sunabiliyor. 

En çok kullanılan QoS parametrelerinden bir tanesi bence DDS'in "Sampling" yapabilme yeteneği. Bu yetenek "History QoS" olarak biliniyor.  Örneğin her "key" değeri için kaç tane "sample" saklamak istediğimizi belirtmek ve bu key'in geçmişine gidebilmek kolaylaşıyor.

Diğer güzel QoS'ler Deadline Qos ve Liveliness Qos.  Yukarıdaki alıcı örneğinde alıcıların yedeklenmesi primary sensor, secondary sensor şeklinde çalışabilme imkanı elde edilebilir. 






27 Ocak 2021 Çarşamba

MongoDB graphLookup metodu

Örnek
Şöyle yaparız
db.nodes.aggregate([ 
 { $match: { treeId: 1001, $and: [ { nodeId: 100 } ] } },
 {
   $graphLookup: {
     from: "nodes",
     startWith: "$nodeId",
     connectFromField: "nodeId",
     connectToField: "parentId",
     restrictSearchWithMatch: {"treeId": 1001},
     maxDepth: 10000
     as: "descendants"
  }
  }
]);
Açıklaması şöyle. $graphLookup için bazı kısıtlar var.
We need the $match stage in order to filter the documents and pass only the documents that match the specified condition(s) to the next pipeline stage. In our case, we need to match a single document identified by its node Id and the tree it belongs to (identified by the tree Id).  This is the document/node from which the $graphLookup stage will begin the recursion from. Have in mind that the filed parent Id can be an Array i.e. we can multiple parents for nodes. Although not demonstrated here, this is supported by $graphLookup

For a full list of options and the internals of the $graphLookup operation, it is highly recommended to check the official documentation here. Make sure you pay attention to the following noteworthy limitations including the following:

  1. The collection specified in from field cannot be shared. This is an important consideration, due to the fact that lack of sharding   means lack of horizontal scalability. On the other hand, if we are dealing with a very large dataset dynamically increasing, then   perhaps other storage engines should be considered for heavy graph-like operations.

  2. The $graphLookup stage must stay within the 100 megabyte memory limit.
Döndürülen sonuç için de bazı kısıtlamalar var. Açıklaması şöyle
The results we get back from the pipeline execution are flat and no order is guaranteed.

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()));
  }
}

One-on-One Meetings

Giriş
Birebir toplantıların, idareciler faydası şöyle
1. Making observations
2. Exploring curiosity
3. Discussing growth
4. Encouraging a shift in mindset
5. Taking action
Exploring curiosity
Burada amaç çalışanları daha iyi tanımak. Bunun için ucu açık sorular sormak gerekiyor. Açıklaması şöyle
Learning about each individual requires asking good open-ended questions with the idea to enable deliberate thinking and engage in a meaningful conversation instead of asking questions that lead to a simple yes/no.

These questions can be covered over multiple one-on-one discussions and some can be repeated over a period of time to understand how they are evolving as an individual.

The most important part of being curious is to ask really good follow-up questions to connect with and understand each person at a deeper level.
 Açıklaması şöyle
"The most important thing you can do for your team collectively is to understand what growth trajectory each person wants to be on at a given time and whether that matches the needs and opportunities of the team. To do that, you are going to have to get to know each of your direct reports at a personal level."
Bazı ucu açık sorular şöyle
- What prevents you from doing your best work?
- What makes you tick?
- What kind of work helps you stretch your limits?
- What would you like to do more, stop doing, and do less to be effective in your work?
- What have you learned recently?
- How do you view team dynamics? [Is it a positive or negative source of energy?]
- Who do you find most helpful in the team? [Why?]
- How do you measure your own progress?
- How do you strive for continuous improvement?
- What catches your attention?
- What seems like a complete waste of time at work?
- What bothers you about work?
- How do you suggest we communicate to be in alignment? [e.g. progress, concerns, how to reach out, what needs my attention]
- What's your opinion about the processes followed in the team?
- What's the one thing interesting about you outside of work? [Any information that helps you get a peek into their personal life.]
- Which book, blog, video, or podcast inspired you recently? [What specific information inspired them?]
- What are your most productive hours? [What enables that level of productivity?]
- What's your favorite movie? [Share yours and why you like that movie.]
- What's your source of inspiration?
- What are your life goals?

21 Ocak 2021 Perşembe

AMQP Frame Tipleri

Giriş
Frame yapısı şöyledir


1. Frame Header
Her frame'in başında bir header var

1. 1. Type Alanı
Açıklaması şöyle
The first byte stores the type of the frame and can assume the value HEADER, METHOD, BODY or HEARTBEAT. 
5 çeşit Frame Type var. Bunlar şöyle

1.1.1 Protocol header
Açıklaması şöyle. Sadece bağlantı kurulurken kullanılır
This is the frame sent to establish a new connection between the broker (RabbitMQ) and a client. It will not be used anymore after the connection.
1.1.2 Method frame
Açıklaması şöyle.  Basic.Publish, Basic.Deliver gibi işler içindir
Carries a RPC request or response. AMQP uses a remote procedure call (RPC) pattern for nearly all kind of communication between the broker and the client. For example, when we are publishing a message, our application calls Basic.Publish, and this message is carried in a method frame, that will tell RabbitMQ that a client is going to publish a message.
1.1.3 Content header
Açıklaması şöyle.  Belli Method frame tiplerinden sonra gönderilir. Header frame içindeki bir çok alana bu yazıda aşağıda açıklanıyor.
Certain specific methods carry a content (like Basic.Publish, for instance, that carries a message to be published), and the content header frame is used to send the properties of this content. For example, this frame may have the content-type of a message that is going to be published and a timestamp.
1.1.4 Body
Açıklaması şöyle
This is the frame with the actual content of your message, and can be split into multiple different frames if the message is too big (131KB is the default frame size limit).
1.1.5 Heartbeat
Açıklaması şöyle
Used to confirm that a given client is still alive. If RabbitMQ sends a heartbeat to a client and it does not respond in timely fashion, the client will be disconnected, as it’s considered dead.
1.2 Channel Alanı
 Açıklaması şöyle. Frame Header içindeki ikinci alandır
The next byte stores the channel which identifies an independent thread of messages. Although the client establishes only a single TCP connection with the Message Broker, the connection is multiplexed — contrary to HTTP/1.1. This means that the client and the message-broker can utilise the same TCP connection to transmit multiple independent threads of messages. 
1.3. Size Alanı
Açıklaması şöyle.  Frame Header içindeki üçüncü alandır. Geriye kaç byte kaldığını belirtir.
The next byte indicates the size of the Payload.  
Frame Sonu
Her Frame'in sonunda da End Byte Marker diye bir şey daha var sanırım

Publish İşlemi
Toplam 3 frame kullanılır. Sırası şöyle
1. Method Frame'lerden birisi olan Basic.Publish Frame
2. Content Header Frame 
3. N tane Body Frame


Deliver İşlemi
Toplam 3 frame kullanılır. Sırası şöyle
1. Method Frame'lerden birisi olan Basic.Deliver Frame
2. Content Header Frame 
3. N tane Body Frame


Basic.Publish Frame İşlemi İçin Header Frame
Mesaj publish etmek için kullanılır



1. expiration
Mesajlar için expiration değeri tanımlanabilir. Mesajın header alanına "expiration" ve milisaniye cinsinden string değeri atanıyor. Eğer expire eden mesajlar için "Dead Letter Exchange" tanımlı değilse, mesaj silinir. 

2. priority
Açıklaması şöyle
RabbitMQ supports something called priority queues, meaning that a queue can be set to have a range of priorities. The priority of each message can be set when it is published. Depending on the priority of the message it is placed in the appropriate priority queue. 
Açıklaması şöyle
As of RabbitMQ 3.5.0, the priority field has been implemented as per the AMQP specification. It’s defined as an integer with possible values of 0 through 9 to be used for message prioritization in queues. As specified, if a message with a priority of 9 is published, and subsequently a message with a priority of 0 is published, a newly connected consumer would receive the message with the priority of 0 before the message with a priority of 9. Interestingly, RabbitMQ implements the priority field as an unsigned byte, so priorities could be anywhere from 0 to 255, but the priority should be limited to 0 through 9 to maintain interoperability with the specification. 
3. delivery-mode 
İki tane değere sahiptir. Açıklaması şöyle
The delivery-mode property has two possible values: 1 for a non-persisted message and 2 for a persisted message.
non-persisted mesajları broker saklamaz. Yanı broker yeniden başlatılırsa mesajlar kaybolur. Eğer saklanması isteniyorsa persistent modda kullanmak gerekir. Bir başka açıklama şöyle
RabbitMQ is often used with Apache Cassandra when application needs access to stream history, or with the LevelDB plugin for applications that need an “infinite” queue, but neither feature ships with RabbitMQ itself.
4. timestamp
 Açıklaması şöyle
Like message-id and correlation-id, timestamp is specified as “for application use.” Even if your message doesn’t use it, the timestamp property is very helpful when you’re trying to diagnose any type of unexpected behavior in the flow of messages through RabbitMQ. By using the timestamp property to indicate when a message was created, consumers can gauge performance in message delivery.
5. appid
Açıklaması şöyle. Mesajı üreten uygulamanın ismidir
The app-id property is defined in the AMQP specification as a “short-string,” allowing for up to 255 UTF-8 characters. If your application has an API-centric design with versioning, you could use the app-id to convey the specific API and version that were used to generate the message. As a method of enforcing a contract between publisher and consumer, examining the app-id prior to processing allows the application to discard the message if it’s from an unknown or unsupported source.
6.  user-id
Açıklaması şöyle. Mesajı üreten uygulamanın ismidir
In the use case of user authentication, it may seem obvious to use the user-id property to identify the user who has logged in, but in most cases this isn’t advisable. RabbitMQ checks every message published with a value in the user-id property against the RabbitMQ user publishing the message, and if the two values don’t match, the message is rejected. For example, if your application is authenticating with RabbitMQ as the user “www”, and the user-id property is set to “linus”, the message will be rejected.

Parser Çalışmalarım - XML Yapısı

Giriş
Bir kaç defa, XML'de tanımlı mesaj yapılarını okuyup bunları parse edebilen kodlarla uğraşmam gerekti. Bazı şeyleri not almak istedim

1. Birbirinden çok farklı XML yapılarını işleyebilecek jenerik bir kod yazmak çok zor bir şey. Var olan yapıya uymayan bir XML gelince bir sürü adaptor yazmak gerekiyor. Bu kadar uğraşa değmeyebilir.

İki Tane XML Lazım
Genellikle iki tane XML gerekiyor. Bu XML dosyalarına farkı farklı isimler verilebilir. Ben kısaca şöyle diyeceğim

1. Record Definition XML
2. Element Definition XML

Record Definition XML
Parse edilecek tüm mesajları içerir. Çoğu protokolde, Record içinde Record var. En azından mesajlarda header bulunuyor.

Element Definition XML
Record içindeki alanlar genellikle bir çok farklı yerde kullanılıyor. Bu yüzden element veya field diye düşünülebilecek bu yapıları farklı bir XML içinde tanımlamak gerekiyor.
1. Her Element 'in bir tane tekil key değeri bulunuyor. Buna identifier diyelim.

Örnek
Sadece 2 tane element 'ten ibaret çok basit bir record
<record id="foo">
  <field id="timeIndicator"/>
 <field id="statusIndicator"/>
</record>
Element yapısı şöyle. Burada field'ın her bir değerinin ne anlama geldiği gösteriliyor.
<element id="timeIndicator" length="2">
  <item code="0" value="No Time Indicator">
  <item code="1" value="Time Indicator">
</element>
XML okunur ve XmlDefinition ve ondan katılan sınıflar yaratılır. Bu sınıflar RecordDefinition, FieldDefinition vs. gibi şeyler. Definition sınıflar sadece XML'deki veriyi bilirler. Yani metadata.

Metadata'yı sarmalayan sınıflar lazım. Bunlar Record, Field gibi sınıflar.  

Record nesnesi kabaca şöyle. Her field kendi içindeki definition nesnesine bakarak stream'den ne kadar veri okuyacağını biliyor. 
public class Record {
  List<Field> fields;

  public boolean parse(Stream stream){
    //Field'ları dolaş ve stream'den okunan değerleri ata
  }
}
Örnek - Açmalı Kapamalı Alanlar
Bu yapılarda F1 gibi bir alanın değerine bakılır. 
- Eğer F1 = 0 ise, F2 ve F3 alanlar geçerlidir. 
- F1 farklı bir değere sahipse, F4 alanı geçerlidir. Yani kod olarak düşünürsek if/else yapısına benziyor

Birinci problem bu yapıyı XML olarak ifade edebilmek. İkinci problem ise bu yapıyı parse edebilecek jenerik kodu yazabilmek.

XML şöyle olabilir. Bu aslında çok basit bir örnek. Çok daha karmaşık şeyler düşünülebilir.
<record id="foo">
  <field id="timeIndicator"/>
  <field id="statusIndicator"/>
  <structureswitch id="indicatorSwitch">
    <when>
      <case value="1"/>
      <field id="voiceIndicator"/>
    </when>
    <when>
      <case value="2"/>
        <field id="smsIndicator"/>
    </when>
    <otherwise>
      <field id="emailIndicator"/>
    </otherwise>
  </structureswitch>
</record>


Consistent Hashing

Giriş
Consistent Hashing, dağıtık sistemlerde istekleri dağıtmak işinde çok kullanılır. 

1. Faydası Nedir?
En büyük faydası klasik HashMap tarzı veri yapılarındaki rehashing() maliyetinden bizi kurtarması. Böylece bir düğüm silinince tüm dünya yer değiştirmiyor, sadece silinen düğüme ait key değerler geriye kalan düğümlere eşit şekilde dağıtılıyor.

Bu da tam olarak bir dağıtık sistemin istediği şey. Yani en az yer değişikliği. Consistent Hashing her zaman dairesel olarak gösteriliyor. 

2. Algoritma
Açıklaması şöyle. İlk olarak 1997 yılında yayınlanıyor, ancak 2007 yılına kadar yaygın değil
In 1997, the paper “Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web” was released. This paper described the approach used by Akamai in their distributed content delivery network.

It took until 2007 for the ideas to seep into the popular consciousness. That year saw two works published:
- last.fm’s Ketama memcached client.
Dynamo: Amazon’s Highly Available Key-value Store

These cemented consistent hashing’s place as a standard scaling technique. It’s now used by Cassandra, Riak, and basically every other distributed system that needs to distribute load over servers.
Algoritma şöyle
Consistent Hashing is organized in the following manner:

1. The servers are hashed using their IP addresses and assigned the position based on the hashing function.
2. Similarly, the keys are hashed to positions using the same hashing function and placed in the virtual ring.
3. Map the keys with the server having the same position, and in case the position don’t match, then assign the key to the first server that we get while moving in a clockwise direction.

Hence in this manner, the keys are assigned to the server in Consistent Hashing. The beauty of Consistent Hashing comes when we add or remove servers.
Bir başka anlatım şöyle
You can think of the circle as all integers 0 ..2³²-1. The basic idea is that each server is mapped to a point on a circle with a hash function. To lookup the server for a given key, you hash the key and find that point on the circle. Then you scan forward until you find the first hash value for any server.
Birinci maddede sunucuları çembere yerleştirmek için IP adresleri kullanılır deniliyor ancak her hangi başka bir özellik te kullanılabilir. Açıklaması şöyle
.... use any server related info to find out the hash, eg, IP address) to store our objects and servers on this circle, i.e, our hash ring
Şeklen şöyle. Burada hash değeri saat yönünde bir sonraki sunucuya yönleniyor. Algoritma saatin tersi yönünde de kullanılabilirdi. İkisi arasında bir fark yok

Eğer D sunucusunu eklersek şeklen şöyle. Burada C'ye giden yük artık ikiye bölündü. Bir kısmı D'ye yönlendi, bir kısmı da değişmedi.
Eğer A sunucusu kapatılırsa, A'nın yükünü B devralır, ancak D ve C etkilenmez. Şeklen şöyle



Ancak bu algoritmada bir eksiklik var. O da Non-Uniform Distribution. 

3. Algoritmada Bir Düzeltme - Non-Uniform Distribution Eksikliği
Non-Uniform distribution eksikliği için açıklama şöyle. Yani zaman içinde tüm yük belli sunuculara yığılmaya başlıyor. Bunu düzeltmek için sunucu birden fazla kez ekleniyor. Yani aynı sunucundan Server1 mesela 2 tane var. Bu sunuculara vnode veya replica deniliyor.
There is a shortcoming of this approach. All the keys may get mapped to the same server, and hence one server will get all the workload, and all the other servers will remain idle. This situation is very inefficient and is very prone to failure. To deal with this, a new concept has been introduced. All the servers are replicated and arranged at different positions in the ring. In this manner, with an increased number of servers, the distribution becomes much uniform and helps in the service’s scaling.
Bir başka açıklama şöyle
In practice, each server appears multiple times on the circle. These extra points are called “virtual nodes”, or “vnodes”. This reduces the load variance among servers. With a small number of vnodes, different servers could be assigned wildly different numbers of keys.

(A brief note on terminology. The original consistent hashing paper called servers “nodes”. Papers will generally talk about“nodes”, “servers”, or “shards”.
Şeklen şöyle

Bir başka şekil şöyle

4. VNode Durumu Tam Düzelmiyor
Açıklaması şöyle
Ring hashing presents a solution to our initial problem. Case closed? Not quite. Ring hashing still has some problems.

First, the load distribution across the nodes can still be uneven. With 100 replicas (“vnodes”) per server, the standard deviation of load is about 10%. The 99% confidence interval for bucket sizes is 0.76 to 1.28 of the average load (i.e., total keys / number of servers). This sort of variability makes capacity planning tricky. Increasing the number of replicas to 1000 points per server reduces the standard deviation to ~3.2%, and a much smaller 99% confidence interval of 0.92 to 1.09.

This comes with significant memory cost. For 1000 nodes, this is 4MB of data, with O(log n) searches (for n=1e6) all of which are processor cache misses even with nothing else competing for the cache.
5. Diğer Seçenekler
Bazı diğer algoritmalar şöyle. Her birisinin kendisine göre artı ve eksileri var
- Jump Hash
- Multi-Probe Consistent Hashing
- Rendezvous Hashing
- Maglev Hashing

6. Kod
Örnek
Şu satırı dahil ederiz
import java.util.Collection;  
import java.util.SortedMap;  
import java.util.TreeMap;  
import com.google.common.hash.HashFunction;
Şöyle yaparız. Bu kodda numberOfReplicas değişkeni ile non-Uniform Distribution da ele alınıyor.
public class ConsistentHash<T> {  
 
 private final HashFunction hashFunction;  
 private final int numberOfReplicas;  
 private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();  
 
 public ConsistentHash(HashFunction hashFunction, int numberOfReplicas,  
     Collection<T> nodes) {  
   this.hashFunction = hashFunction;  
   this.numberOfReplicas = numberOfReplicas;  
 
   for (T node : nodes) {  
     add(node);  
   }  
 }
 public void add(T node) {  
   for (int i = 0; i <numberOfReplicas; i++) {  
     circle.put(hashFunction.hash(node.toString() + i), node);  
   }  
 }  
 public void remove(T node) {  
   for (int i = 0; i <numberOfReplicas; i++) {  
     circle.remove(hashFunction.hash(node.toString() + i));  
   }  
 }  
 public T get(Object key) {  
   if (circle.isEmpty()) {  
     return null;  
   }  
   int hash = hashFunction.hash(key);  
   if (!circle.containsKey(hash)) {  
     SortedMap<Integer, T> tailMap = circle.tailMap(hash);  //>= yani saat yönünde
     hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();  
   }  
   return circle.get(hash);  
 }  
}
Açıklaması şöyle
From a programming perspective, what we would do is keep a sorted list of server values (which could be angles or numbers in any real interval), and walk this list (or use a binary search) to find the first server with a value greater than, or equal to, that of the desired key. If no such value is found, we need to wrap around, taking the first one from the list.

To ensure object keys are evenly distributed among servers, we need to apply a simple trick: To assign not one, but many labels (angles) to each server. So instead of having labels A, B and C, we could have, say, A0 .. A9, B0 .. B9 and C0 .. C9, all interspersed along the circle. The factor by which to increase the number of labels (server keys), known as weight, depends on the situation (and may even be different for each server) to adjust the probability of keys ending up on each. For example, if server B were twice as powerful as the rest, it could be assigned twice as many labels, and as a result, it would end up holding twice as many objects (on average).
- add() metodunda her node isminin numberOfReplicas kadar eşit dağılacak şekilde TreeMap'e eklendiği görülebilir
- get() metodunda tailMap() ile >= anahtar değerine sahip SortedMap bulunuyor. Eğer SortedMap boş ise circle.firstKey() ile ilk eleman döndürülüyor
- Eğer remove() ile bir tane node silinirse bile, mevcut atamalara bir şey olmuyor. Yani A0'a atanmış değer yine A0'da kalıyor. Sadece silinen node - mesela C olsun - ile ilgili atamalar farklı yerlere dağıtılıyor. Açıklaması şöyle
So, what’s the benefit of all this circle approach? Imagine server C is removed. To account for this, we must remove labels C0 .. C9 from the circle. This results in the object keys formerly adjacent to the deleted labels now being randomly labeled Ax and Bx, reassigning them to servers A and B.

But what happens with the other object keys, the ones that originally belonged in A and B? Nothing! That’s the beauty of it: The absence of Cx labels does not affect those keys in any way. So, removing a server results in its object keys being randomly reassigned to the rest of the servers, leaving all other keys untouched:



20 Ocak 2021 Çarşamba

NGINX - nginx.conf Dosyası - HTTP proxy

Giriş
NGINX kendisine gelen istekleri bir başka adrese yönlendirir (forward). Yani sunucularımız önüne koyduğumuz ve kendisine gelen istekleri sunucularımıza yönlendiren reverse proxy olarak çalışır.

upstream kavramı
Reverse proxy kullanırken bazen karşımıza upstream kelimesi çıkar. Açıklaması şöyle. Yani upstream ile kendi sunucumuz kastediliyor.
In the context of a reverse proxy server, an upstream server is a server that the reverse proxy forwards requests to. The upstream server can be any server that can handle the request, such as a web server, application server, or microservice.
server_name Alanı
server_name ile Nginx'in dinleyeceği adres belirtilir. Bu adresin altına açılan location XXX şeklindeki alanlarla server_name için gelen isteklerin nereye yönlendirileceği belirtilir. proxy_pass ile yönlendirilecek adres belirtilir.

Böylece hem Reverse Proxy, hem de Forward Proxy olarak görev yapabilir.

Örnek - forward proxy
Şöyle yaparız
server {
  listen 81;

  location / {
    resolver 8.8.8.8;
    proxy_http_version 1.1;
    proxy_pass https://$host$request_uri;
  }
}
Örnek - reverse proxy
Aynı birden fazla virtual server çalışıyorsa şöyle yaparız.
server {
  server_name device1.example.com;
  location / {
    proxy_pass http://192.168.0.1:80;
  }
}
server {
  server_name device2.example.com;
  location / {
    proxy_pass http://192.168.0.2:80;
  }
}
server {
  server_name device3.example.com;
  location / {
    proxy_pass http://192.168.0.3:80;
  }
}
Örnek - reverse proxy
Şöyle yaparız. Burada niçin proxy_set_header  alanlarını atamak lazım bir gün öğrenirsem yazarım.
worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
    proxy_set_header   X-Forwarded-Port $server_port;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   X-Forwarded-Prefix $contextpath;
    proxy_set_header   X-Request-Id $pid-$msec-$remote_addr-$request_length;

    server {
        listen 8080;

        resolver 127.0.0.11 valid=30s;

        location /messaging {
            set $upstream messaging-api;
            set $contextpath /messaging;

            client_max_body_size 50M;
            rewrite            ^/messaging(/|$)(.*) /$2 break;
            proxy_pass         http://$upstream:8080;
            proxy_redirect     off;
        }
    }
}
Örnek - reverse proxy + url rewrite
Şöyle yaparız. Burada "rewrite ^/lekana(.*)$ $1 break;" ile url içindeki lekana siliniyor.
server {
    listen          80;
    server_name     bassa.com www.bassa.com;

    location / {
        proxy_pass http://bassa-api:7656;
    }

    location /lekana {
        rewrite ^/lekana(.*)$ $1 break;
        proxy_pass http://lekana-api:7654;
    }

    location /siddhi {
        rewrite ^/siddhi(.*)$ $1 break;
        proxy_pass http://siddhi-api:7655;
    }
}

Memcached

Giriş
Not : Memcached muadili olarak "Oracle Coherence" var.

Bazı notlar şöyle.  Memcached ile clustering ve replication yetenekleri yoktur. 
Memcached’s deliberately simple architecture provides flexibility in building abstractions on top of it, as well as provides easy horizontal scalability to meet increased demand. A single memcached process by itself is a simple key-value store and deliberately has no knowledge of its peers, or even the notion of a memcached cluster.
- Sadece key/value çiftini bilir. Redis gibi farklı veri yapılarını desteklemez. 
- Memcached multi-threaded çalışır.

Memcached vs Redis
Açıklaması şöyle
Use Memcached when : Simple Key Value storage needed. Need to only store string. No need to perform any operational query on cache. Scaling vertically by using more cores and threads is easier. When keys are maximum 250 B and Values maximum 1MB, when you are ok with only LRU eviction policy. Its Volatile.

Use Redis when: You need to store objects. Scaling horizontally is easier. You need to store Set, Hash, List, Sorted Set (A non-repeating list of string values ordered by a score value). When you want to chose from multiple eviction policies. When you would want to save data (its non volatile)
Bellek Dolarsa
Açıklaması şöyle. Normalde her şey sadece bellekte saklanır
Strictly in memory and extended to save key-value details into drive using an extension extstore
Supported Eviction Policies
Açıklaması şöyle
Least Recently Used (LRU)
Transaction
Açıklaması şöyle
Memcached doesn’t support transactions
Mcrouter 
Facebook tarafından geliştirilen ve 2014 yılında open source yapılan bir ürün. Memcached sunucularının önüne konuluyor. 

Mcrouter + Consistent Hashing kullanarak havuza Memcached sunucuları eklenebiliyor. Açıklaması şöyle
Mcrouter applies a hashing algorithm against the cache key of every incoming request to deterministically shard the request to one host within a pool. This works well for evenly distributing traffic among servers, but memcached has a unique requirement that its clusters need to be arbitrarily scalable — operators need to be able to freely adjust cluster capacity in response to changing traffic demands while minimizing the client-side impact.

Consistent hashing ensures that most of a keyspace partition maps to the same server even as the total number of eligible shards increases or decreases. This allows the system to scale out transparently to the client layer due to highly localized and predictable hit rate impact, thus reducing the chance that small changes in capacity cause catastrophic drops in cluster-wide hit rate.

Apache Kafka Replication

Replication Nedir?
Açıklaması şöyle
But what if a broker managing a single partition stops responding or goes down? How would you read the messages written to that partition?
Replication or data redundancy comes to rescue here. Every partition has a replica stored on a different broker. Messages are synced or copied to the replica. If the primary partition fails, then the messages can be read from the replica. 

Broker managing the primary partition is known as Leader for that partition. Other brokers which handle the replica for the same partition are followers.

A broker can be a leader for a given partition and it can function as a follower for other partitions. 

Producers always write the data to the Leader. Once the Leader commits the data, followers poll the leader to bring their replicas in syn with the Leader. 
Şeklen şöyle. Partition'a yazma işlemi Leader Partition üzerinden yapılır. Verinin kaybolması ihtimaline karşı Leader Partition veriyi aynı cluster içindeki farklı broker'lar üzerin çalışan Follower Partition'lara da dağıtılır. 


Leader Partition
Açıklaması şöyle. Leader partition yazma işlemini gerçekleştirir ve veriyi follower partition'lara dağıtır.
Among the multiple partitions, there is one `leader` and remaining are `replicas/followers` to serve as back up. Kafka always allows consumers to read only from the leader partition. A leader and follower of a partition can never reside on the same broker for obvious reasons. Followers are always sync with a leader. The broker chooses a new leader among the followers when a leader goes down. A topic is distributed across broker clusters as each partition in the topic resides on different brokers in the cluster.
Açıklaması şöyle. Burada durability yani verinin kalıcılığı ve availability yani süreklilik atasında denge var.
1. For each partition, there exists one leader broker and N follower brokers. The config which controls how many such brokers (1 + N) exist is replication.factor .
2. An in-sync replica (ISR) is a broker that has the latest data for a given partition. A leader is always an in-sync replica. A follower is an in-sync replica only if it has fully caught up to the partition leader it’s following.
3. The time period can be configured via replica.lag.time.max.ms. If a broker goes down or has network issues, then it couldn’t follow up with the leader and after 10 seconds, this broker will be removed from ISR.
4. Producer clients only write to the leader broker — the followers asynchronously replicate the data.
5. replication.factor is a broker configuration which denotes the total amount of times the data inside a single partition is replicated across the cluster.
6. acks is a producer configuration which specifies the number of brokers that must receive the record before it is considered a successful write.
7. When a producer is set to acks=0 , the write is considered successful the moment the request is sent out without waiting for the broker to send an acknowledgement.
8. When a producer is set to acks=1 , the write is considered successful when the message is acknowledged by only the partition leader.
9. When a producer is set to acks=all or acks=-1, min.insync.replicas specifies the minimum number of in-sync replicas required to exist in ISR list for the request to be processed.
10. acks and min.insync.replicas setting together form a good way to configure the preferred trade-off between durability guarantees and availability performance.