Skip to main content

pg_notify และ Event Outbox

ภาพรวม

Domain นี้มีตารางเดียวคือ event_outbox ซึ่งไม่ได้ประกาศไว้ใน Prisma schema แต่สร้างด้วย manual SQL (manual-sql/5.sql) ทำหน้าที่เป็น outbox สำรองของกลไก pg_notify — ทุกครั้งที่ trigger function ยิง pg_notify จะ insert payload เดียวกันลงตารางนี้ด้วย เพื่อให้ consumer ที่หลุดการเชื่อมต่อไปสามารถ catch-up ย้อนหลังได้ (pg_notify ไม่เก็บข้อความที่ส่งไปแล้ว)

ตาราง event_outbox

ColumnTypeNullableDefaultคำอธิบาย
idBIGSERIALNOnextval(...)Primary key
channelVARCHAR(100)NO-ชื่อ channel ของ pg_notify ที่ event นี้ถูกส่งไป
payloadJSONBNO-payload ของ event (เนื้อหาเดียวกับที่ส่งผ่าน pg_notify)
created_atTIMESTAMPTZNONOW()วันเวลาที่ event ถูกบันทึกลง outbox
processedBOOLEANNOFALSEconsumer ประมวลผล event นี้แล้วหรือยัง
processed_atTIMESTAMPTZYES-วันเวลาที่ event ถูกประมวลผลเสร็จ

หมายเหตุ

pg_notify channel ทั้งหมดในระบบ

ChannelTrigger functionตารางต้นทางเงื่อนไขที่ยิง
attribute_changednotify_attribute_change()line_userมีการเปลี่ยนค่าใน column หลัก (display_name, firstname, lastname, email, mobile_no, language, follow) หรือ key ใด ๆ ใน custom_attribute
friend_track_event_insertednotify_friend_track_event_insert()friend_track_eventมีการ INSERT แถวใหม่
form_submittednotify_form_submitted()form_submissionis_submitted เปลี่ยนจาก false เป็น true
campaign_clicknotify_campaign_click()tracking_logINSERT ที่มี action_type = 'click', content_type = 'campaign' และมี campaign_id กับ line_uid

Trigger ที่ผูกกับ function เหล่านี้

  • trg_line_user_attribute_change — AFTER UPDATE ON line_user
  • friend_track_event_insert_notify — AFTER INSERT ON friend_track_event
  • form_submission_notify — AFTER INSERT OR UPDATE ON form_submission
  • tracking_log_campaign_click_notify — AFTER INSERT ON tracking_log

ข้อสังเกตอื่น ๆ

  • event_outbox ไม่มีอยู่ใน prisma/schema.prisma — ถ้าใช้ Prisma migrate ต้องรัน manual-sql/5.sql แยกต่างหาก
  • มี partial index 2 ตัวเพื่อรองรับรูปแบบการใช้งานหลัก:
    • idx_event_outbox_catchup บน created_at โดยกรอง WHERE processed = FALSE — ใช้ดึง event ที่ยังไม่ถูกประมวลผลตอน consumer กลับมาเชื่อมต่อ
    • idx_event_outbox_cleanup บน processed_at โดยกรอง WHERE processed = TRUE — ใช้ลบ event เก่าที่ประมวลผลไปแล้ว
  • notify_attribute_change() จะข้ามการทำงานทั้งหมดเมื่อ session ตั้งค่า app.skip_triggers = 'true' (ใช้กับงาน bulk update และเป็น transaction-scoped จึงปลอดภัย ในระบบ multi-tenant)
  • notify_form_submitted() ต้อง JOIN form_builder และ line_oa เพื่อหา lineOaId และ organizationId ก่อนประกอบ payload เพราะ form_submission ไม่ได้เก็บสองค่านี้ไว้
  • payload ทั้งหมดถูกสร้างด้วย json_build_object(...)::text แล้วแปลงเป็น jsonb ตอน insert ลง outbox ดังนั้นค่าใน payload จึงตรงกับที่ส่งผ่าน pg_notify ทุกประการ