บทที่ 2 · Part 1 — Foundation
Architecture
Cluster, Worker, Task Queue, Event History, Namespace และเส้นทางเดินของ 1 transaction
ส่วนประกอบมีไม่เยอะ แต่ความสัมพันธ์ระหว่างกันสำคัญมาก โดยเฉพาะข้อเท็จจริงที่ว่า โค้ดของคุณไม่เคยรันบน Temporal Cluster
จบบทนี้คุณจะ
- แยกออกว่าอะไรคือ Cluster อะไรคือ Worker และใครรันโค้ดของคุณ
- เข้าใจ Task Queue, Event History และ Namespace
- ไล่เส้นทางของ 1 transaction ตั้งแต่ต้นจนจบได้
ภาพรวม
จุดที่คนเข้าใจผิดบ่อยที่สุด
Temporal Cluster ไม่เคยเห็นและไม่เคยรันโค้ดของคุณ มันเป็นแค่ที่เก็บ state และคิวงาน โค้ดทั้งหมดรันใน Worker process ที่คุณ deploy เอง แปลว่าข้อมูลลูกค้าและ credential ที่ใช้เรียก bank API อยู่ในโครงสร้างพื้นฐานของคุณ ไม่ได้อยู่ที่ Temporal (เรื่องนี้สำคัญมากตอนคุยกับทีม security — ดูบทที่ 12)
Temporal Cluster
Cluster คือ backend ที่ทำหน้าที่หลัก 3 อย่าง:
- Event History — log ถาวรของทุกสิ่งที่เกิดขึ้นกับ workflow แต่ละตัว นี่คือ source of truth ของสถานะ
- Task Queues — คิวที่ Worker มา poll เอางานไปทำ
- Visibility store — index สำหรับค้นหา/ลิสต์ workflow (เช่น "หา top-up ที่ค้างอยู่ของลูกค้ารายนี้")
รันได้ 3 แบบ:
| แบบ | คำสั่ง/รูปแบบ | ใช้เมื่อไหร่ |
|---|---|---|
| CLI dev server | temporal server start-dev | development และ test เท่านั้น — เป็น single process, ค่าเริ่มต้นเก็บใน memory |
| Self-hosted | deploy เอง + ฐานข้อมูล (Cassandra / PostgreSQL / MySQL) + Elasticsearch | เมื่อต้องเก็บทุกอย่างในโครงสร้างพื้นฐานของตัวเอง (ข้อกำหนดที่พบบ่อยในสถาบันการเงิน) |
| Temporal Cloud | managed service | ไม่อยากดูแล cluster เอง — ต้องผ่าน vendor risk assessment ก่อน |
ห้ามใช้ dev server บน production
แม้จะใส่ --db-filename ให้ persist ลงไฟล์ SQLite ได้ก็ตาม
dev server ออกแบบมาเพื่อ development เท่านั้น ไม่มี HA ไม่มี scaling
Worker
Worker คือ process ที่คุณเขียนและ deploy เอง (ในที่นี้คือ Go binary ตัวหนึ่ง) มันวนลูป poll → execute → report กับ Task Queue ที่ระบุไว้
Worker 1 ตัว host โค้ด 2 ประเภท:
| Workflow | Activity | |
|---|---|---|
| หน้าที่ | สั่งงาน จัดลำดับ ตัดสินใจ | ลงมือทำงานจริงที่มี side effect |
| ข้อบังคับ | ต้อง deterministic ห้าม I/O | ทำอะไรก็ได้ — เรียก API, เขียน DB, อ่านไฟล์ |
| ตัวอย่างในเคส wallet | "ตรวจ limit ก่อน แล้วค่อยหักธนาคาร ถ้าหักไม่ผ่านให้จบเลย" | "POST ไปที่ /v1/debit ของธนาคาร" |
| ถ้าล้ม | ถูก replay ใหม่จาก history | ถูก retry ตาม Retry Policy |
การแบ่งนี้คือหัวใจของ Temporal: ความไม่แน่นอนทั้งหมดถูกกักไว้ใน Activity ส่วน Workflow เป็นตรรกะบริสุทธิ์ที่สร้างขึ้นใหม่ได้เสมอ
Task Queue
Task Queue เป็นแค่ ชื่อ (string) ที่ใช้จับคู่ระหว่างงานกับ Worker ไม่ต้องสร้างล่วงหน้า พอมี Worker มา poll ชื่อนี้ และมีคน start workflow ด้วยชื่อนี้ ก็จับคู่กันเอง
ในระบบ wallet จริง มักแยก Task Queue ตามคุณสมบัติของงาน:
wallet-core → workflow หลัก (เบา, จำนวนมาก)
bank-integration → activity ที่ยิง bank API (ต้องคุม concurrency ตาม rate limit ธนาคาร)
notification → push / SMS (ล้มได้ ไม่กระทบเงิน)
reconciliation → batch กลางคืน (กิน CPU/memory สูง)
เหตุผลที่ต้องแยก: ถ้า batch reconciliation กิน worker หมด การเติมเงินของลูกค้าจะไม่มี worker เหลือทำ การแยก queue ทำให้จำกัดผลกระทบได้ (เรื่อง capacity และการกันไม่ให้ tenant ใหญ่กิน worker หมด อยู่ในบทที่ 11)
Event History
ทุก workflow execution มี event history ของตัวเอง เป็น log แบบ append-only ที่บันทึกทุกอย่างที่เกิดขึ้น ลองดูของ top-up 1 รายการ:
1 WorkflowExecutionStarted input: {walletID, amount: 1000}
2 WorkflowTaskScheduled
3 WorkflowTaskStarted
4 WorkflowTaskCompleted
5 ActivityTaskScheduled CheckLimit
6 ActivityTaskStarted
7 ActivityTaskCompleted result: {ok: true}
8 WorkflowTaskScheduled
...
12 ActivityTaskScheduled ChargeBank
13 ActivityTaskStarted
14 ActivityTaskFailed "connection reset" ◀── ธนาคารล่ม
15 ActivityTaskScheduled ChargeBank (attempt 2)
16 ActivityTaskStarted
17 ActivityTaskCompleted result: {bankRef: "BK-8891"}
...
25 WorkflowExecutionCompleted
สิ่งที่ได้จาก history นี้:
- Audit trail ฟรี — ตอบได้ว่าธุรกรรมนี้เกิดอะไรขึ้นบ้าง เวลาไหน ลองใหม่กี่ครั้ง เพราะอะไร
- กู้สถานะได้ — worker ตัวใหม่อ่าน history แล้วสร้างสถานะเดิมขึ้นมาใหม่
- Debug ได้จริง — เปิด Web UI แล้วเห็น timeline ทั้งหมด ไม่ต้องไล่ log ข้าม service
ข้อจำกัดที่ต้องจำ
- payload เดี่ยว สูงสุด 2 MB
- gRPC message สูงสุด 4 MB
- history ทั้งก้อน สูงสุด 50 MB (ในทางปฏิบัติควรคุมให้ต่ำกว่า 10 MB)
เพราะฉะนั้น อย่าส่งข้อมูลก้อนใหญ่ผ่าน workflow เช่น ไฟล์ statement หรือ list ธุรกรรม 100,000 แถว ให้ส่ง reference (S3 key, transaction ID) แทน แล้วให้ activity ไปโหลดเอง — ดูบทที่ 11
Namespace
Namespace คือหน่วยแบ่งแยกเชิงตรรกะภายใน cluster — workflow ID จะไม่ชนกันข้าม namespace และตั้งค่า retention, RBAC, rate limit แยกกันได้
รูปแบบการแบ่งที่ใช้บ่อยในองค์กรการเงิน:
- ตาม environment:
wallet-dev,wallet-uat,wallet-prod - ตามระดับความอ่อนไหวของข้อมูล เพื่อให้กำหนดสิทธิ์เข้าถึงต่างกันได้
- ตาม business unit เมื่อแต่ละทีมต้องการ retention policy ของตัวเอง
Retention period สำคัญเป็นพิเศษ — เป็นตัวกำหนดว่า history ของ workflow ที่จบแล้ว จะถูกเก็บนานแค่ไหน ซึ่งต้องสอดคล้องกับข้อกำหนดการเก็บเอกสารของธุรกิจคุณ อย่าถือว่า Temporal เป็นที่เก็บ audit log ระยะยาว ถ้าต้องเก็บหลายปี ให้ส่งออกไปยังระบบจัดเก็บของคุณเอง
เส้นทางของ 1 transaction
สังเกตขั้นตอน "รัน workflow ตั้งแต่ต้น แต่ใช้ผลเดิมจาก history" — workflow ถูกรัน ตั้งแต่บรรทัดแรกใหม่ทุกครั้ง แต่ step ที่มีผลลัพธ์อยู่ใน history แล้ว จะไม่ถูกเรียกซ้ำ SDK จะคืนค่าเดิมกลับมาให้ทันที
นี่คือสาเหตุที่ workflow ต้อง deterministic และเป็นหัวข้อของบทถัดไป ซึ่งเป็นบทที่สำคัญที่สุดของคอร์สนี้
คำศัพท์ที่ควรจำจากบทนี้
Command = สิ่งที่ workflow code ขอให้ทำ (เช่น "schedule activity นี้") · Event = สิ่งที่ถูกบันทึกลง history จริง · ความไม่ตรงกันระหว่าง 2 อย่างนี้คือที่มาของ non-determinism error