อภิธานศัพท์
ศัพท์ของ 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/consume | Security 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-native | Apache มี 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-once | record ทุกตัวถูกส่งอย่างน้อยหนึ่งครั้ง ไม่มีวันหาย ทำได้โดย commit offset ของ consumer หลัง ประมวลผลเสร็จ — พิสูจน์ด้วยการ kill จริงกลางช่อง: record ถูก apply สองครั้งหลัง crash ระหว่างการเขียนจริงกับการ commit | Delivery Semantics |
| at-most-once | record ทุกตัวถูกส่งศูนย์หรือหนึ่งครั้ง ไม่มีวันซ้ำ ทำได้โดย 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 อย่าง CSV | Schema 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 นั้นได้คือของก่อนยุค KRaft | Brokers, Topics & Partitions |
| broker | Kafka server process หนึ่งตัว หลาย broker รวมกันเป็นคลัสเตอร์ แต่ละ broker เก็บข้อมูลบางส่วนและให้บริการอ่าน/เขียนสำหรับ partition ที่ตัวเองเป็น leader | What 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 topic | topic ของ Kafka ที่หนุนหลัง state store ของ Kafka Streams: ทุกการอัปเดต state store จะถูกเขียนลงไปด้วย ดังนั้น instance ที่ crash แล้ว restart ที่อื่นจะสร้าง state store ใน RocksDB ของตัวเองขึ้นใหม่ได้ด้วยการ replay changelog | Kafka Streams |
| cleanup.policy | ค่าระดับ topic ที่เลือก lifecycle ของ topic: delete (default — ลบ segment ตามอายุ/ขนาด), compact (เก็บแค่ค่าล่าสุดต่อ key) หรือ compact,delete (ทั้งคู่ — มีเพดานอายุแข็งบน snapshot ที่สร้างใหม่ได้ตาม key) | Retention & Compaction |
__cluster_metadata | topic ภายในที่ 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_RECORD | KRaft & 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 ข้าม partition | Keys & Partitioning |
| combined controller / isolated controller | node แบบ combined รัน process.roles=broker,controller (ทำทั้งสองหน้าที่ในโปรเซสเดียว — รูปแบบที่ทุกการพิสูจน์ในโมดูล foundations ของคอร์สนี้ใช้) ส่วน controller แบบ isolated รัน process.roles=controller อย่างเดียว แลกโปรเซสที่ต้องรันเพิ่มกับการแยก blast-radius ไม่ให้ load ของ client แย่ง CPU/disk I/O ของ controller quorum | KRaft & the Cluster |
| commit (offset commit) | บันทึกของ consumer ว่า “ทุกอย่างก่อน offset นี้เสร็จแล้ว” Kafka เก็บมันเป็น record จริงที่ decode ได้ใน __consumer_offsets การ commit offset ของ record ตัวเอง แทนที่จะเป็น offset + 1 คือบั๊กที่พิสูจน์แล้วว่าเกิดซ้ำได้จริง: record ล่าสุดที่ประมวลผลไปจะถูกส่งซ้ำตอน restart | Offsets & 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-javascript | Node.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.type | codec ที่ใช้บีบอัด 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_offsets | topic ภายในแบบ compacted ที่เก็บ offset ที่ commit ไว้ทุกอัน keyed ด้วย (group, topic, partition) อ่านตรง ๆ ได้ด้วย kafka-console-consumer.sh --formatter org.apache.kafka.tools.consumer.OffsetsMessageFormatter — ค่าที่ decode แล้วมี offset, leaderEpoch และ commitTimestamp | Offsets & Commits |
| consumer | client ที่อ่าน record จาก topic หนึ่งตัวหรือมากกว่า ในฐานะ consumer group โดยติดตามตำแหน่งของตัวเอง (offset) ต่อ partition | What Is Kafka |
| consumer group | กลุ่ม consumer ที่ใช้ groupId ร่วมกัน Kafka มอบแต่ละ partition ให้สมาชิกกลุ่มเพียงคนเดียวในเวลาหนึ่ง ๆ ดังนั้นจำนวน partition คือเพดานของ parallelism — consumer ตัวที่ 7 บน topic ที่มี 6 partition จะว่างสนิท กลุ่มต่างกันเป็นอิสระต่อกัน แต่ละกลุ่มติดตาม offset ของตัวเองตลอดทั้ง stream | Consumer Groups & Rebalancing |
| controller | broker ที่ทำหน้าที่เป็นส่วนหนึ่งของ 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.servers | controller.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 |
| converter | component ของ Kafka Connect ที่แปลระหว่าง record format ภายในของ Connect กับ byte บน topic — AvroConverter serialize ด้วย schema id ของ Schema Registry; JsonConverter เขียนเป็น JSON ธรรมดา นี่คือจุดที่ Schema Registry เสียบเข้ากับ pipeline ของ Connect | Kafka 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.ms | tombstone (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 | ความหมาย | บทเรียน |
|---|---|---|
| eachBatch | handler ของ KafkaJS ที่รับ batch ทั้งก้อนในครั้งเดียว และ ฟังก์ชัน heartbeat ที่ต้องเรียกเองสำหรับงานที่ช้าจริง ๆ — พิสูจน์สด ๆ: งาน eachBatch 8 วินาที (นานกว่า sessionTimeout 6 วินาที) ไม่ทำให้เกิด rejoin เลยเมื่อมีการเรียก heartbeat() เองกลางทาง | The Consumer API |
| eachMessage | handler การอ่านที่ง่ายที่สุดของ 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.tolerance | config ของ 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.version | record ชนิดหนึ่งใน __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 |
| follower | replica ของ partition ที่ไม่ทำอะไรนอกจาก fetch จาก leader ต่อเนื่องเพื่อให้เหมือนกัน ถ้า broker ของ leader ตาย follower ที่อยู่ใน ISR อยู่แล้วจะถูกเลื่อนขึ้น — พิสูจน์สด ๆ ด้วย kafka-topics.sh --describe จริงที่เห็น Leader: 3 กลายเป็น Leader: 1 หลัง broker 3 ถูกหยุด ไม่มีคำสั่ง failover เอง | Replication & ISR |
| Term | ความหมาย | บทเรียน |
|---|---|---|
| group coordinator | component ฝั่ง broker ที่สมาชิกของ consumer group ลงทะเบียนด้วยและ rebalance ผ่าน ภายใต้ consumer protocol ของ KIP-848 มันยังคำนวณ assignment เอง (แบบ diff ไม่ใช่คำนวณใหม่ทั้งหมด) แทนที่จะปล่อยให้ group leader ที่ client เลือกทำแบบ classic protocol | Consumer 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 ทันที ไม่ใช่แค่ตัวเลขบน dashboard | Monitoring & 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 producer | producer ที่ประทับ 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 | ความหมาย | บทเรียน |
|---|---|---|
| JMX | interface ที่ 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 |
| KafkaJS | Node.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-848 | consumer 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 |
| KRaft | metadata quorum ที่ Kafka สร้างเองบน Raft (KIP-500) เก็บ metadata ของคลัสเตอร์เป็น log ที่ replicate กัน แทนที่จะพึ่ง ZooKeeper ensemble ภายนอก ตั้งแต่ Kafka 4.0 เป็นต้นไป KRaft คือโหมดเดียว — ZooKeeper และ flag --zookeeper หายไปทั้งหมด ไม่ใช่แค่ deprecated | KRaft & 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 |
| leader | replica ตัวเดียวที่จัดการอ่าน/เขียนทั้งหมดของ partition ณ เวลาใดเวลาหนึ่ง ตัวอื่น (follower) แค่ fetch จากมัน ความเป็น leader กระจายแบบ round-robin ข้าม partition เพื่อไม่ให้ broker ตัวเดียวเป็น leader ของทุกอย่างบนคลัสเตอร์ที่สุขภาพดี | Replication & ISR |
| leader epoch / leader-epoch-checkpoint | leader epoch คือตัวนับที่เพิ่มขึ้นเรื่อย ๆ บอกว่า leader ไหนคุมอยู่ ณ offset ไหน leader-epoch-checkpoint คือไฟล์บนดิสก์จริงที่บันทึกมัน ใช้ตรวจจับและตัด (truncate) ข้อมูลที่เบี่ยงเบนหลังจากมี unclean leader change — หนึ่งในห้าไฟล์จริงในทุก segment directory คู่กับ .log, .index, .timeindex และ partition.metadata | The 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.listeners | listeners คือที่ที่ 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 ไม่ได้โดยไม่ restart | Retention & Compaction |
| log.segment.delete.delay.ms | segment จะถูกลบออกจากดิสก์จริง ๆ นานแค่ไหนหลังพ้น retention — default 60000ms segment จะถูกเปลี่ยนชื่อเป็น .deleted ก่อน (เช่น 00000000000000000000.log.deleted) ยืนยันสดบน broker ของคอร์สนี้เอง | Retention & Compaction |
| Term | ความหมาย | บทเรียน |
|---|---|---|
| max.in.flight.requests.per.connection | idempotence ต้องการค่านี้ไม่เกิน 5 (ไม่ใช่ต้องเป็น 1 เป๊ะ) — broker เก็บ batch สูงสุดห้าตัวต่อ producer ไว้ track sequence ขณะยังรักษา order ได้ Java client บังคับเพดานนี้เอง; KafkaJS ไม่บังคับ (ตรวจจาก source แล้ว: idempotent: true มีผลแค่ค่า default ของ retry ไม่แตะ maxInFlightRequests เลย ซึ่งยังเป็น null/ไม่จำกัดจนกว่าจะตั้งเอง) | The Idempotent Producer |
| metadata log | log ที่ replicate กันซึ่ง controller quorum ของ KRaft ดูแล — กลไกจริงที่อยู่เบื้องหลัง “รูปทรงของคลัสเตอร์ก็แค่ log อีกตัว”: ทุก broker ที่เข้าร่วมคลัสเตอร์จะ replay มันจาก offset 0 เพื่อสร้างมุมมองในหน่วยความจำของตัวเอง แบบเดียวกับที่ consumer replay data partition | KRaft & 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_random | Keys & Partitioning |
| Term | ความหมาย | บทเรียน |
|---|---|---|
| num.partitions | จำนวน partition default ทั้งคลัสเตอร์ที่ใช้เมื่อ topic ถูกสร้างอัตโนมัติหรือสร้างโดยไม่ระบุ --partitions — default จริงคือ 1 บน broker ของคอร์สนี้ เห็นได้ใน tag ที่มา DEFAULT_CONFIG ของ kafka-configs.sh --describe --all | Brokers, Topics & Partitions |
| offset | ตำแหน่งของ record ภายใน partition — จำนวนเต็มที่เพิ่มขึ้นเรื่อย ๆ ที่ consumer ติดตามเพื่อรู้ว่าตัวเองอ่านไปถึงไหนแล้ว การอ่าน record ไม่เคยลบมันออก offset คือเคอร์เซอร์ที่ consumer เป็นเจ้าของ ไม่ใช่สิ่งที่ broker เปลี่ยนตอนส่งมอบ | What Is Kafka |
| Term | ความหมาย | บทเรียน |
|---|---|---|
| page cache | cache ระดับ 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) นั้น แทนที่จะเทียบเนื้อหา payload | The Idempotent Producer |
| poll loop | consume 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 |
| producer | client ที่สร้างข้อความ (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 และ replication | Security and Production |
| read_committed / readUncommitted | isolation 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 สมาชิกที่ไม่เกี่ยวข้องยังอ่านต่อเนื่องตลอด rebalance | Consumer 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 |
| RequestHandlerAvgIdlePercent | JMX MBean kafka.server:type=KafkaRequestHandlerPool ที่วัดว่า thread pool ของ request-handler ของ broker ว่างแค่ไหน — ใกล้ 1.0 (พิสูจน์สด ๆ ที่ 0.97) แปลว่ายังมีกำลังเหลือเยอะ ยิ่งเข้าใกล้ 0 แปลว่า handler thread ทุกตัวทำงานตลอดเวลา และ latency ของ produce/fetch กำลังจะพุ่ง อ่านตรง ๆ ด้วย JmxTool คู่กับ UnderReplicatedPartitions | Monitoring & 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 ก่อน deserialize | Schema 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 ของ transaction | Transactions |
| 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 group | consumer 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-string | Kafka 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 store | key-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 ของ certificate | Security and Production |
| tombstone | record ที่มี valueSize: -1 และไม่มี payload — เครื่องหมายจริงบนดิสก์ว่า key ถูกลบบน topic แบบ compacted พิสูจน์ตรง ๆ ใน output ของ kafka-dump-log.sh มันไม่ถูกลบทันทีที่มีการเขียนใหม่มาแทนที่ — มันอยู่ต่ออย่างน้อย delete.retention.ms เพื่อให้ consumer ที่ตามหลังยังเห็นการลบ | Retention & Compaction |
| topic | stream ของ record ที่มีชื่อและเป็น logical (เช่น orders) ที่ Kafka แบ่งทางกายภาพเป็น partition — topic เดียว มี log ที่เรียงลำดับอิสระต่อกันหลายตัวอยู่ข้างใน | What Is Kafka |
| TopologyTestDriver | test 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-partitioning | Kafka 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 <= 5 | Transactions |
| transaction.timeout.ms / transaction.max.timeout.ms | transaction 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_state | topic ภายในแบบ 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 |