ข้ามไปยังเนื้อหา

อภิธานศัพท์

ศัพท์ของ Kafka ครอบคลุมหลายชั้นพร้อมกัน: ชั้น storage (log, segment, index, offset) ที่ทุกชั้นอื่นวางอยู่บนมัน, ชั้น coordination (KRaft, controller quorum, __cluster_metadata) ที่มาแทน ZooKeeper ตั้งแต่ Kafka 4.0, topic/partition/replication (leader, follower, ISR, high watermark) ที่ทำให้ log ทนทานและขยายได้, producer กับ consumer (acks, idempotence, transaction, consumer group, rebalancing) ที่อ่าน/เขียนมัน และชั้น processing/integration (share group, Kafka Streams, Connect, Schema Registry) ที่สร้างอยู่บนทุกอย่างข้างต้น เวลาอ่านบทเรียนแล้วเจอคำที่ไม่คุ้น หรือไม่แน่ใจว่ายังใช้ได้อยู่กับ Kafka 4.3.1 ไหม ให้กลับมาดูที่นี่ — ทุกคำนิยามด้านล่างมาจากบทเรียนที่พิสูจน์กับ broker จริงหรือ client จริง ไม่ใช่จากความจำ

Termความหมายบทเรียน
acksค่าที่ producer ตั้งว่าต้องมี replica กี่ตัวยืนยันการเขียนก่อนถือว่าสำเร็จ: acks=0 (ไม่รอเลย ได้ response ว่างกลับมาไม่มี offset — พิสูจน์แล้วว่าให้การยืนยันการส่งเป็นศูนย์ไม่ว่าผลจะเป็นอย่างไร), acks=1 (leader ตัวเดียว) หรือ acks=all/acks=-1 (ทุก in-sync replica ซึ่งเป็นค่า default จริงของ KafkaJS)Acks and Durability
ACL (Access Control List)กฎที่ผูก principal, operation (Read/Write/…) และ resource (topic, group, …) เข้าด้วยกัน ACL จะไม่ทำอะไรเลยจนกว่าจะตั้งค่า authorizer — kafka-acls.sh --list บน broker ที่ไม่มี authorizer จะโยน SecurityDisabledException จริง; พอเปิดแล้ว principal ที่ไม่มีสิทธิ์จะได้ TopicAuthorizationException จริงตอน produce/consumeSecurity and Production
acknowledgement mode (ACCEPT / RELEASE / REJECT)วิธีที่ share-group consumer จัดการ record ที่ตัวเองรับมา: ACCEPT (ค่า default) คือทำเสร็จแล้ว ไม่ถูกส่งซ้ำอีก; RELEASE (“ทำไม่เสร็จ ขอคืน”) จะเพิ่ม delivery count ทุกครั้งที่ release จนกว่าจะถึง group.share.delivery.count.limit (default 5); REJECT คือทำเครื่องหมายว่าเสียถาวร ถูกอ่านครั้งเดียวแล้วไม่ถูกส่งซ้ำอีกเลยChoosing a Processing Layer
apache/kafka vs apache/kafka-nativeApache มี image broker สองแบบ: apache/kafka (JVM มี CLI tool bin/*.sh ครบทุกตัว) กับ apache/kafka-native (คอมไพล์ด้วย GraalVM เร็วกว่าประมาณ 7 เท่าจนถึง log “Kafka Server started” จากการวัดจริงบน Mac เครื่องหนึ่ง แต่ /opt/kafka/ มีแค่ไฟล์ binary ที่คอมไพล์แล้ว — ไม่มี kafka-topics.sh ไม่มี CLI เลย)Running Kafka Locally
at-least-oncerecord ทุกตัวถูกส่งอย่างน้อยหนึ่งครั้ง ไม่มีวันหาย ทำได้โดย commit offset ของ consumer หลัง ประมวลผลเสร็จ — พิสูจน์ด้วยการ kill จริงกลางช่อง: record ถูก apply สองครั้งหลัง crash ระหว่างการเขียนจริงกับการ commitDelivery Semantics
at-most-oncerecord ทุกตัวถูกส่งศูนย์หรือหนึ่งครั้ง ไม่มีวันซ้ำ ทำได้โดย commit offset ก่อน ประมวลผล — พิสูจน์ด้วยการ kill จริง: รอดมา 3 จาก 4 record อีก 1 ตัวหายถาวรเพราะ offset ขยับผ่านมันไปแล้วDelivery Semantics
authorizer (StandardAuthorizer)component ของ broker ที่บังคับใช้ ACL จริง ๆ Kafka มาโดยไม่ตั้งอะไรไว้เป็น default authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer คือ authorizer แบบ KRaft-native ตัวจริง (AclAuthorizer ยุค ZooKeeper หายไปแล้ว) — super.users คือรายชื่อ principal ที่ข้ามการเช็ก ACL ทุกอันSecurity and Production
auto.create.topics.enableค่า default ของ broker คือ true: การ produce/consume ไปยัง topic ที่ยังไม่มีจะแข่งกับการสร้างจริง (UNKNOWN_TOPIC_OR_PARTITION ชั่วคราว แล้วสำเร็จ) ถ้าตั้งเป็น false (เป็น static config ต้อง restart) error เดิมจะเกิดซ้ำตลอดไปโดย topic ไม่ถูกสร้างขึ้นจริงBrokers, Topics & Partitions
auto.offset.reset / share.auto.offset.resetตำแหน่งที่ client เริ่มอ่านเมื่อไม่มี committed position subscribe({ fromBeginning }) ของ KafkaJS เลือกได้ระหว่าง earliest (true) หรือ latest (false, ค่า default) ส่วนของ share group คือ share.auto.offset.reset (default latest) ซึ่งเป็น broker config ระดับ group ตั้งด้วย kafka-configs.sh --entity-type groups — ส่ง property auto.offset.reset ของ client ปกติให้ share consumer จะได้ ConfigException จริงOffsets & Commits
Avro / Protobuf / JSON Schemaสามรูปแบบ serialization ที่ Schema Registry จัดการทั่วไป: Avro (binary กะทัดรัด เป็นตัวเลือกที่ใช้บ่อยที่สุด), Protobuf (schema language ของ Google, เครื่องมือข้ามภาษาแข็งแรง) และ JSON Schema (อ่านง่ายด้วยตาคน) ที่ไม่ใช่: รูปแบบดิบไม่ประกาศ type อย่าง CSVSchema Registry
Termความหมายบทเรียน
batch.num.messagesตัวจำกัดขนาด batch เฉพาะของ librdkafka วัดเป็นจำนวน record แทนที่จะเป็น byte เปิดให้ใช้ผ่าน native surface ของ @confluentinc/kafka-javascript — ไม่มีชื่อเทียบเท่าใน KafkaJS หรือ Java client ตัวเปล่าBatching and Compression
bootstrap serverที่อยู่ broker ตัวแรกที่ client เชื่อมต่อ (--bootstrap-server หรือ brokers: [...] ใน client config) จากนั้น client จะค้นพบคลัสเตอร์ทั้งหมดและว่า broker ไหนเป็น leader ของแต่ละ partition Kafka สมัยใหม่ไม่มี --zookeeper เทียบเท่าอีกแล้ว — เครื่องมือที่ยังรับ flag นั้นได้คือของก่อนยุค KRaftBrokers, Topics & Partitions
brokerKafka server process หนึ่งตัว หลาย broker รวมกันเป็นคลัสเตอร์ แต่ละ broker เก็บข้อมูลบางส่วนและให้บริการอ่าน/เขียนสำหรับ partition ที่ตัวเองเป็น leaderWhat Is Kafka
buffer.memoryขนาด buffer สำหรับส่งข้อมูลฝั่ง producer ของ Java client เมื่อเต็ม send() จะ block (สูงสุด max.block.ms) เป็น back-pressure KafkaJS ไม่มีการตั้งค่าเทียบเท่า — มันไม่ buffer record ล่วงหน้าก่อน send() แบบที่ Java client ทำBatching and Compression
Termความหมายบทเรียน
changelog topictopic ของ Kafka ที่หนุนหลัง state store ของ Kafka Streams: ทุกการอัปเดต state store จะถูกเขียนลงไปด้วย ดังนั้น instance ที่ crash แล้ว restart ที่อื่นจะสร้าง state store ใน RocksDB ของตัวเองขึ้นใหม่ได้ด้วยการ replay changelogKafka Streams
cleanup.policyค่าระดับ topic ที่เลือก lifecycle ของ topic: delete (default — ลบ segment ตามอายุ/ขนาด), compact (เก็บแค่ค่าล่าสุดต่อ key) หรือ compact,delete (ทั้งคู่ — มีเพดานอายุแข็งบน snapshot ที่สร้างใหม่ได้ตาม key)Retention & Compaction
__cluster_metadatatopic ภายในที่ KRaft จัดการ (partition 0) ซึ่ง คือ metadata log ของคลัสเตอร์เลย — การสร้าง topic, การเลือก leader, การเปลี่ยน ISR ทุกอย่างคือ event ที่ถูก append เข้าไปและ replicate ข้าม controller quorum kafka-dump-log.sh --cluster-metadata-decoder อ่านมันได้ตรง ๆ เห็น record ชนิดจริงอย่าง TOPIC_RECORD, PARTITION_RECORD, FEATURE_LEVEL_RECORD และ REGISTER_BROKER_RECORDKRaft & the Cluster
cluster.idตัวตนของคลัสเตอร์ ถูกเขียนลง meta.properties ของทุก broker ตอน format ยืนยันบน image ของคอร์สนี้เอง: container apache/kafka ใหม่ที่ไม่ได้ตั้ง env CLUSTER_ID จะได้ค่า default ที่ฝังไว้ ค่าเดียวกัน ทุกครั้ง — kafka-storage.sh random-uuid รันสองครั้งได้ค่าต่างกันจริง พิสูจน์ว่า default ของ container (ไม่ใช่ตัวเครื่องมือ) ต่างหากที่ตายตัวRunning Kafka Locally
co-partitioningการให้ topic สองตัวมีจำนวน partition และ partitioner เหมือนกัน เพื่อให้ key ที่ใช้ร่วมกัน (เช่น orderId) ตกที่หมายเลข partition เดียวกันในทั้งสอง topic เสมอ — เงื่อนไขที่ Kafka Streams ต้องการสำหรับ join แบบอิง key เพื่อเห็นทั้งสองฝั่งของ join ในเครื่องเดียวกัน ไม่ต้อง shuffle ข้าม partitionKeys & Partitioning
combined controller / isolated controllernode แบบ combined รัน process.roles=broker,controller (ทำทั้งสองหน้าที่ในโปรเซสเดียว — รูปแบบที่ทุกการพิสูจน์ในโมดูล foundations ของคอร์สนี้ใช้) ส่วน controller แบบ isolated รัน process.roles=controller อย่างเดียว แลกโปรเซสที่ต้องรันเพิ่มกับการแยก blast-radius ไม่ให้ load ของ client แย่ง CPU/disk I/O ของ controller quorumKRaft & the Cluster
commit (offset commit)บันทึกของ consumer ว่า “ทุกอย่างก่อน offset นี้เสร็จแล้ว” Kafka เก็บมันเป็น record จริงที่ decode ได้ใน __consumer_offsets การ commit offset ของ record ตัวเอง แทนที่จะเป็น offset + 1 คือบั๊กที่พิสูจน์แล้วว่าเกิดซ้ำได้จริง: record ล่าสุดที่ประมวลผลไปจะถูกส่งซ้ำตอน restartOffsets & Commits
compactionกระบวนการของ cleaner thread ที่เก็บเฉพาะ record ล่าสุดต่อ key บน topic แบบ compact แล้วเก็บกวาดค่าเก่า ๆ ทิ้ง พิสูจน์ข้าม segment ด้วย kafka-dump-log.sh ก่อน/หลังจริง: ค่าที่ถูกแทนที่จะถูกเก็บกวาดก็ต่อเมื่อ cleaner รันจริง (คุมด้วย min.cleanable.dirty.ratio default 0.5) และ segment ที่ active จะไม่ถูก compact เลยไม่ว่าค่าของ key ในนั้นจะเก่าแค่ไหนRetention & Compaction
compatibility mode (BACKWARD / FORWARD / FULL)กฎที่ Schema Registry บังคับใช้ก่อนรับ schema version ใหม่ของ subject: BACKWARD (default — schema ใหม่อ่านข้อมูลเก่าได้ อัปเกรด consumer ก่อน), FORWARD (schema เก่าอ่านข้อมูลใหม่ได้ อัปเกรด producer ก่อน) หรือ FULL (สองทางพร้อมกัน) การลงทะเบียนที่ผิดกฎถูกปฏิเสธทันทีSchema Registry
@confluentinc/kafka-javascriptNode.js client ของ Confluent ที่ยัง maintain อยู่ สร้างบน librdkafka มีสอง surface จาก package เดียว: promise API แบบเข้ากันได้กับ KafkaJS (.KafkaJS.Kafka) และ native librdkafka callback API (Producer/KafkaConsumer) ที่ใช้ config key ดิบของ librdkafka รองรับ KIP-848 (group.protocol=consumer) มาตั้งแต่ librdkafka 2.12.0 (ต.ค. 2025)The Producer API
compression.typecodec ที่ใช้บีบอัด batch ก่อนส่ง: lz4, zstd, snappy, gzip หรือ none (default) KafkaJS มีให้ แค่ gzip แบบ native (ตรวจจาก source แล้ว) — Snappy/LZ4/ZSTD จะโยน KafkaJSNotImplemented เว้นแต่จะติดตั้งและลงทะเบียน codec package แยกต่างหาก วัดจริงบน JSON แบบสมจริง: gzip บีบ batch 500 record จาก 108,177 เหลือ 4,741 byte (22.8 เท่า); บน random byte ที่บีบไม่ได้ lz4 กลับ ช้ากว่า ไม่บีบเลยประมาณ 4%Batching and Compression
config source (DEFAULT_CONFIG / STATIC_BROKER_CONFIG / DYNAMIC_BROKER_CONFIG / DYNAMIC_TOPIC_CONFIG)tag ที่มาของ config จริงที่ kafka-configs.sh --describe --all พิมพ์ใน synonyms={...} พิสูจน์ด้วย chain สามชั้นจริง: DYNAMIC_TOPIC_CONFIG ของ topic เองชนะ DYNAMIC_BROKER_CONFIG ของ broker ซึ่งชนะ DEFAULT_CONFIG ที่ติดมากับตัวเครื่อง ค่าที่เป็น DEFAULT_CONFIG ไม่ได้แปลว่าจะ --alter แบบ dynamic ได้ — auto.create.topics.enable และ log.dirs ทั้งคู่ปฏิเสธ --alter ด้วย InvalidRequestException: Cannot update these configs dynamically จริงBrokers, Topics & Partitions
connector (source / sink)หน่วยของ Kafka Connect ที่ใช้ config อย่างเดียว ใช้ซ้ำได้: connector แบบ source ดึงจากระบบภายนอก (ฐานข้อมูล, ไฟล์) เข้า Kafka; connector แบบ sink ส่งจาก Kafka ไปยังระบบภายนอก (search index, warehouse) deploy ไปยัง distributed Connect cluster ผ่าน REST API (POST /connectors)Kafka Connect
__consumer_offsetstopic ภายในแบบ compacted ที่เก็บ offset ที่ commit ไว้ทุกอัน keyed ด้วย (group, topic, partition) อ่านตรง ๆ ได้ด้วย kafka-console-consumer.sh --formatter org.apache.kafka.tools.consumer.OffsetsMessageFormatter — ค่าที่ decode แล้วมี offset, leaderEpoch และ commitTimestampOffsets & Commits
consumerclient ที่อ่าน record จาก topic หนึ่งตัวหรือมากกว่า ในฐานะ consumer group โดยติดตามตำแหน่งของตัวเอง (offset) ต่อ partitionWhat Is Kafka
consumer groupกลุ่ม consumer ที่ใช้ groupId ร่วมกัน Kafka มอบแต่ละ partition ให้สมาชิกกลุ่มเพียงคนเดียวในเวลาหนึ่ง ๆ ดังนั้นจำนวน partition คือเพดานของ parallelism — consumer ตัวที่ 7 บน topic ที่มี 6 partition จะว่างสนิท กลุ่มต่างกันเป็นอิสระต่อกัน แต่ละกลุ่มติดตาม offset ของตัวเองตลอดทั้ง streamConsumer Groups & Rebalancing
controllerbroker ที่ทำหน้าที่เป็นส่วนหนึ่งของ KRaft metadata quorum คอย replicate metadata log ของคลัสเตอร์ ตัวหนึ่งจะถูกเลือกเป็น leader (active controller) นับตั้งแต่ Kafka 4.0 เอา ZooKeeper ออก quorum นี้เอง — ไม่ใช่ระบบภายนอก — คือที่ที่รูปทรงของคลัสเตอร์อาศัยอยู่KRaft & the Cluster
controller quorumกลุ่ม voter ของ controller ที่ replicate กันด้วย Raft และตกลงกันเรื่อง metadata log kafka-metadata-quorum.sh describe --status รายงาน LeaderId, CurrentVoters และ MaxFollowerLagTimeMs จริง — พิสูจน์แล้วทั้งบน quorum node เดียว (quorum ที่มีแค่หนึ่งเสียง) และ quorum 3 node จริงที่มี Leader หนึ่งตัวกับ Follower สองตัวKRaft & the Cluster
controller.quorum.voters / controller.quorum.bootstrap.serverscontroller.quorum.voters คือรายชื่อ voter แบบ static ดั้งเดิม (1@broker1:9093,2@broker2:9093,...) ตายตัวตั้งแต่ start — เปลี่ยนสมาชิกต้องแก้ config แล้ว restart controller ทุกตัว controller.quorum.bootstrap.servers คือทางเลือกแบบ dynamic ของ KIP-853 ยืนยันแล้วว่าเป็น config key จริงบน Kafka 4.3.1 พร้อม subcommand add-controller/remove-controller ที่ใช้ได้จริงKRaft & the Cluster
convertercomponent ของ Kafka Connect ที่แปลระหว่าง record format ภายในของ Connect กับ byte บน topic — AvroConverter serialize ด้วย schema id ของ Schema Registry; JsonConverter เขียนเป็น JSON ธรรมดา นี่คือจุดที่ Schema Registry เสียบเข้ากับ pipeline ของ ConnectKafka Connect
Termความหมายบทเรียน
dead-letter topic (DLQ)topic ที่ record มีพิษถูก produce แยกไว้ก่อนที่ offset ของมันจะถูก commit ผ่านไป พิสูจน์ว่าลำดับมีผลจริงด้วยการ kill จริง: commit ก่อนแล้วค่อย produce ไป DLQ ทำให้ record หายถาวรถ้า crash ในช่องว่างนั้น (หาไม่เจอที่ไหนเลย); produce ไป DLQ ก่อนแล้วรอ ack ก่อน commit เสี่ยงแค่ DLQ entry ซ้ำที่ไม่เป็นอันตรายในจุด crash เดียวกันError Handling & Retries
delete.retention.mstombstone (record ที่มี valueSize: -1 ซึ่งเป็นเครื่องหมายจริงบนดิสก์ว่า key นี้ถูกลบ) จะอยู่ได้นานแค่ไหนหลัง compaction เจอมันครั้งแรก — พิสูจน์ตรง ๆ ใน output ของ kafka-dump-log.sh: tombstone อยู่นานกว่าค่าที่มันแทนที่ เพื่อให้ consumer ที่ตามหลังยังเห็นการลบก่อนที่ marker เองจะหายไปRetention & Compaction
delivery.timeout.msเส้นตาย wall-clock เดี่ยวของ Java/librdkafka client (default 120000) ครอบคลุมวงจรการส่งทั้งหมด — batching, network และทุก retry — ตั้งแต่ send() จนถึงได้รับการยืนยัน KafkaJS ไม่มีเส้นตายเดี่ยวเทียบเท่า มันจำกัด retry ด้วยจำนวนครั้งและ backoff แทน (retries, initialRetryTime, maxRetryTime) ซึ่งได้แค่ประมาณงบเวลารวมเท่านั้นBatching and Compression
distributed mode (Connect)worker หลายตัวของ Connect ประสานงานกันเหมือน consumer group โดยเก็บ config ของ connector และ offset ไว้ใน topic ของ Kafka — worker ตายไปงานของมัน (task) จะ rebalance ไปยังตัวที่รอดอยู่ นี่คือโหมด production จริง บริหารผ่าน REST API; โหมด standalone คือ worker เดียว config ไฟล์ local เหมาะกับ demo ครั้งเดียวKafka Connect
Termความหมายบทเรียน
eachBatchhandler ของ KafkaJS ที่รับ batch ทั้งก้อนในครั้งเดียว และ ฟังก์ชัน heartbeat ที่ต้องเรียกเองสำหรับงานที่ช้าจริง ๆ — พิสูจน์สด ๆ: งาน eachBatch 8 วินาที (นานกว่า sessionTimeout 6 วินาที) ไม่ทำให้เกิด rejoin เลยเมื่อมีการเรียก heartbeat() เองกลางทางThe Consumer API
eachMessagehandler การอ่านที่ง่ายที่สุดของ KafkaJS ทีละ record heartbeat ถูกส่งแบบ synchronous ทันทีหลัง eachMessage return ในรอบ loop เดียวกัน — ไม่ใช่ timer แยก — ดังนั้น handler ที่ block นานเกิน sessionTimeout จะทำให้ heartbeat ของตัวเองอดตายและเกิด rebalance จริง (The coordinator is not aware of this member, re-joining the group)The Consumer API
errors.deadletterqueue.* / errors.toleranceconfig ของ Connect sink connector ที่ route record ที่ Connect ประมวลผลไม่ได้ไปยัง dead-letter topic จริง แทนที่จะฆ่า task ทิ้ง errors.tolerance มี default เป็น none — ยืนยันสด ๆ กับ endpoint /config/validate ของ worker ที่รันจริง — ภายใต้ค่านี้ record ที่เสียจะฆ่า task ทิ้งตรง ๆ ไม่ว่า errors.deadletterqueue.topic.name จะตั้งเป็นอะไรก็ตาม มีแค่ errors.tolerance=all เท่านั้นที่เปิดใช้ DLQ มีความหมายแค่กับ connector แบบ sink เท่านั้น — ตั้งบน source connector validate ผ่านแต่ไม่ทำอะไรเลยKafka Connect
exactly-once (EOS)record ทุกตัวมีผลต่อผลลัพธ์แค่ครั้งเดียว — ไม่หาย ไม่ซ้ำ — ทำได้ใน consume-transform-produce loop โดยเอา input offset ของ consumer ใส่ ใน transaction ของ producer ผ่าน sendOffsets พิสูจน์ end-to-end ด้วย SIGKILL กลาง transaction จริง (2 จาก 5 sends buffer ไว้ ยังไม่ commit เลย): การ restart fence transaction ที่ค้างอยู่แล้วประมวลผล batch ใหม่ทั้งก้อน จบที่ output record 5 ตัวไม่ซ้ำกันสำหรับ input 5 ตัวExactly-Once Processing
exactly_once_v2ค่า processing.guarantee ของ Kafka Streams ที่ห่อวงจร read-process-write แต่ละรอบ — รวมถึง changelog ของ state store และ consumer offset — ไว้ใน Kafka transaction เดียว เพื่อไม่ให้ retry apply การอัปเดตซ้ำสองKafka Streams
Termความหมายบทเรียน
fencingกลไกที่ transactionalId มีไว้เพื่อสิ่งนี้: producer ตัวที่สองที่เชื่อมต่อด้วย id เดียวกันจะเพิ่ม epoch ของ transaction บน broker และ commit() ครั้งถัดไปของตัวแรก (zombie) จะพัง ด้วย KafkaJSProtocolError จริง (“Producer attempted an operation with an old epoch…”, retriable: false) — send() ก่อนหน้านั้นสำเร็จไปแล้วในเครื่อง (buffer ไว้) มีแค่ commit ที่ถูกปฏิเสธTransactions
FEATURE_LEVEL_RECORD / metadata.versionrecord ชนิดหนึ่งใน __cluster_metadata ที่ตรึงระดับเวอร์ชันของ feature หนึ่ง ๆ (เช่น metadata.version เอง, featureLevel: 30 บน broker ของคอร์สนี้) อ่านสด ๆ ด้วย kafka-dump-log.sh --cluster-metadata-decoder kafka-features.sh describe รายงานข้อมูล feature-level แบบเดียวกันสำหรับคลัสเตอร์ที่กำลังรันอยู่ (เช่น share.version SupportedMinVersion: 0 SupportedMaxVersion: 1 FinalizedVersionLevel: 1)KRaft & the Cluster
followerreplica ของ partition ที่ไม่ทำอะไรนอกจาก fetch จาก leader ต่อเนื่องเพื่อให้เหมือนกัน ถ้า broker ของ leader ตาย follower ที่อยู่ใน ISR อยู่แล้วจะถูกเลื่อนขึ้น — พิสูจน์สด ๆ ด้วย kafka-topics.sh --describe จริงที่เห็น Leader: 3 กลายเป็น Leader: 1 หลัง broker 3 ถูกหยุด ไม่มีคำสั่ง failover เองReplication & ISR
Termความหมายบทเรียน
group coordinatorcomponent ฝั่ง broker ที่สมาชิกของ consumer group ลงทะเบียนด้วยและ rebalance ผ่าน ภายใต้ consumer protocol ของ KIP-848 มันยังคำนวณ assignment เอง (แบบ diff ไม่ใช่คำนวณใหม่ทั้งหมด) แทนที่จะปล่อยให้ group leader ที่ client เลือกทำแบบ classic protocolConsumer Groups & Rebalancing
group.protocolตัวเลือกฝั่ง client ว่าจะพูด rebalance protocol ไหน: classic (ตัวเดียวที่ KafkaJS มี — มันมี assignor แค่ตัวเดียวคือ round-robin) หรือ consumer (KIP-848 broker-driven, incremental — เป็น config key ระดับบนสุดของ librdkafka บน @confluentinc/kafka-javascript ห้าม ซ้อนใต้ kafkaJS: {} ทำแบบนั้นจะได้ KafkaJSError: The 'groupProtocol' property is not supported จริง) ค่า default ฝั่ง client คือ classic แม้ broker จะเปิดทุก protocol ไว้เป็น default ก็ตามConsumer Groups & Rebalancing
group.share.delivery.count.limitค่า default ทั้งคลัสเตอร์ (5) ที่จำกัดว่า share group จะส่ง record ที่ถูก RELEASE ซ้ำได้กี่ครั้งก่อนจะกลายเป็นสถานะสุดท้าย — พิสูจน์สด ๆ: record หนึ่งตัวถูก release ซ้ำ ๆ เห็น Delivery:1 ถึง Delivery:5 แล้วหยุดถูกส่งซ้ำไปเลยChoosing a Processing Layer
Termความหมายบทเรียน
health metrics (UnderReplicatedPartitions / OfflinePartitionsCount / ActiveControllerCount)metric JMX ของ broker ที่ต่อรองไม่ได้: under-replicated partitions กับ offline partitions ต้องเป็นศูนย์ทั้งคู่ และต้องมี active controller เดียวทั้งคลัสเตอร์ — เบี่ยงเบนไปจากนี้คือสัญญาณ alert ทันที ไม่ใช่แค่ตัวเลขบน dashboardMonitoring & Consumer Lag
heartbeatสัญญาณที่สมาชิก consumer group ส่งเพื่อบอกว่ายังอยู่ในกลุ่ม ใน classic protocol ของ KafkaJS มันเป็นฝั่ง client ส่งทันทีหลัง eachMessage/eachBatch return — ไม่ใช่ timer พื้นหลัง ภายใต้ KIP-848 จังหวะนี้ย้ายไปเป็น broker config (group.consumer.heartbeat.interval.ms default 5000ms เทียบกับ default ฝั่ง client ของ classic ที่ 3000ms)The Consumer API
high watermark (HW)offset ที่ ทุก in-sync replica replicate มาถึงแล้ว — เส้นแบ่งจริงระหว่าง “เขียนแล้ว” กับ “commit แล้ว”: consumer ที่ isolation level default จะไม่มีวันอ่านเลยเส้นนี้ไป และ acks=all แท้จริงคือสัญญาว่า HW จะขยับผ่าน record ของคุณAcks and Durability
Termความหมายบทเรียน
idempotent producerproducer ที่ประทับ producer id (PID), epoch และ sequence number ต่อ partition บนทุก record เพื่อให้ broker ทิ้ง duplicate จาก retry ได้ — exactly-once ต่อ partition ค่า default ใน Java client เป็น true มาตั้งแต่ Kafka 3.0; KafkaJS default ไปทางตรงข้าม (idempotent: false) และต้อง opt-in เองThe Idempotent Producer
index (offset index)ไฟล์ .index ที่ map offset → ตำแหน่ง byte ในไฟล์ .log ของ segment ใช้สำหรับ seek เร็ว พิสูจน์แล้วว่า sparse จริง: 8 record กับ index.interval.bytes=4096 (default จริง) ได้ entry เดียวใน index ไม่ใช่แปดอัน การอ่านจะ binary-search index ที่ sparse หาตำแหน่งใกล้ที่สุดก่อนเป้าหมาย แล้วสแกนต่อไปข้างหน้าเป็นระยะสั้น ๆ ที่มีขอบเขตThe Log Abstraction
index.interval.bytesค่า config (default 4096) ที่คุมว่าต้อง append ข้อมูล log กี่ byte ก่อนที่ไฟล์ sparse .index/.timeindex จะได้ entry ใหม่ ตั้งสูงเกินไป (พิสูจน์ด้วยตัวอย่าง SpotTheBug 100 MiB บน segment.bytes ที่ 1 GiB) จะบังคับให้สแกนยาวตอน lookup — ตรงข้ามกับเจตนา “ลด overhead” ที่ตั้งใจตอนขยายค่านี้The Log Abstraction
ISR (in-sync replica set)ชุด replica ที่ตามทัน leader ณ ตอนนี้เลย พร้อมเป็น leader ได้โดยไม่เสียข้อมูล — พิสูจน์แล้วว่าเป็นแบบสด ไม่ใช่ static: มันหดตัวภายใน replica.lag.time.max.ms (default 30000ms) เมื่อ broker เงียบไป แล้วขยายกลับเมื่อ join คืน acks=all รอสิ่งที่อยู่ใน ISR ณ ตอนนั้น ซึ่งอาจเล็กกว่า replication factor เดิมของ topic ก็ได้Replication & ISR
Termความหมายบทเรียน
JMXinterface ที่ broker ของ Kafka เผยแพร่ metric หลายร้อยตัวผ่าน ถูก scrape โดยเครื่องมืออย่าง Prometheus JMX exporter หรือ APM healthcheck ที่ใช้ CLI tool ของ broker เองซ้ำพร้อมกับ JMX อาจพังได้: kafka-broker-api-versions.sh เปิด JVM สั้น ๆ ของตัวเองซึ่งสืบทอด KAFKA_JMX_OPTS แล้วพยายาม bind port เดียวกับที่ broker ที่กำลังรันอยู่ถืออยู่แล้ว — พิสูจน์ด้วย ExportException: Port already in use จริงMonitoring & Consumer Lag
KafkaJSNode.js Kafka client ที่ถูก deploy กว้างขวางที่สุด (คอร์สนี้ pin ไว้ที่ 2.2.4) — และ ไม่มีคน maintain ตั้งแต่ 27 ก.พ. 2023 (ยืนยันด้วย npm view kafkajs time.modified; ผู้ดูแลเปิด issue “Looking for maintainers” บน GitHub) มันยังรันได้ดีกับ Kafka 4.3.1 แต่ไม่มี initTransactions() แยก ให้ gzip แบบ native แค่ตัวเดียว ไม่มี linger.ms/batch.size และไม่เคยได้ KIP-848 หรือ share-group เลยThe Producer API
key (message key)field ที่ไม่บังคับบน record ที่เลือก partition ของมัน: partitioner default จะ hash key เพื่อให้ key เดียวกันตกที่ partition เดียวกันเสมอ ทำให้ได้ ordering ต่อ entity พิสูจน์แล้วว่าตรงกันทุก bit ระหว่าง DefaultPartitioner ของ KafkaJS กับ kafka-console-producer.sh ของ Java จริงสำหรับ key เดียวกันKeys & Partitioning
KIP-848consumer rebalance protocol รุ่นถัดไปที่ broker เป็นคนขับเคลื่อน GA ตั้งแต่ Kafka 4.0 ย้าย assignment ไปที่ group coordinator และทำให้ rebalance เป็นแบบ incremental — พิสูจน์สด ๆ: ภายใต้ classic การเปลี่ยนสมาชิกทำให้ ทุก สมาชิกหยุดชะงัก (แม้แต่คนที่ assignment สุดท้ายไม่เปลี่ยนเลย); ภายใต้ group.protocol=consumer partition ที่ไม่เกี่ยวข้องยังอ่านต่อเนื่องไม่สะดุด มีแค่ partition ที่ย้ายเท่านั้นที่หยุด librdkafka รองรับ GA ตั้งแต่ v2.12.0 (ต.ค. 2025); KafkaJS ไม่เคย implement เลยConsumer Groups & Rebalancing
KIP-890”Transactions Server-Side Defense” สรุปแบบ finalize ใน Kafka 4.x — ทำให้ transactional protocol แข็งแรงขึ้นต่อกรณีขอบอย่าง transaction ที่ค้างและ producer แบบ zombie เปิดใช้อัตโนมัติทันทีที่การอัปเกรด 4.0 finalize โดยไม่ต้องแก้โค้ดฝั่ง client เลยTransactions
KIP-932 (Share Groups / Queues for Kafka)ข้อเสนอที่นำ share group มาให้ — ความหมายแบบ queue บน topic ของ Kafka ตรง ๆ ไม่ต้องมี broker แยก ยืนยันแล้วว่า GA ใน Kafka 4.2.0 จากประกาศ release และรันสดจริงบน broker 4.3.1 ของคอร์สนี้ (kafka-features.sh describe แสดง share.version FinalizedVersionLevel: 1 เปิดเป็น default) ส่วน release 4.3 เพิ่ม acknowledgement type RENEW และ adaptive share-coordinator batching เข้าไปบนฐาน GA นั้นChoosing a Processing Layer
KIP-1071 (Streams Rebalance Protocol)protocol rebalance ฝั่ง broker ตัวใหม่ group.protocol=streams สำหรับ Kafka Streams — ตามประกาศ release 4.2.0 GA “with a limited feature set”: มีแค่ sticky assignment (ไม่มี warm-up task, ไม่มี rack-aware placement), ไม่มี static membership, ไม่มีการเปลี่ยน topology ระหว่างรันสด และการย้าย group ระหว่าง protocol classic กับตัวใหม่ทำได้แค่แบบ offline เท่านั้นKafka Streams
KRaftmetadata quorum ที่ Kafka สร้างเองบน Raft (KIP-500) เก็บ metadata ของคลัสเตอร์เป็น log ที่ replicate กัน แทนที่จะพึ่ง ZooKeeper ensemble ภายนอก ตั้งแต่ Kafka 4.0 เป็นต้นไป KRaft คือโหมดเดียว — ZooKeeper และ flag --zookeeper หายไปทั้งหมด ไม่ใช่แค่ deprecatedKRaft & the Cluster
KStreamมุมมอง Kafka Streams ของ topic แบบลำดับ event ที่ไม่มีขอบเขต — ทุก record คือข้อเท็จจริงใหม่ ไม่มีอะไรถูกเขียนทับ KTable คืออีกครึ่งของ stream-table duality เดียวกัน: table คือภาพนิ่งที่ได้จากการ replay stream ของการอัปเดต และ toStream() แปลง table กลับเป็น stream ของการเปลี่ยนแปลงของมันKafka Streams
KTableมุมมอง Kafka Streams ของ topic ในฐานะ changelog ที่ตีความเป็น ค่าล่าสุดต่อ key — แต่ละ record คือการ upsert แถวของ key นั้น สร้างจาก stateful operation อย่าง count()/aggregate() และหนุนหลังด้วย state store ที่ changelog topic ทำให้มันทนทานข้าม crash ได้Kafka Streams
Termความหมายบทเรียน
lag (consumer lag)log-end-offset − committed offset ของ partition หนึ่ง ๆ — สัญญาณสุขภาพระดับแอปที่สำคัญที่สุดตัวเดียว อ่านได้ด้วย kafka-consumer-groups.sh --describe --group ดูที่ แนวโน้ม: คงที่หรือลดลงคือสุขภาพดี ไต่ขึ้นเรื่อย ๆ คือสัญญาณแรกที่บอกว่า consumer group ตามไม่ทันMonitoring & Consumer Lag
leaderreplica ตัวเดียวที่จัดการอ่าน/เขียนทั้งหมดของ partition ณ เวลาใดเวลาหนึ่ง ตัวอื่น (follower) แค่ fetch จากมัน ความเป็น leader กระจายแบบ round-robin ข้าม partition เพื่อไม่ให้ broker ตัวเดียวเป็น leader ของทุกอย่างบนคลัสเตอร์ที่สุขภาพดีReplication & ISR
leader epoch / leader-epoch-checkpointleader epoch คือตัวนับที่เพิ่มขึ้นเรื่อย ๆ บอกว่า leader ไหนคุมอยู่ ณ offset ไหน leader-epoch-checkpoint คือไฟล์บนดิสก์จริงที่บันทึกมัน ใช้ตรวจจับและตัด (truncate) ข้อมูลที่เบี่ยงเบนหลังจากมี unclean leader change — หนึ่งในห้าไฟล์จริงในทุก segment directory คู่กับ .log, .index, .timeindex และ partition.metadataThe Log Abstraction
LEO (log-end-offset)ตำแหน่งของ record ล่าสุดใน partition — “ตัวตั้ง” ในสูตร consumer lag (lag = log-end-offset − committed offset) และเป็น field เดียวกับที่ kafka-metadata-quorum.sh describe --replication รายงานต่อ voter สำหรับ Raft log ของ controller quorum เอง (LogEndOffset)Monitoring & Consumer Lag
linger.ms / batch.sizeปุ่มปรับ batching ของ Java/librdkafka: linger.ms (default 0) คือ producer รอนานแค่ไหนเพื่อรวบรวม record เพิ่มก่อนส่ง batch ที่ยังไม่เต็ม batch.size (byte) จำกัดว่า batch นั้นโตได้แค่ไหน KafkaJS ไม่มีทั้งคู่ (ตรวจจาก source/types แล้ว) — มัน batch แบบซ่อนเร้น อะไรก็ตามที่ส่งเข้าไปใน send() ครั้งเดียว ไม่มี timer สะสมเลยBatching and Compression
listeners / advertised.listenerslisteners คือที่ที่ broker bind (เช่น PLAINTEXT://:9092) ส่วน advertised.listeners คือที่อยู่ที่มันส่งให้ client ใน metadata response ทุกครั้ง หลัง ครั้งแรก ตั้งค่าตัวหลังผิดจะได้การเชื่อมต่อ bootstrap ที่ทำงานแต่ตามด้วย getaddrinfo ENOTFOUND จริงในจังหวะถัดไป พิสูจน์สด ๆ แล้ว CONTROLLER คือ listener แบบที่สามที่ client ปกติไม่เคยต่อตรง — มีแต่ broker process ที่ทำหน้าที่ controller เท่านั้นที่ใช้Brokers, Topics & Partitions
log (append-only log)โครงสร้างหลักของ Kafka: ลำดับ record ที่เรียงลำดับและไม่เปลี่ยนแปลง ที่ record ใหม่ถูก append ที่ท้ายเสมอและได้ offset ตัวถัดไปเป็นจำนวนเต็ม ไม่เคยถูกเขียนทับในตำแหน่งเดิม — พิสูจน์แล้วว่าเป็นไฟล์จริงบนดิสก์ (segment .log) ที่เก็บ record batch ไม่ใช่แค่แนวคิดนามธรรมThe Log Abstraction
log.retention.check.interval.msช่วง polling ของ broker เองสำหรับการลบตาม retention (เวลา/ขนาด) — default 300000ms (5 นาที) ยืนยันทั้งจาก kafka-configs.sh --describe และจาก InvalidRequestException จริงที่พิสูจน์ว่าเปลี่ยนแบบ dynamic ไม่ได้โดยไม่ restartRetention & Compaction
log.segment.delete.delay.mssegment จะถูกลบออกจากดิสก์จริง ๆ นานแค่ไหนหลังพ้น retention — default 60000ms segment จะถูกเปลี่ยนชื่อเป็น .deleted ก่อน (เช่น 00000000000000000000.log.deleted) ยืนยันสดบน broker ของคอร์สนี้เองRetention & Compaction
Termความหมายบทเรียน
max.in.flight.requests.per.connectionidempotence ต้องการค่านี้ไม่เกิน 5 (ไม่ใช่ต้องเป็น 1 เป๊ะ) — broker เก็บ batch สูงสุดห้าตัวต่อ producer ไว้ track sequence ขณะยังรักษา order ได้ Java client บังคับเพดานนี้เอง; KafkaJS ไม่บังคับ (ตรวจจาก source แล้ว: idempotent: true มีผลแค่ค่า default ของ retry ไม่แตะ maxInFlightRequests เลย ซึ่งยังเป็น null/ไม่จำกัดจนกว่าจะตั้งเอง)The Idempotent Producer
metadata loglog ที่ replicate กันซึ่ง controller quorum ของ KRaft ดูแล — กลไกจริงที่อยู่เบื้องหลัง “รูปทรงของคลัสเตอร์ก็แค่ log อีกตัว”: ทุก broker ที่เข้าร่วมคลัสเตอร์จะ replay มันจาก offset 0 เพื่อสร้างมุมมองในหน่วยความจำของตัวเอง แบบเดียวกับที่ consumer replay data partitionKRaft & the Cluster
min.cleanable.dirty.ratioเกณฑ์ (default 0.5) ที่คุมว่า partition แบบ compacted พร้อมให้ cleaner thread รันแค่ไหน ตั้งต่ำ (เช่น 0.01) cleaner จะรันแทบต่อเนื่อง เหมาะกับ demo หรือ topic ที่ค่าซ้ำเก่า ๆ ต้องหายไวเป็นพิเศษ แลกกับ cleaner I/O ที่ทำงานตลอดRetention & Compaction
min.insync.replicasจำนวน in-sync replica ขั้นต่ำที่ต้องมีให้การเขียนแบบ acks=all ผ่านได้ พิสูจน์ด้วย ISR ที่หดตัวจริงบนคลัสเตอร์ 3-broker จริงว่าปฏิเสธการเขียนด้วย NotEnoughReplicasException จริงของ broker เอง (“Messages are rejected since there are fewer in-sync replicas than required”) — แต่พิสูจน์แล้วว่า ไม่ ปฏิเสธเลยเมื่อขาดแบบโครงสร้าง (ตั้ง min.insync.replicas สูงกว่า replication.factor เอง เช่นบน broker เดียว) กรณีนั้นจะเห็นแค่ใน --under-min-isr-partitions ไม่เคยเป็น error ตอนเขียนเลยAcks and Durability
murmur2 / murmur2_randomอัลกอริทึม hash ที่ partitioner default (เข้ากันได้กับ Java) ใช้กับ record ที่มี key: hash(key) % numberOfPartitions default จริงตามเอกสารของชั้น librdkafka ใน @confluentinc/kafka-javascript คือ consistent_random (อิง CRC32) — คลาส compat .KafkaJS.Kafka override เป็น murmur2_random เพื่อให้เข้ากันได้กับ Java/KafkaJS โดยเฉพาะ พิสูจน์ด้วยการเทียบสดสามแบบ: consistent (CRC32) ตกคนละ partition กับ murmur2_randomKeys & Partitioning
Termความหมายบทเรียน
num.partitionsจำนวน partition default ทั้งคลัสเตอร์ที่ใช้เมื่อ topic ถูกสร้างอัตโนมัติหรือสร้างโดยไม่ระบุ --partitions — default จริงคือ 1 บน broker ของคอร์สนี้ เห็นได้ใน tag ที่มา DEFAULT_CONFIG ของ kafka-configs.sh --describe --allBrokers, Topics & Partitions
offsetตำแหน่งของ record ภายใน partition — จำนวนเต็มที่เพิ่มขึ้นเรื่อย ๆ ที่ consumer ติดตามเพื่อรู้ว่าตัวเองอ่านไปถึงไหนแล้ว การอ่าน record ไม่เคยลบมันออก offset คือเคอร์เซอร์ที่ consumer เป็นเจ้าของ ไม่ใช่สิ่งที่ broker เปลี่ยนตอนส่งมอบWhat Is Kafka
Termความหมายบทเรียน
page cachecache ระดับ OS ที่ Kafka พึ่งพาเพื่อความเร็วในการอ่าน แทนที่จะมี cache ใหญ่ ๆ ในโปรเซสของตัวเอง RAM ว่างที่เหลือให้ page cache (ไม่ถูก JVM heap ขนาดใหญ่เกินไปกินไป) คือสิ่งที่ทำให้ kernel เสิร์ฟการอ่านล่าสุดจากหน่วยความจำได้Performance & Tuning
partitionหน่วยทั้งของ parallelism (partition อยู่คนละ broker กันได้ ขยาย throughput การอ่าน/เขียน) และของ ordering (Kafka รับประกัน order ภายใน partition เท่านั้น ไม่เคยข้าม topic ทั้งก้อน) — พิสูจน์สด ๆ: การเขียนแบบสลับกันจริงตามเวลาไปสอง partition ถูกอ่านออกมาเป็น “ทั้งหมดของ partition หนึ่งก่อน แล้วค่อยทั้งหมดของอีกตัว” ไม่มีความสัมพันธ์กับลำดับการเขียนข้าม partition เลยBrokers, Topics & Partitions
partitioner (DefaultPartitioner)component ของ producer ที่แปลง key ของ record เป็นหมายเลข partition, hash(key) % numberOfPartitions สำหรับ key ที่ไม่ใช่ null พิสูจน์จาก source ของ KafkaJS เอง: JavaCompatiblePartitioner เป็นชื่อ deprecated ของ DefaultPartitioner — implementation ของ murmur2 เปลี่ยนตอน KafkaJS 2.0.0 ทำให้แอปที่อัปเกรดโดยไม่ pin Partitioners.LegacyPartitioner โดน partition ของทุก key เดิมเปลี่ยนไปแบบเงียบ ๆKeys & Partitioning
partitionsConsumedConcurrentlyตัวเลือก run() ของ KafkaJS ที่ให้ eachMessage ของสูงสุด N partition รันพร้อมกันจริง ๆ — พิสูจน์สด ๆ: 3 partition แต่ละอันมี handler หน่วง 1 วินาที เสร็จรวมประมาณ 1 วินาที (ไม่ใช่ 3) ด้วย partitionsConsumedConcurrently: 3 เห็น timestamp เริ่มพร้อมกันเป๊ะทั้งสามตัวThe Consumer API
PID / producer epoch / sequence numberสาม field ที่ idempotent producer ประทับลงบนทุก record batch — เป็นข้อมูลจริงบนดิสก์ ยืนยันด้วย kafka-dump-log.sh (producerId: 0 producerEpoch: 0 baseSequence: 0 ...) ไม่ใช่แนวคิดนามธรรม broker ปฏิเสธ retry ที่มี sequence ที่บันทึกไว้แล้วสำหรับ (producerId, partition) นั้น แทนที่จะเทียบเนื้อหา payloadThe Idempotent Producer
poll loopconsume loop ภายในของ KafkaJS ขับเคลื่อนด้วย consumer.run() — cooperative และ single-threaded ต่อ partition slot: eachMessage/eachBatch ของ record N ต้อง return ก่อนที่ loop จะ heartbeat, commit หรือ fetch record N+1 บน slot นั้นได้ ไม่มีอะไรมาขัดจังหวะ handler ที่ช้าแบบ preemptive เลยThe Consumer API
producerclient ที่สร้างข้อความ (topic, key ที่ไม่บังคับ, value) แล้วเขียนไปยัง partition ผ่าน send() ซึ่ง return Promise และเสร็จแบบ asynchronous — ไม่เคย block จนกว่า record จะไปถึง broker จริง ๆWhat Is Kafka
Termความหมายบทเรียน
quotaเพดานต่อ client เรื่อง bandwidth หรืออัตรา request ที่กันไม่ให้ producer หรือ consumer ตัวใดตัวหนึ่งแย่ง capacity ของคลัสเตอร์ที่เหลือ — ส่วนหนึ่งของ checklist ความพร้อม production จริง คู่กับ ACL, TLS และ replicationSecurity and Production
read_committed / readUncommittedisolation level ของ consumer: read_committed (default ของ KafkaJS คือ readUncommitted: false) ไม่เคยเห็น record จาก transaction ที่ยังเปิดอยู่หรือถูก abort; readUncommitted: true เผยให้เห็นจริง — พิสูจน์สด ๆ ด้วยการ abort transaction ตั้งใจแล้วอ่านทั้งสองแบบ: consumer แบบ default ไม่เห็นอะไรเลย ส่วนตัวที่ readUncommitted: true เห็น record ผีนั้นTransactions
rebalance (classic vs KIP-848)การแจก partition ใหม่ในกลุ่ม consumer เมื่อสมาชิกเปลี่ยน ภายใต้ classic ทุกสมาชิกหยุดแล้ว rejoin ทุกครั้งที่มีการเปลี่ยน — พิสูจน์สด ๆ ว่ามีการหยุดชะงักจริง (แม้สั้น) แม้แต่สมาชิกที่ assignment สุดท้ายเหมือนเดิมเป๊ะ ภายใต้ consumer protocol ของ KIP-848 มีแค่ partition ที่ย้ายจริงเท่านั้นที่ถูก revoke สมาชิกที่ไม่เกี่ยวข้องยังอ่านต่อเนื่องตลอด rebalanceConsumer Groups & Rebalancing
record batch (v2 format)หน่วยจริงบนดิสก์ที่ kafka-dump-log.sh แสดงหนึ่งบรรทัดต่อ batch: header (baseOffset/lastOffset, producerId/producerEpoch/baseSequence, magic: 2 สำหรับ format v2, crc) ที่ทุกบรรทัด record `offset: …` ข้างในใช้ร่วมกัน
replicaสำเนาของ partition ที่เก็บอยู่บน broker — replica แบบ leader ตัวเดียวจัดการอ่าน/เขียนทั้งหมด ที่เหลือเป็น follower Replicas: ใน kafka-topics.sh --describe แสดงทุก broker ที่มีสำเนา ไม่ว่าจะตามทันอยู่หรือไม่ (ชุดย่อยที่ตามทันคือ ISR)Replication & ISR
replication factorจำนวนสำเนาที่ Kafka เก็บของ partition หนึ่ง ๆ — --replication-factor 3 หมายถึง partition อยู่บนสาม broker ค่า default ทั้งคลัสเตอร์ (default.replication.factor) ใช้กับ topic ที่สร้างอัตโนมัติ min.insync.replicas ที่สูงกว่า replication factor จริงคือกับดัก misconfiguration เงียบ ๆ ที่เกิดได้จริงReplication & ISR
RequestHandlerAvgIdlePercentJMX MBean kafka.server:type=KafkaRequestHandlerPool ที่วัดว่า thread pool ของ request-handler ของ broker ว่างแค่ไหน — ใกล้ 1.0 (พิสูจน์สด ๆ ที่ 0.97) แปลว่ายังมีกำลังเหลือเยอะ ยิ่งเข้าใกล้ 0 แปลว่า handler thread ทุกตัวทำงานตลอดเวลา และ latency ของ produce/fetch กำลังจะพุ่ง อ่านตรง ๆ ด้วย JmxTool คู่กับ UnderReplicatedPartitionsMonitoring & Consumer Lag
retention (retention.ms / retention.bytes)เพดานอายุ/ขนาดของ cleanup.policy=delete default: retention.ms (default 168 ชั่วโมง) และ retention.bytes (เพดาน 50 GiB คือ 53687091200 เป๊ะ ไม่ใช่ 53687456000 ซึ่งเป็นตัวเลขที่คลาดไป 364,800 byte ที่พบในโน้ตเก่าบางที่) บังคับใช้บน segment ทั้งก้อน ไม่ใช่ record เดี่ยว ๆ และ active segment ได้รับการยกเว้น — ขนาดจริงบนดิสก์ของ topic เกิน retention.bytes ได้สูงสุดหนึ่ง segment ณ เวลาใดก็ได้Retention & Compaction
Termความหมายบทเรียน
SASL/SCRAMกลไก authentication ที่ credential อยู่ใน __cluster_metadata log ของ KRaft ในคลัสเตอร์เอง — ไม่ต้องมี ZooKeeper ไม่ต้องมี credential store ภายนอก provision แบบ dynamic ด้วย kafka-configs.sh --alter --entity-type users --add-config 'SCRAM-SHA-256=[password=...]' มีแค่ metadata ของ credential อย่าง iterations=4096 เท่านั้นที่ถูกเก็บหรือแสดงกลับมา ไม่เคยเป็นรหัสผ่านตัวจริงSecurity and Production
schema id (wire format)จำนวนเต็มเล็ก ๆ ที่ serializer ได้กลับมาหลังลงทะเบียน schema ครั้งเดียว จากนั้น value ของทุก record จะถูกนำหน้าด้วย magic byte หนึ่งตัว, schema id 4 byte แล้วตามด้วย payload — consumer อ่าน id แล้วไปดึง (และ cache) schema ที่ตรงกันจาก registry ก่อน deserializeSchema Registry
Schema Registryสัญญาเวอร์ชันที่ทุกฝ่ายใช้ร่วมกันสำหรับรูปทรงของ record: producer ลงทะเบียน schema แล้วได้ schema id กลับมา; consumer ดึง schema ด้วย id นั้นเพื่อให้ทั้งสองฝั่งเห็นตรงกันเสมอว่าจะอ่าน byte ยังไง — เปลี่ยนสมมติฐานที่ไม่เป็นทางการเรื่องรูปทรง record ให้เป็นสัญญาที่บังคับใช้ได้จริงSchema Registry
segment (segment.bytes / segment.ms)หน่วยที่ log ของ partition ถูกแบ่งทางกายภาพ — ไฟล์สามตัว .log/.index/.timeindex ต่อหนึ่ง segment ตั้งชื่อตาม base offset ของมัน segment.bytes มีพื้น 1 MiB ที่บังคับจริง (ค่าที่เล็กกว่านี้ถูกปฏิเสธตอนสร้าง topic เลย) segment.ms บังคับให้ roll ตามเวลาไม่ว่าขนาดจะเท่าไหร่ และ segment จะลบหรือเช็ก retention ไม่ได้จนกว่าจะ roll (ปิด) ก่อนThe Log Abstraction
sendOffsetsคำสั่ง transaction ของ KafkaJS (txn.sendOffsets({ consumerGroupId, topics })) ที่ commit input offset ของ consumer เข้าไปใน transaction ของ producer ให้ atomic กับ output record การ commit offset แยกต่างหากจะเปิดช่องว่างขนาดเท่า crash ที่ transaction มีไว้ปิดกลับมาอีกครั้ง — พิสูจน์สด ๆ: input record 3 ตัวกลายเป็น output record 6 ตัวที่ซ้ำกันหลัง crash ระหว่างการ commit แยกกับการ commit ของ transactionTransactions
session.timeout.ms / heartbeat.interval.msภายใต้ protocol แบบ classic สองตัวนี้เป็นตัวเลือก consumer ฝั่ง client ของ KafkaJS (default 30000ms / 3000ms) — client ตั้งจังหวะ liveness ของตัวเอง ภายใต้ KIP-848 มันย้ายไปเป็น config ที่ broker คุม (group.consumer.session.timeout.ms default 45000ms, group.consumer.heartbeat.interval.ms default 5000ms) ยืนยันตรงกับ broker 4.3.1 ที่กำลังรันจริงConsumer Groups & Rebalancing
share groupconsumer group แบบ KIP-932 ที่สมาชิกหลายคนอ่าน partition เดียวกัน ได้พร้อมกัน แต่ละ record ถูกรับและ acknowledge ทีละตัว — ไม่มีการเป็นเจ้าของ partition-ต่อ-consumer ความหมายแบบ queue ของ RabbitMQ/SQS แบบ native บน topic ของ Kafka ตรง ๆ พิสูจน์สด ๆ ด้วย kafka-console-share-consumer.sh ครบทั้งสาม acknowledgement mode; ทั้ง KafkaJS และ @confluentinc/kafka-javascript ไม่มี share-group protocol เลย (ยืนยันด้วย grep ที่ไม่เจอเลยข้าม type definition ของทั้งสอง package) — วันนี้มีแค่ KafkaShareConsumer ของ Java client กับ CLI tool เท่านั้นที่ใช้งานได้Choosing a Processing Layer
SMT (Single Message Transform)การแก้ record แบบ inline ของ Kafka Connect ที่ไม่ต้อง deploy งานแยก (transforms=... เช่น InsertField$Value) — ไม่ต้องมี job แยกเลย พิสูจน์สด ๆ ว่า fail ด้วย DataException: Only Struct objects supported for [field insertion] จริงกับ value แบบ plain-string ของ FileStreamSourceConnector: SMT อย่าง InsertField/ReplaceField ต้องการ record ที่มี Struct schema (ปกติมาจาก JDBC, Debezium, หรือ source แบบ Avro/JSON-Schema) ไม่ใช่ value แบบ schemaless หรือ plain-stringKafka Connect
sparse indexดีไซน์ของไฟล์ .index/.timeindex ของ Kafka: หนึ่ง entry ต่อ index.interval.bytes ของข้อมูล log ไม่ใช่ หนึ่ง entry ต่อ record — พิสูจน์ตรง ๆ: 8 record ได้ index entry เดียวเป๊ะ ทำให้ index เล็กพอ binary-search ได้ถูก ขณะที่การสแกนต่อไปข้างหน้ายังมีขอบเขตจำกัดThe Log Abstraction
standalone mode (Connect)worker ของ Kafka Connect ตัวเดียว config ของ connector อยู่ในไฟล์ local — ง่าย ไม่มี fault tolerance เหมาะกับ demo บนแล็ปท็อป การใช้งาน production จริงคือโหมด distributed แทนKafka Connect
state storekey-value store local (RocksDB โดย default) ที่อยู่ในแอป Kafka Streams instance เก็บผลลัพธ์ที่กำลังสะสมของ stateful operation (aggregate, count, reduce, join, windowing) หนุนหลังด้วย changelog topic ทำให้ instance ที่ crash แล้ว restart ที่อื่นสร้างมันขึ้นใหม่ได้ด้วยการ replay โดยไม่ต้องจัดการฐานข้อมูลแยกเองKafka Streams
sticky partitioner (KIP-480)กลยุทธ์ default ของ Java producer (ตั้งแต่ Kafka 2.4) สำหรับ record ที่ key เป็น null: ยึดติดกับ partition เดียวจนกว่าจะเต็มหรือ linger.ms หมด เพื่อ batch ที่น้อยกว่าแต่ใหญ่กว่า DefaultPartitioner ของ KafkaJS ไม่เคย implement แบบนี้ — พิสูจน์จาก source เองและการรันสด (1, 2, 3, 4, 5, 0, 1, 2) ว่ามันทำ round-robin key ที่เป็น null ทีละ partition ต่อการเรียกแทนKeys & Partitioning
subject (Schema Registry)key ที่ schema ถูกเก็บและ version ไว้ ปกติคือ <topic>-value (หรือ <topic>-key) — เช่น orders-value compatibility mode ของ subject (BACKWARD/FORWARD/FULL) คุมว่า registry จะรับ version ใหม่ของมันแบบไหนได้บ้างSchema Registry
Termความหมายบทเรียน
task (Connect)หน่วยของ parallelism ที่ connector ของ Kafka Connect ถูกแบ่งออก สูงสุด tasks.max — worker กระจาย task ข้าม distributed cluster (เช่น หนึ่ง task ต่อหนึ่งตาราง หรือต่อช่วง partition)Kafka Connect
timeindexไฟล์ map timestamp → offset แบบ sparse (.timeindex) ที่อยู่คู่กับ .log/.index ของทุก segment ขับเคลื่อนการค้นหาตามเวลาและ --offsets-for-times ถูก pre-allocate ที่ log.index.size.max.bytes (default 10 MiB) ไม่ว่าจะเขียนจริงน้อยแค่ไหน — เป็นสิ่งที่เห็นจริงใน ls -la ที่น่าแปลกใจอยู่บ้างThe Log Abstraction
TLS / mTLSการเข้ารหัสฝั่ง broker (listeners=SSL://:9093, keystore สำหรับตัวตนของ broker เอง, truststore สำหรับผู้ที่มันเชื่อถือ) ที่ตัวมันเองพิสูจน์แค่ตัวตนของ broker ให้ client เห็นเท่านั้น ssl.client.auth=required เปลี่ยนสิ่งนี้ให้เป็น TLS แบบ mutual เรียกร้องและตรวจสอบ certificate ของ client ด้วย ssl.endpoint.identification.algorithm=https เปิดการตรวจสอบ hostname เทียบกับ SAN ของ certificateSecurity and Production
tombstonerecord ที่มี valueSize: -1 และไม่มี payload — เครื่องหมายจริงบนดิสก์ว่า key ถูกลบบน topic แบบ compacted พิสูจน์ตรง ๆ ใน output ของ kafka-dump-log.sh มันไม่ถูกลบทันทีที่มีการเขียนใหม่มาแทนที่ — มันอยู่ต่ออย่างน้อย delete.retention.ms เพื่อให้ consumer ที่ตามหลังยังเห็นการลบRetention & Compaction
topicstream ของ record ที่มีชื่อและเป็น logical (เช่น orders) ที่ Kafka แบ่งทางกายภาพเป็น partition — topic เดียว มี log ที่เรียงลำดับอิสระต่อกันหลายตัวอยู่ข้างในWhat Is Kafka
TopologyTestDrivertest harness ของ Kafka Streams แบบ in-memory ไม่ต้องมี broker เลย — พิสูจน์สด ๆ รัน word-count topology จริง count() ปล่อย update record 8 ตัวสำหรับ input 2 บรรทัด ผ่าน repartition topic ที่ถูกสร้างให้อัตโนมัติ ต้องการ default serde ที่ไม่มี Struct ไม่งั้นจะ throw ทันทีตอนสร้าง ทดสอบ logic ของ topology ได้ แต่ไม่ทดสอบ partition assignment จริงหรือการบังคับ co-partitioningKafka Streams
transactionหน่วยงานที่ให้ producer เขียนไปหลาย partition (และ commit consumer offset) แบบ atomic — ทุกอย่างมองเห็นได้พร้อมกันตอน commit() หรือไม่มีอะไรเห็นเลยตอน abort() ทุก commit/abort ในคอร์สนี้คือ record จริงที่อ่านได้ใน __transaction_state เดินตามลำดับสถานะจริง 1 → 2 → 4 (เริ่ม → เตรียม → เสร็จสมบูรณ์)Transactions
transactional idตัวระบุที่คงที่ที่ transactional producer ลงทะเบียนตอน connect() (KafkaJS ไม่มี initTransactions() แยก — มันเป็นแบบ implicit) ทำให้ Kafka fence instance แบบ zombie ออกได้ทันทีที่ตัวแทนที่ที่ใช้ id เดียวกันเริ่ม transaction ของตัวเอง ต้องมี idempotent: true และใน Java client ต้อง max.in.flight.requests.per.connection <= 5Transactions
transaction.timeout.ms / transaction.max.timeout.mstransaction timeout ที่ producer ขอ (config key ของ KafkaJS คือ transactionTimeout) เทียบกับเพดานจริงของ broker transaction.max.timeout.ms (default 900000 คือ 15 นาที) — เกินเพดานนี้จะพังทันทีตอน InitProducerId/producer.transaction() ไม่ใช่ตอน commit ด้วย KafkaJSProtocolError จริงที่ข้อความสลับลำดับคำของ config key (max.transaction.timeout.ms) จาก key จริงTransactions
__transaction_statetopic ภายในแบบ compacted ที่เก็บ record commit/abort ของทุก transaction อ่านได้ด้วย kafka-console-consumer.sh --formatter org.apache.kafka.tools.consumer.TransactionLogMessageFormatter — แต่ละ record มี transactionalId, producerId/producerEpoch (คู่ที่ fencing ใช้เทียบ) และ transactionStatus เป็นตัวเลขTransactions
Termความหมายบทเรียน
unclean leader electionการเลือก replica ที่เก่าและไม่ตามทันเป็น leader เมื่อ ISR หล่นเหลือศูนย์ — ทำให้ partition กลับมา online ได้แต่ สูญเสีย record ทุกตัวที่มันไม่เคยได้รับ ค่า default ของ Kafka คือ unclean.leader.election.enable=false ปฏิเสธการแลกนี้ (ยืนยันสดแล้ว) เลือกความทนทานเหนือความพร้อมใช้งาน — partition ที่ ISR ทั้งหมดล่มจะอยู่ offline ต่อจนกว่า in-sync replica จะกลับมาReplication & ISR
worker (Connect)โปรเซส JVM ที่รัน connector และ task ของมันจริง ๆ worker หนึ่งตัวหรือมากกว่ารวมกันเป็น Connect cluster ในโหมด distributed ถ้า worker ตาย task ของมันจะ rebalance ไปยังตัวที่รอดอยู่Kafka Connect
ZooKeeper (removed)ระบบ coordination ภายนอกที่ Kafka เคยพึ่งพาก่อนมี KRaft เก็บ metadata ของคลัสเตอร์ไว้ใน ensemble แยกต่างหากที่ต้อง deploy, รักษาความปลอดภัยและปรับแต่งเอง ถูกเอาออกทั้งหมดตั้งแต่ Kafka 4.0 — ไม่ใช่แค่ deprecated แต่หายไปเลย: ไม่มี ZooKeeper process ให้ start และ --zookeeper ไม่ใช่ flag ที่เครื่องมือบน broker Kafka 4.3.1 ของคอร์สนี้รับด้วยซ้ำKRaft & the Cluster