2.5 · Data flow + caching
บทนี้เล่าว่าข้อมูลเคลื่อนที่ในระบบอย่างไร — เข้าทางไหน ออกทางไหน และ Redis "cache" ที่หลายคนคิดว่ามี แท้จริงแล้วใช้งานเกือบไม่ได้เลย
Data flow แบบแบ่งตามทิศทาง
(write) (read)
│ │
▼ ▼
┌───────────────────┐ ┌────────────────────┐
│ end-user แก้ข้อมูล │ │ end-user อ่านข้อมูล │
└─────────┬─────────┘ └─────────┬──────────┘
│ │
│ mutation │ query
▼ ▼
vtrc-api (MariaDB) vtrc-api (proxy)
│ │
│ sync (batch) │ GraphQL via END_POINT_CENTRALIZE
▼ ▼
HRMI/MSSQL cu-central-api
│
▼
MSSQL (HRMI)หลักสำคัญ — write ไป MariaDB ก่อน → แล้ว sync ไป HRMI ใน batch แต่ read ดึง live จาก HRMI ทุกครั้ง (ผ่าน cu-central-api)
กฎเหล็กเรื่อง data ownership
| ข้อมูล | Source of truth | หมายเหตุ |
|---|---|---|
| ข้อมูลส่วนตัวพนักงาน (ชื่อ, นามสกุล, identityCard) | HRMI | VTRC อ่านอย่างเดียว |
| ที่อยู่, เบอร์โทร | HRMI | แก้ไม่ได้ใน VTRC |
| บัญชีธนาคาร | HRMI | — |
| Password | cu-central-api + HRMI | forgetPassword → override HRMI |
| ข้อมูลการลา, สวัสดิการ, กองทุน | MariaDB (VTRC) | VTRC เป็น source |
| Tax draft (ความคุมลดหย่อน) | MariaDB → sync HRMI | sync ใน batch |
| Profile beneficiary (กองทุน) | MariaDB | — |
| คำขอทั้งหมด | MariaDB | — |
| Session, User, Role | MariaDB | — |
Read flow — "live query ทุกครั้ง"
VTRC อ่าน HR data แบบ ไม่มี cache จริง แม้จะมี Redis อยู่ใน topology
พนักงานเปิดสลิป
│
▼
vtrc-api resolver → controller → centralize bridge
│
▼
cu-central-api (END_POINT_CENTRALIZE) → MSSQL
│
▼
return rows กลับมาทั้งหมดตอนอ่านโค้ดจะเจอ setCache=true ในหลาย resolver — แต่ call site จริงใน lib/controllers/file/file.js:314-315 ถูก comment ไว้หมดแล้ว
Redis — "wired แต่ vestigial"
เปิดดู vtrc-api/api/src/lib/redis.js:1-20 — Redis module มีครบ
redisSet(key, value, expSeconds)redisGet(key)redisDelete(key)
และมี helper ระดับสูง lib/controllers/redis.controller.js:1-20 —
setCacheData(name, uid, value)→ key patterncache:{name}:{uid}, TTL 36000s (10 ชม.)getCacheData(name, uid)
แต่ในโค้ดจริง แทบทุก call site ถูก comment ไว้
| ไฟล์ | บรรทัด | สถานะ |
|---|---|---|
routes.js:10 | import | import มาแต่ไม่ใช้ |
routes.js:411-444 | ใน /file/:id | comment หมด |
resolvers/post.js:7, 39-41, 103-104 | post resolver | comment |
resolvers/course.js:6, 36-37, 57-58 | course resolver | comment |
resolvers/hrmi.js:7 | import | comment ทั้งบรรทัด |
lib/controllers/file/file.js:6, 314-315 | file controller | comment ที่จุดใช้ |
สรุป — Redis ใน vtrc-api เสมือนไม่มี ปัจจุบันทำหน้าที่แค่ session store ให้ cu-central-api
ทำไมถึง comment ไว้
ไม่มี git blame อธิบาย แต่เดาได้ 2 เหตุผล
- stale data — cache hit แล้วได้ข้อมูลเก่า ทำให้พนักงานเห็นสลิปเดือนเก่า จึงปิดไป
- invalidation ยาก — เพราะ data source จริงอยู่ใน HRMI ที่ VTRC ไม่ได้ควบคุม จึงไม่รู้จังหวะที่ควร invalidate
Redis ที่ยังใช้จริง
Redis ไม่ได้ตายทั้งหมด — ยังใช้ใน 2 จุด
| การใช้ | ที่ไหน | key pattern |
|---|---|---|
| Session store ของ cu-central-api | cu-central-api/main-api/src/lib/cache/ | (ตัวอย่าง pattern session:{sid}) |
| OTP store ของ cu-central-api | cu-central-api/main-api/src/lib/otp/ | OTP rows ใน DB + Redis |
ใน vtrc-api เอง — Redis แทบไม่ได้ใช้เลย แม้ container ยังเปิดอยู่ (vtrc-api/docker-compose.yml:3-13)
Write flow — "MariaDB ก่อน → sync HRMI ทีหลัง"
เมื่อพนักงานยื่นคำขอ (ลา, เบิกสวัสดิการ, แก้ tax) ข้อมูลไปที่
1. vtrc-api บันทึกใน MariaDB (ทันที)
2. sync ไป HRMI (ใน batch — ไม่ใช่ทันที)
3. HRMI ใช้ draft คำนวณภาษีเดือนถัดไปตัวอย่าง — tax draft (vtrc-api/api/src/lib/controllers/tax/)
พนักงานเปิดหน้า "ความคุมลดหย่อน"
│
▼
fetchTaxDraft(month, year) → ดึง draft ปัจจุบันจาก MariaDB
│
│ (ถ้ายังไม่เคยกรอก → prefill จาก HRMI)
▼
templateFormTax → ขอ form schema
│
▼
พนักงานแก้ข้อมูล → createTaxDraft / updateTaxDraft
│
▼
draft ถูกเก็บใน MariaDB (table TaxDraft)
│
│ (sync ไป HRMI ใน batch — ไม่ใช่ทันที)
▼
HRMI ใช้ draft คำนวณภาษีเดือนถัดไประวัง — sync ไป HRMI ไม่ใช่ทันที ถ้า HR หรือพนักงานแก้ draft แล้ว HRMI ยังไม่ sync → ฝั่ง HRMI จะใช้ข้อมูลเก่า จึงต้องรอ batch window
ความสัมพันธ์ระหว่าง 2 schema ใน MariaDB
vtrc-api/api/src/connector.js:8-48 — เปิด connection 2 ตัว
| Connection | Schema | ใช้ทำอะไร |
|---|---|---|
db | vtrc | core business data (users, sessions, leave, withdraw, fund) |
db2 | vtrc-centralize | cross-schema join tables + Session.tokenCD (จาก centralize bridge) |
ตัวอย่าง — Session.tokenCD เก็บ token ที่ centralize bridge refresh แล้ว (vtrc-api/api/src/centralize/controllers/auth.js:116-124) — ใช้สำหรับ session continuity ระหว่าง vtrc-api และ cu-central-api
DB pool กับ replica
VTRC-API มี 4 replica ที่เปิด pool max: 100 ต่อ replica — รวมสูงสุด 400 connection จาก api tier เข้า MariaDB
ถ้า MariaDB max_connections ตั้งไว้ต่ำกว่านี้ → connection exhaustion ได้
cu-central-api เปิด pool 6 ตัว × max 100 → 550 connection เข้า MSSQL — เสี่ยงเดียวกัน
Cache strategy ที่ควรจะเป็น vs ที่เป็น
| Feature | ที่ควร | ที่เป็น |
|---|---|---|
| สลิปเงินเดือน | cache 1-7 วัน (TTL ตามรอบเงินเดือน) | ไม่ cache — ดึง live ทุกครั้ง |
| โครงสร้างองค์กร | cache 1 วัน (เปลี่ยนนาน) | ไม่ cache |
| รายชื่อผู้อนุมัติ | cache 1 ชม. | ไม่ cache |
| Tax draft | ไม่ cache (user แก้บ่อย) | ไม่ cache ✓ |
| Profile พนักงาน | cache 1 ชม. | ไม่ cache |
ถ้าทำ performance optimization — caching เป็นจุดที่ให้ผลคุ้มที่สุด เพราะ Redis พร้อมใช้ แค่เปิด comment กลับมา (แต่ต้องแก้ปัญหา invalidation ก่อน)
เคล็ดลับตอนทำงานจริง
ถ้าเจอ "ข้อมูลไม่อัปเดต"
อย่าเริ่ม clear cache Redis — เพราะแทบไม่ได้ใช้ ให้เช็คก่อนว่า
- ข้อมูลที่แก้ → เข้า MariaDB หรือยัง (ดู
updatedAtในตาราง) - ถ้าต้อง sync ไป HRMI → sync ใน batch window หรือยัง
- ถ้าเป็น HR data → ต้องแก้ที่ HRMI ต้นทาง ไม่ใช่ VTRC
ถ้า HR บอกว่า "ฝั่ง HRMI ยังไม่เห็น"
เพราะ sync เป็น batch — บางครั้งต้องรอ 1-2 ชม. ขึ้นอยู่กับ cron window ถ้าเร่งด่วน HR ต้อง manual sync
"ทำไม query ช้า"
ส่วนใหญ่ไม่ใช่ที่ cache แต่ที่
Sessiontable ใหญ่ →LIKE %jti%ไม่ใช้ index- HRMI query complex → MSSQL slow
- N+1 query ใน resolver
ลอง EXPLAIN ใน MariaDB ก่อน แล้วค่อยไปที่ centralize bridge