Skip to main content

Redis Cache ของ Webhook Config และ Agent Mode

ภาพรวม

บนเส้นทางประมวลผล LINE event นั้น webhook service ไม่แตะ PostgreSQL เลย การตัดสินใจ routing ทั้งหมดมาจาก Redis เพียง 2 key เท่านั้น การออกแบบเช่นนี้เป็นเพราะ endpoint ต้องตอบกลับ LINE ให้เร็วที่สุด และเป็นจุดที่รับ traffic สูงสุดในระบบ

Key (ก่อนเติม prefix)ชนิดผู้เขียนผู้อ่าน
webhook_config:{webhookId}hashcms-api (ตอนแก้ config ของ OA)webhook-go (อ่านอย่างเดียว)
agent_mode:{lineOaId}:{userId}hashworker-go (ตอนเริ่ม/จบ session) และ webhook-go (เฉพาะ lastActivity)webhook-go

ทุก key จะถูกเติม prefix REDIS_NAMESPACE (ค่าเริ่มต้นคือ LINE_MANAGEMENT:) โดย internal/cache.Cache ให้อัตโนมัติ กล่าวคือโค้ดเรียกด้วยชื่อสั้น แต่ key จริงบน Redis คือ LINE_MANAGEMENT:webhook_config:xxxx

Business Flow

ฟิลด์ใน webhook_config ที่โค้ดอ่านจริง

ฟิลด์การใช้งาน
lineOaIdแปลงเป็น int64 แล้วใส่ลง payload ของทุกคิว หาก parse ไม่ได้จะได้ค่า 0 โดยไม่ crash
forwardWebhookUrlถ้าไม่ว่างจะ forward body ดิบไปยัง URL นี้
mboxEnabledค่า "1" หมายถึงเปิดระบบคุยกับเจ้าหน้าที่
mboxBaseUrl, mboxApiToken, mboxAccountId, mboxInboxIdcredential ของ Chatwoot ที่แนบไปกับ payload ส่งให้ worker
mboxLineWebhookUrlURL ปลายทางที่ forward ข้อความไปขณะอยู่ใน agent mode
mboxTimeoutMinutes, mboxWarningMinutesระยะเวลา timeout และการเตือน (worker เป็นผู้ใช้งาน)
mboxGreetingMessage, mboxWarningMessage, mboxEndMessage, mboxTimeoutMessageข้อความระบบที่ส่งให้ผู้ใช้
mboxExitKeywords, mboxAgentKeywordskeyword ที่ใช้ trigger การเข้าและออก agent mode
mboxDepartmentPickerEnabledค่า "1" หมายถึงให้ผู้ใช้เลือกแผนกก่อน handoff
mboxDepartmentPickerHeaderText, mboxDepartmentPickerGeneralLabel, mboxDepartmentsข้อมูลปุ่มเลือกแผนก โดย mboxDepartments เก็บเป็น JSON string

ค่าทุกตัวเป็น string เนื่องจากเป็น Redis hash โค้ดจึงเปรียบเทียบตรง ๆ กับ "1" ไม่ใช่ค่า boolean

พฤติกรรมเมื่อ cache miss

เมื่อ HGETALL คืน map ว่าง ระบบจะถือว่าไม่มี config แล้ว publish payload ทั้งก้อน ลงคิว line_webhook แล้วจบการทำงาน ไม่ถือเป็น error และไม่ fallback ไปอ่านฐานข้อมูล เนื่องจาก worker-go จะเป็นผู้ค้น config จากฐานข้อมูลเอง

พฤติกรรมเมื่อ Redis error

ระบบจะ log ข้อความ processLine: HGETALL webhook_config failed แล้ว return ทันที ข้อความนั้นจะหายไปโดยไม่มี retry และไม่มี dead letter queue เพราะ response 200 ถูกส่งกลับ LINE ไปก่อนหน้าแล้ว จุดนี้เป็นข้อมูลสำคัญเมื่อต้องสืบสวนกรณีข้อความหาย

agent_mode และการนับ activity

  • การมี key อยู่ (hash ไม่ว่าง) หมายความว่าผู้ใช้คนนี้กำลังคุยกับเจ้าหน้าที่ บอทจึงต้องเงียบ
  • webhook-go เขียนเฉพาะฟิลด์ lastActivity เป็น epoch หน่วยมิลลิวินาทีในรูป string ทุกครั้งที่มีข้อความเข้ามา และ ไม่ตั้ง TTL เพราะการหมดอายุ session เป็นหน้าที่ของ worker-go
  • การสร้างและลบ key นี้ทั้งหมดอยู่ที่ worker-go โดย webhook-go เพียงแตะ lastActivity เท่านั้น

ไฟล์และฟังก์ชันหลัก

ไฟล์ฟังก์ชัน
internal/cache/cache.goCache.HGetAll, Cache.HSet, Cache.Get/Set/Del/DelWildcard/Keys/HGet/IncrBy/Expire, setKeyName (เติม namespace)
internal/line/service.goเรียก HGetAll สำหรับ webhook_config และ agent_mode และมี buildMboxConfig สำหรับ map hash เป็น struct
internal/platform/redisx/redisx.goManager แบบหลาย instance โดย instance ที่ใช้ชื่อว่า "redis"
internal/config/api_load.goค่า REDIS_URL, REDIS_NAMESPACE (ค่าเริ่มต้น LINE_MANAGEMENT) และ REDIS_TTL
cmd/api/main.goสร้าง cache.New(rdb, namespace, ttl) แล้วใส่ลงใน server.Deps.Cache

จุดเชื่อมต่อกับ Service อื่น

  • cms-api — โมดูล line-oa-management และ auto-response เป็นผู้ warm cache นี้ หาก cache หายหรือไม่ถูก sync webhook จะทำงานในโหมด default คือส่งเข้าคิวอย่างเดียว
  • worker-go — เป็นเจ้าของ lifecycle ของ agent_mode:* ทั้งการสร้างเมื่อ handoff สำเร็จ และการลบเมื่อ exit หรือ timeout อีกทั้งเป็น consumer ของคิว line_webhook
  • Redis instance เดียวกัน ถูกใช้เป็น backend ของ rate limiter ด้วย แต่แยก key space ต่างหากที่ throttle:app:*
  • หากไม่ได้ตั้งค่า Redis (REDIS_URL ว่าง) ตัว deps.Cache จะเป็น nil และ handler จะไม่ spawn pipeline เลย โดย endpoint ยังคงตอบ 200 แต่จะไม่มีการประมวลผลใด ๆ เกิดขึ้น