/var/www/yatta47.log

/var/www/yatta47.log

やったのログ置場です。スクラップみたいな短編が多いかと。

Kafkaのメッセージサイズ上限を上げたのに通らない原因 — 3箇所の制限と設定方法

Kafkaのメッセージサイズ上限を上げたのに通らない原因 — 3箇所の制限と設定方法

Kafkaで大きいメッセージを送ろうとして MessageSizeTooLargeException が出たとき、設定を上げる場所はProducer・Broker・Topicの3箇所あります。どれか1つでも漏れると通りません。

何を調べたか

Kafkaでメッセージサイズの上限を上げたのに、まだエラーで弾かれるという状況に遭遇したんですよね。Producerの max.request.size を上げたのに MessageSizeTooLargeException が消えない。調べてみたら、メッセージサイズの制限って3箇所に分かれていて、それぞれ独立して判定しているということがわかりました。

どういうことか — 3箇所の制限

メッセージがProducerからBrokerに届いて、Topicに格納されるまでに、3つのゲートを通ります。

flowchart LR
    P["Producer<br/>max.request.size<br/>デフォルト: ≒1MB"] -->|送信| B["Broker<br/>message.max.bytes<br/>デフォルト: ≒1MB"]
    B -->|書き込み| T["Topic<br/>max.message.bytes<br/>デフォルト: Broker値を継承"]

それぞれの役割はこうなっています。

ゲート 設定名 役割 デフォルト
Producer max.request.size 送信リクエストの最大サイズ。超えるとProducer側でエラー ≒1MB
Broker message.max.bytes 受け付けるメッセージの最大サイズ。超えると拒否 ≒1MB
Topic max.message.bytes Topic単位の上限。Brokerを通ってもここで引っかかる Broker値を継承

これ、3つが互いに参照し合っていないんですよね。Producerが「Brokerの設定は何MBだから送信を止めよう」みたいな判断はしない。

各ゲートが自分の前を通るメッセージのサイズだけを見ています。

なぜこうなっているかというと、責任のレイヤーが違うからです。

flowchart TD
    A[アプリケーション層] -->|"max.request.size"| G1[送信側のガードレール]
    B[インフラ層] -->|"message.max.bytes"| G2[クラスタのリソース保護]
    C[用途別] -->|"max.message.bytes"| G3["Topic単位の制御<br/>(ログ用は小さく、画像用は大きく)"]

分散システムとして各コンポーネントが自律的に判断する設計なので、こうなるのは理にかなっています。

設定方法

大きいメッセージを通したい場合、3箇所(+Consumer)を揃えて変更する必要があります。

# Producer
max.request.size=10485760          # 10MB

# Broker(server.properties)
message.max.bytes=10485760         # 10MB

# Topic(topic設定)
max.message.bytes=10485760         # 10MB

# Consumer
max.partition.fetch.bytes=10485760 # 10MB

Topicの設定変更はこうです。

# 既存Topicの設定変更
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics \
  --entity-name my-topic \
  --alter \
  --add-config max.message.bytes=10485760

Consumer側の max.partition.fetch.bytes は厳密にはメッセージサイズ制限ではなく、1回のfetchで取得するデータの上限です。Kafkaはprogressを保証するため、この値を超える最初のレコードバッチは例外的に返却されるので、読み出し自体は失敗しません。

ただし、fetch効率が落ちてパフォーマンスに影響する可能性があるので、大きいメッセージを扱うなら揃えておくのが安全です。

注意点

ハマりやすいパターンが3つあります。

1つ目は、Producerだけ上げたケース。max.request.size を10MBにしても、Brokerの message.max.bytes がデフォルトの1MBのままだと、送信はできるけどBrokerで拒否されます。

2つ目は、BrokerとProducerは上げたけどTopicを忘れたケース。Topic作成時にデフォルト以外の max.message.bytes が設定されていると、そこで引っかかります。

Topic側の設定は必ずしもBrokerのデフォルトを継承するとは限らないので注意が必要です。

3つ目は、Producer・Broker・Topicは全部上げたけどConsumer側を忘れたケース。max.partition.fetch.bytes(デフォルト≒1MB)が小さいままだと、メッセージの保存はできていてもfetch効率が落ちてスループットが低下する可能性があります。

読み出し自体は失敗しませんが、パフォーマンスに影響します。

まとめ

Kafkaのメッセージサイズ制限は3つの独立したゲートで構成されています。1箇所だけ変更して「通った」と思っていると、別のTopicや別のConsumerで地雷を踏むことになります。

変更するときは「この設定を参照しているコンポーネントは全部どれか」を先に洗い出してから揃えて変更するのが安全です。

参考