Kafka Acks再入門

このブログエントリはKafkaコミッタである Stanislav Kozlovski(𝕏|Ln) のサイトで2022/11/06に公開されたKafka Acks Explainedの日本語訳です。Stanislav本人の了承を得て翻訳/公開しています。
Kafkaに関する仕事を始めて4年になりますが、経験上未だに2つの設定について広く誤解されていると感じる事があります。それはacksとmin.insync.replicasであり、さらにはこの2つの設定がどう影響し合うかについてです。このエントリはこの非常に重要な2つの誤解を解き、適切に理解してもらう事を目的としています。
Replication
この2つの設定を理解するためにはまずKafka Replicationプロトコルについて少しおさらいする必要があります。
このブログの読者の皆さんはある程度Kafkaについてご存知だと想定しています - もし自信がない場合はぜひThorough Introduction to Apache Kafkaもご参照ください。
各Partitionには1つのLeader Broker(1)と複数のFollower Broker(N)がアサインされます。この複製の数はreplication.factorで設定する事ができ(1+N)つまり総数を表します。つまりこの設定では「対象となるPartitionに対してクラスタ上で何個の複製が出来るか」を指定します。
デフォルトであり通常推奨する設定値は3です。
In-Sync Replicas
in-sync replica(ISR)は対象Partitionの最新状態と同期が取れているBrokerを指します。当然Leaderは常にISRとなり、Followerの場合はLeaderの更新に追い付き同期が取れた状態のもののみISRとなります。仮にFollowerがLeaderに追従できなくなった場合、そのFollowerはISRではなくなります。
ちなみに、厳密にはISRか否かという判断はもう少し複雑で、ここで説明されているようにすんなり「このFollowerは最新の状態か」と判断出来る訳ではありません。ただ厳密な話をし始めるとこのエントリの主旨から外れるので、ここでは上の図にある赤いBrokerは同期が取れていないと、見たまま捉えてください。
Acks
Acksはクライアント (Producer) 側の設定で、「どこまでFollowを含めて書き込みの確認が取れてからクライアントに返答するか」を指定するものです。有効な値は0、1、そしてallの3つです。
acks=0
0が設定された場合、クライアントはBrokerまで到達したかの確認さえ行いません - メッセージがKafka Brokerに対して送られたタイミングでackを返します。
acks=1
1が設定された場合、クライアント (Producer) はLeaderにまでメッセージが到達した時点で書き込みの完了と判断します。Leader Brokerはメッセージを受け取った時点でレスポンスを返します。
acks=all
allと設定された場合、クライアントは全てのISRにメッセージが到達した時点で書き込みの完了と判断します。この際Leader BrokerがKafka側の書き込み判定を行なっており、全てのISRへのメッセージ到達の上クライアントにレスポンスを返します。

acksの機能性
この通りacksはパフォーマンスとデータ欠損耐性のバランスを決める非常に有益な設定です。データ保全を優先するのであればacks=allの設定が適切です。1 一方レイテンシやスループットに関する要件が極めて高い場合には0に設定すれば最も効率が良くなりますが、同時にメッセージロスの可能性は高まります。
Minimum In-Sync Replicas
acks=allの設定に関して、もう一つ重要な要素があります。
例えばLeaderが全てのISRへの書き込み完了した上でレスポンスを返すとして、LeaderのみがISRだった場合、結果としてacks=1と振る舞いは同じとなるのでしょうか?
ここでmin.insync.replicasの設定が重要になります。
min.insync.replicasというBroker側の設定は、acks=allの際に「最低いくつのISRとなっているか (Leaderを含めて幾つのレプリカが最新状態か) を指定するものです。つまりLeaderは、acks=allのリクエストに対して指定されたISRに満たないまでは返答せず、またそれが何かしらの理由で達成できない場合にはエラーを返します。データ保全観点でのゲートキーバーの様な役割を果たします。

min.insync.replicas=2となっている場合には条件を満たす為この時点でレスポンスが返されます。

min.insync.replicasを下回るためLeaderからはエラーレスポンスが返る、つまり書き込みは失敗します。一方同じ状況であってもacksの設定が0もしくは1の場合には正常なレスポンスが返されます。
注意点
一般的にmin.insync.replicasは「Leaderがクライアントに返答する際に、どれだけレプリケーションが完了しているかを指定する」と解釈されていますが、これは誤りです。正確には「リクエストを処理する為に最低いくつのレプリカが存在するか」を指定する設定です。
6) を受け取った場合、Broker 2への同期が完了してもレスポンスは返しません。この場合、処理時にISRとなっているBroker 3への同期が完了して初めてレスポンスが返されます。
まとめ
図で説明したことによって理解が深まったのではないかと思います。
おさらいすると、acksとmin.insync.replicasはKafkaへの書き込みにおける欠損体制を指定する事ができます。
acks=0- 書き込みはクライアントがLeaderにメッセージを送った時点で成功とみなします。Leaderからのレスポンスを待つことはしません。acks=1- 書き込みはLeaderへの書き込みが完了した時点で成功とみなします。acks=all- 書き込みはISR全てへの書き込みが完了した時点で成功とみなします。ISRがmin.insync.replicasを下回る場合には処理されません。
その他情報
Kafkaは複雑な分散システムであり、学ばなければいけない事が多いのも事実です。Kafkaの他の重要な要素については以下も参考にしてください。
- Kafka consumer data-access semantics - クライアント (Consumer) における欠損耐性、可用性、データ整合性の確保に関わる詳細。
- Kafka controller - Broker間の連携がどの様になされるのかの詳細。特にレプリカが非同期 (Out-of-Sync) となるのはどういう条件下かについて説明しています。
- “99th Percentile Latency at Scale with Apache Kafka - Kafkaのパフォーマンスに関するConfluent Blogエントリ。
- Kafka Summit SF 2019 videos
- Confluent blog - Kafkaに関する様々なトピックを網羅。
- Kafka documentation
Kafkaは継続的かつアクティブに開発されていますが、機能追加や改善は活発なコミュニティにより支えられています。開発の最前線で何が起こっているか興味がある場合はぜひメーリングリスト に参加してください。

(訳者注)ほとんどのユースケースでは
acks=allが適切であり、Kafkaのデフォルトでもあります。 ↩︎