10 Ağustos 2021 Salı

Debezium

Debezium sanırım en popüler CDC aracı. Açıklaması şöyle.
Its name comes from the combination of “DB” (a.k.a Database) and “-ium” (a very common suffix found in many elements of the periodic table). Debezium platform uses Kafka as Data Change Events Log. From version 1.2.x Debezium included support for CloudEvents format, which open the possibility to seamlessly integration to Event-Driven Architectures.

The pluggable model, used by many CDC tools like Debezium, can be useful for Database replication, feed analytics systems, produce KPIs, and populate caches. A monolith system can, for instance, start to produce CDC events that can feed new individual databases during migration from Monoliths to Microservices. Or an event from a relational Database can seamlessly feed a NoSQL database.
Şeklen şöyle
Debezium ve Kafka Connect İlişkisi

Source Connectors
Açıklaması şöyle
Debezium currently ships connectors for MySQL, PostgreSQL, SQL Server, Oracle, Db2, and MongoDB.
Debezium as a library
Şu satırı dahil ederiz
<dependency>
 <groupId>io.debezium</groupId>
 <artifactId>debezium-api</artifactId>
 <version>${version.debezium}</version>
</dependency>

<dependency>
 <groupId>io.debezium</groupId>
 <artifactId>debezium-embedded</artifactId>
 <version>${version.debezium}</version>
</dependency>
Eğer MySQL Connector için şu satırı dahil ederiz
<dependency>
<groupId>io.debezium</groupId> <artifactId>debezium-connector-mysql</artifactId> <version>${version.debezium}</version> </dependency>
Debezium as a standalone server
Açıklaması şöyle
The Debezium server is configured to use one of the Debezium source connectors to capture changes from the source database. Change events can be serialized to different formats like JSON or Apache Avro and then sent to one of the various messaging infrastructures such as Amazon Kinesis, Google Cloud Pub/Sub, or Apache Pulsar.
PostgreSQL Connector
1. Debezium Sürümü 0.10 ve Büyükse
Açıklaması şöyle
A logical decoding output plugin is no longer needed if you use a Debezium version greater than 0.10. As of Debezium 0.10, the connector supports PostgreSQL 10+ logical replication streaming using pgoutput, which emits changes directly from the replication stream.
2 Debezium Sürümü Küçükse
Açıklaması şöyle
As of PostgreSQL 9.4, logical decoding is implemented by decoding the contents of the write-ahead log and processing them in a user-friendly manner with the help of an output plugin. The output plugin enables clients to consume the changes.
Debezium has a Postgres Connector that works with the following output plugins.
- protobuf to encode changes in Protobuf format.
- wal2json to encode changes in JSON format.
wal2json kurulumu
Postgre içinde şöyle yaparız
$ apt-get update && apt-get install postgresql-13-wal2json
Registering the PostgreSQL connector with Kafka Connect
Debezium'a bir Json post etmek gerekiyor. 

Açıklaması şöyle. Yani önce bir snapshot oluşturulur, daha sonra değişiklikler işlenmeye başlanır.
Once connected, Debezium will perform an initial snapshot of your data and emit change events to a Kafka Topic. Then, services can consume the topics and act on them.
Örnek
Elimizde şöyle bir kod olsun
import org.apache.kafka.common.config.ConfigDef;
import org.apache.kafka.connect.connector.ConnectRecord; import org.apache.kafka.connect.data.Schema; import org.apache.kafka.connect.data.Struct; import org.apache.kafka.connect.header.Headers; import org.apache.kafka.connect.transforms.Transformation; public class OutboxTransformer<R extends ConnectRecord<R>> implements Transformation<R> { @Override public R apply(R record) { Struct kStruct = (Struct) record.value(); String databaseOperation = kStruct.getString("op"); if ("c".equalsIgnoreCase(databaseOperation)) { Struct after = (Struct) kStruct.get("after"); String UUID = after.getString("id"); String payload = after.getString("payload"); String eventName = after.getString("event_name").toLowerCase(); String topic = eventName.toLowerCase(); Headers headers = record.headers(); headers.addString("eventId", UUID); // Prepare the event to be published. record = record.newRecord(topic, null, Schema.STRING_SCHEMA, UUID, null, payload, record.timestamp(), headers); } return record; } @Override public ConfigDef config() {return new ConfigDef();} @Override public void close() {} @Override public void configure(Map<String, ?> configs) {} }
Bu kodu build etmek için şöyle yaparız
$ cd outbox-transformer
$ ./gradlew clean build
$ docker build -t outbox-transformer .
Docker dosyamız şöyledir
FROM debezium/connect
ENV DEBEZIUM_DIR=$KAFKA_CONNECT_PLUGINS_DIR/debezium-transformer

RUN mkdir $DEBEZIUM_DIR
COPY build/libs/outbox-transformer-0.0.1-SNAPSHOT.jar $DEBEZIUM_DIR
Docker Compose
Docker Compose ve Debezium yazısına taşıdım

PosgtreSQL Connector
PosgtreSQL Connector yazısına taşıdım

2. MySQL Connector

Registering the MySQL connector with Kafka Connect
Açıklaması şöyle
You can register the MySQL connector by sending a POST request to the Kafka Connect API.
Örnek
Şöyle yaparız
{
  "name": "delayed-email-message",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "tasks.max": "1",
    "database.hostname": "mysql",
    "database.port": "3306",
    "database.user": "root",
    "database.password": "rootpass",
    "database.server.id": "184054",
    "database.server.name": "dbserver1",
    "database.whitelist": "emails",
    "database.history.kafka.bootstrap.servers": "kafka:9093",
    "database.history.kafka.topic": "delayed.emails.history",
    "key.converter": "org.apache.kafka.connect.json.JsonConverter",
    "value.converter": "org.apache.kafka.connect.json.JsonConverter",
    "key.converter.schemas.enable": "false",
    "value.converter.schemas.enable": "false",
    "table.whitelist": "emails.delayed_messages",
    "transforms": "Reroute, filter, unwrap",
    "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
    "transforms.unwrap.drop.tombstones": "true",
    "transforms.Reroute.type": "io.debezium.transforms.ByLogicalTableRouter",
    "transforms.Reroute.topic.regex": "(.*)(delayed_messages)$",
    "transforms.Reroute.topic.replacement": "email.execution",
    "transforms.filter.type": "io.debezium.transforms.Filter",
    "transforms.filter.language": "jsr223.groovy",
    "transforms.filter.topic.regex": "email.execution",
    "transforms.filter.condition": "value.op == \"u\" && value.before.is_ready == false && value.after.is_ready == true"
  }
}
Açıklaması şöyle
The connector is configured on the delayed_messages table (lines 19), and will only respond to an update operation that has is_ready=false, changed to is_ready=true.

Let’s have a closer look at the transformers section
ExtractNewRecordState — is used for message flattening
Reroute — changes the default Debezium Kafka topic to “email.execution”
Filter — filters messages with update operation, that changes its is_ready flag from false to true. That ensures that the Debezium connector produces a message for this specific update operation only, and ignores any other operations
Oracle Connector
Debezium Connector yazısına taşıdım

Kafka topics
Açıklaması şöyle
By default, Debezium creates a Kafka topic for each table in a database. The naming convention is similar to the following.
server.database.table
Yani dbserver1 sunucusundaki inventory veri tabanındaki customers tablosu için şöyledir
dbserver1.inventory.customers




7 Ağustos 2021 Cumartesi

Web Authentication Standard

Giriş
Çoğu tarayıcı da artık var. Açıklaması şöyle
Long story short, with WebAuthentication you can authenticate in an application without sending your password to any remote server by using either your OS credentials, fingerprint or face recognition etc.
Nasıl Çalışır
Açıklaması şöyle
... it works by generating a site-specific private key and storing that key somewhere on the user's device. Then, when the user wants to sign into a site, their browser uses this stored key to prove the user's identity to the site using public key authentication, signing them in.

So where do biometrics come into this? Using the web authentication standard, it is possible for websites to request that the user's browser verify their identity locally before allowing them to sign-in using the stored public key. This local verification can be done using biometrics, among other methods.
Biometric Olarak Ne Kullanılabilir
Biometric veriyi tanımlarken "something you are" ifadesi kullanılıyor. Bu ifadenin açıklaması şöyle
"Something you are" refers to biometric identification. Examples of this could include:
- fingerprint
- voice print
- facial recognition
- vein pattern and blood flow detection
- behavioral biometrics, such as gait or typing timing

This is described in NIST SP 800-63B section 5.2.3 which does not list specific methods, but describes requirements and how to use them.

Section 10.4 lists usability considerations of these specific biometrics, including problems with their reliability:
- Fingerprints
- Face
- Iris
Yani Biometric Veri Sunucuya Gönderilmez
Açıklaması şöyle
The user never authenticates to the internet service with biometrics. Instead, the user authenticates to a local device (fingerprint reader, mobile phone, ...), and then that device authenticates to the service. The service trusts the device to perform the user authentication.

Notice how the service never receives the biometrics themselves - so they can't be leaked through a service or database compromise.
Örnek
Bu işi anlatan örnek  yazı burada. Kaynak kodu burada. Aslında işin temelini yapan kütüphaneler şöyle
<dependency> <groupId>com.yubico</groupId> <artifactId>webauthn-server-core</artifactId> <version>1.7.0</version> <exclusions> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.yubico</groupId> <artifactId>yubico-util</artifactId> <version>1.7.0</version> </dependency>
Repository Sınıfları
User şöyledir ve haliyle bir UserRepository vardır
public class WebAuthnUser implements UserDetails {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String username;
  private byte[] recoveryToken;
  private byte[] addToken;
  private LocalDateTime registrationAddStart;
  ...
}
Credentials şöyledir. Bu ilgili kullanıcının credentials bilgisidir.
public class WebAuthnCredentials {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private byte[] credentialId;
  private Long appUserId;
  private Long count;
  private byte[] publicKeyCose;
  private String userAgent;
  ...
}
Registration Start
Burada sadece tarayıcı aslında sadece username'i göndermek zorunda. Diğer alanlar registration başladıktan sonra eğer istenirse yeni token eklemek için kullanılabiliyor. İşlemin sonucunda bir
WebAuthnUser yaratılır. Gelen istek şöyle
public class RegistrationStartRequest {

  private String username;
  private String registrationAddToken;
  private String recoveryToken;
  ...
}
JSON olarak şöyle
{"username":"newjunit","registrationAddToken":null,"recoveryToken":null}
Döndürülen cevap şöyle
public class RegistrationStartResponse {

  public enum Status {
    OK, USERNAME_TAKEN, TOKEN_INVALID
  }

  public enum Mode {
    NEW, ADD, RECOVERY
  }

  @JsonIgnore
  private final Mode mode;

  private final Status status;

  private final String registrationId;
  
  //RelyingPartyIdentity + UserIdentity + challenge
  private final PublicKeyCredentialCreationOptions publicKeyCredentialCreationOptions;
  ...
}
JSON olarak şöyle
{
  "status": "OK",
  "registrationId": "Ey6LyiaXLwgM8wxeNIXnKQ==",
  "publicKeyCredentialCreationOptions": {
    "rp": {
      "name": "localhost",
      "id": "localhost",
      "icon": {
        "empty": false,
        "present": true
      }
    },
    "user": {
      "name": "newjunit",
      "displayName": "newjunit",
      "id": "AAAAAAAAAAM",
      "icon": {
        "empty": true,
        "present": false
      }
    },
    "challenge": "hZV-7roGRNnDzytShOxyDAvVTAHQTcVamfr2TYmDJZg",
    "pubKeyCredParams": [
      {
        "alg": -7,
        "type": "public-key"
      },
      {
        "alg": -8,
        "type": "public-key"
      },
      {
        "alg": -257,
        "type": "public-key"
      }
    ],
    "timeout": {
      "empty": true,
      "present": false
    },
    "excludeCredentials": {
      "empty": false,
      "present": true
    },
    "authenticatorSelection": {
      "empty": true,
      "present": false
    },
    "attestation": "none",
    "extensions": {}
  }
}
Registration Finish
Gelen istek şöyle
public class RegistrationFinishRequest {

  private final String registrationId;
  private String userAgent;
  
  //Signed credential
  private PublicKeyCredential<AuthenticatorAttestationResponse,
    ClientRegistrationExtensionOutputs> credential;
}
Registration Repository'den registrationId ile nesne bulunur. Doğrulama yapılır ve WebAuthnCredentials nesnesi kaydedilir. 

Assertion Start ve Assertion End
Doğrulama yapılır







6 Ağustos 2021 Cuma

RabbitMQ Fanout Exchange - Ana Gemi Tüm Gemilere Mesaj Gönderir

Giriş
Fanout Exchange -  Tüm kuyruklara mesajı yönlendirir. Routing key dikkate alınmaz. Açıklaması şöyle
 A fanout exchange copies and routes a received message to all queues that are bound to it regardless of routing keys or patterns. The keys provided will simply be ignored.
Şeklen şöyle

Açıklaması şöyle
In the above diagram each consumer is independent of the others and receives its own copies of all the messages. To scale out the Consumer App 1, more instances of that application would need to be deployed, consuming from the same Queue 1.

Fanout exchanges are one of the fastest exchanges as they do not need to inspect any routing key or message header. Although an exchange may send a single message to multiple queues, in reality it doesn't necessarily duplicate the bits. It can persist the message to the message store and simply register a pointer to the message in each queue.


RabbitMQ Direct Exchange - Küçük Gemiler Ana Gemiye Mesaj Gönderir

Giriş
Routing key = Binding key ise mesajı kuyruğa yönlendirir. Açıklaması şöyle
Direct exchanges route messages to all queues with an exact match for something called a routing key. 
Bu şu anlama gelir. Direct Exchange'e bir kuyruk ya da birden fazla kuyruk takılabilir. 

Mesaj gönderirken kuyruk ismi routing key olarak belirtilir.

CockroachDB

Giriş
Açıklaması şöyle
CockroachDB is a globally distributed SQL database constructed on top of a transactional and consistent key-value store that you can use everywhere. The database tool is optimized for the cloud to deliver guaranteed transactions for local and globally distributed workloads and it allows you to build global, scalable and resilient cloud services. 
Why is CockroachDB compatible with PostgreSQL?
Burada CockroachDB ve PostgreSQL uyumluluğu anlatılıyor

CockroachDB Mimarisi
Açıklaması şöyle
CockroachDB is implemented as a distributed key-value store over a monolithic sorted map, to make it easy for large tables and indexes to function. While CockroachDB is a distributed SQL database, developers treat it as a relational database because it uses the same SQL syntax. But on an architecture level, CockroachDB’s architecture is different from a relational database architecture. In CockroachDB, every table is ordered lexicographically by key. So, when we store the data on the database, we are leveraging the key value store.
Distributed SQL Database Özellikleri
Şeklen şöyle

1. Multi-Datacenter Clustering
Yani birden fazla  region ve datacenter'da çalışabilmesi

2. Active-Active (Peer-to-peer) vs Primary-Replica
Yani yazma işlemlerini herhangi bir düğümün yapabilmesi veya tek bir primary/master düğümün yapabilmesi. Bu açıklaması  Replication başlığı altında yazılmış ancak bence bu başlık altında olmalı. Açıklaması şöyle
Distributed systems designs also affect how you might distribute data across the different racks or datacenters you’ve deployed to. For example, given a primary-replica system, only the datacenter with the primary can serve any write workloads. Other datacenters can only serve as a read-only copy.

In a peer-to-peer system that supports multi-datacenter clustering, each node in the overall cluster can accept reads or writes. This allows for better geographic workload distribution.
3. Replication
Açıklaması şöyle. Yine kullanılan model göre yazma işlemine göre quorum seçiliyor.
Operations then can have different levels of consistency. You might have a local quorum read or write at the three node datacenter — requiring two of three nodes to be updated for local quorum. Or you might have a cluster-wide quorum, requiring any three nodes across either or both datacenters to be updated for an operation to be successful. Tunable consistency, combined with multi-datacenter topology awareness, basically gives you a lot more flexibility to customize workloads.
4. Topology Awareness
Açıklaması şöyle. Yani aynı data center içinde bile availability zone tanımlanabilmesi
.. if all your nodes were installed in the same rack, and if that rack went down, that’s no good. So topology awareness was added so that you could be rack aware within the same datacenter. This ensures you spread your data across multiple racks of that datacenter, thus minimizing outages if power or connectivity is lost to one rack or another. That’s the barest-bones form of topology awareness you’d want.



5 Ağustos 2021 Perşembe

Redis Cluster Mode With Shards - Availability İçindir

Giriş
Cluster mode kullanırken Sentinel gerekmez. Açıklaması şöyle
You don’t need Sentinel when using Redis cluster.
Redis Cluster performs automatic failover if any problem occur in any primary instance.
Cluster Mode Nedir?
Verinin tek bir düğüme sığmaması ve availability yüzünden dağıtılmasıdır. Açıklaması şöyle
But what if we’re dealing with large data that can’t be contained in one node? Redis supports clusters to shard your data across multiple nodes.
Cluster Mode iki şekilde olabilir
- Sharding with a Redis cluster
- Sharding and Replication with Redis cluster - En çok kullanılan bu

Slots Nedir
The entire keyspace in Redis Clusters is divided into 16384 slots (called hash slots) and these slots are assigned to multiple Redis nodes. A given key is mapped to one of these slots and the hash slot for a key is computed:

HASH_SLOT = CRC16(key) mod 16384

In most cases, you don’t need to know these internals as Redis will take care of the push and pull of data from the right cluster.
Açıklaması şöyle
Redis Cluster does not use consistent hashing, but a different form of sharding where every key is conceptually part of what we call a hash slot. There are 16384 hash slots in Redis 
Cluster, and to compute what is the hash slot of a given key, we simply take the CRC16 of the key modulo 16384. Every node in a Redis Cluster is responsible for a subset of the hash slots, so for example you may have a cluster with 3 nodes, where:

- Node A contains hash slots from 0 to 5500.
- Node B contains hash slots from 5501 to 11000.
- Node C contains hash slots from 11001 to 16383.

1. Sharding With Cluster Nedir?
Açıklaması şöyle. Yani verinin dağıtılmasıdır.
If data is to be sharded to several nodes, Redis offers an open-source version of Redis Cluster. It supports building a cluster from several replication groups. Data within the cluster is sharded over 16 384 slots. Slot ranges are determined among Redis nodes.

Nodes within the cluster communicate over a separate open port to know their neighbors’ statuses. To work with Redis Cluster, the app should use a special connector.
Şeklen şöyle.


2. Sharding And Replication With Cluster Nedir?
Şeklen şöyle. Yani verinin dağıtılması ve her düğümün de replica'sının olmasıdır
Şeklen şöyle
Açıklaması şöyle
What is the Redis Cluster?

Data is automatically partitioned over many Redis nodes, resulting in a stable and reliable data service. So automatically split your dataset among multiple nodes. A cluster must have at least three master nodes to work correctly, with Redis recommending that each master have at least one slave and requires Redis version 3.0 or higher.

Every node should open two TCP connections. The standard Redis TCP port for serving clients. The second port is utilized for the Cluster bus, which is a binary protocol-based node-to-node communication.

If a master instance becomes unavailable due to network failures or software/hardware failures, the other Master nodes will notice by the Cluster bus and reach a failover state. After then, a suitable slave of the unavailable Master node will step forward and be promoted to become the new Master.
Cluster istemciyi doğru sunucuya yönlendirir. Açıklaması şöyle
In Redis Cluster nodes don't proxy commands to the right node in charge for a given key, but instead, they redirect clients to the right nodes serving a given portion of the keyspace. 
İyi ve kötü yönleri şöyle

Redis Cluster Pros:
- It has no central architecture, automatically splits data among multiple nodes.
- Data is distributed among several nodes based on a hash slot, and data distribution can be adjusted dynamically.
- Scalability: Add and remove nodes in the cluster easily. Nodes can be added or removed dynamically, and the system can scale up to 1000 nodes.
- Automatic failover can be done by using Slave as a standby data copy. Because of supports the master-slave structure, you don’t need any additional failover handling, it has built-in failover of the master;

Redis Cluster Cons:
- You need at least 6 nodes — 3 master and slave
- Not entirely highly available, the cluster will stop down if the majority of masters are unavailable in the case of a bigger failure.
- Data is replicated asynchronously, and there is no guarantee of data consistency.
- Because the replication structure only allows one layer, the slave node can only duplicate the master node.
- Because data is sharded among the masters, clients should have network access to all nodes in a Redis Cluster. If a client wants to write data to Master1 but the data belongs to Master2, Master1 will give the client a MOVE message, directing it to forward the request to Master2.
- Not every library supports; the lack of client library implementations in Redis Cluster.
- It is not possible to ensure a strong consistency. In practice, this means that Redis Cluster may lose writes that were acknowledged by the system to the client under certain circumstances.
- Because of data partitioning, data handling becomes more complicated; for example, you should handle multiple RDB / AOF files, and you need to aggregate the persistence files from multiple instances and hosts to generate a backup of your data. It is not possible to manage backups from a single location.


Redis sentinel.conf Dosyası

monitor Alanı
Şöyle yaparız
sentinel monitor redis-cluster 172.18.0.2 6379 2
Açıklaması şöyle
Sentinels only need to look at primary nodes to decide on failover.
down-after-milliseconds Alanı
Açıklaması şöyle
We can use below config to decide on how long before a cluster node is considered down.
Şöyle yaparız
sentinel down-after-milliseconds redis-cluster 5000
failover-timeout Alanı
Açıklaması şöyle
We can add below to allow timeout for current replication writes to complete before a failover kick-off:
Şöyle yaparız
sentinel failover-timeout redis-cluster 10000