event_queue
定位
event_queue 用來派發通知類任務。
常見通知類型:
- SMS
- app push
- web push
- MoEngage
寫入位置
寫入 model:
Lib/Model/EventQueue.php寫入方式:
EventQueue::get()->insertEvent($eventKey, $eventTaskDO);資料會寫入:
event_queue.event_key
event_queue.params
event_queue.priority其中 params 通常是 EventTaskDO JSON,裡面會包含 tasks。
消費 worker
worker:
Endpoint/V1/EventQueue.php::index()啟動 script:
CLI/cron_event_queue.shworker 流程:
讀 event_queue
-> lock row
-> 複製到 event_queue_log_*
-> 刪除 event_queue row
-> 查 event_actions
-> NotificationFactory 建 handler
-> handler process()
-> 更新 event_queue_log_*.status_id / message控制參數
event_queue worker 的版本控制參數放在:
event_queue_params.p_key = run_version目前基準值:
| id | p_key | p_value |
|---|---|---|
| 1 | run_version | 2 |
若有調整 queue 執行程式,且程式會依版本切換邏輯,要同步更新 run_version 的 p_value。
完整控制參數請看 event_queue_params。
event_queue_log 分表
EventQueue worker 會依時間決定 log table:
event_queue_log_1
event_queue_log_2
event_queue_log_3
event_queue_log_4所以查 event log 時不要只查 event_queue_log。
實務查詢時要把 event_queue_log_1 到 event_queue_log_4 都納入。只查 event_queue 與舊的 event_queue_log 主表,會把已被 worker 成功處理的 notification 誤判成沒有送出。
Notification handler
dispatcher:
Lib/Util/Notification/NotificationFactory.php注意:NotificationFactory::$handlers 是用 event_actions.template_type 找 handler,不是用 event_queue.event_key。例如 User_Change_Password 會先查 event_actions 得到 template_type=email,最後才走 EmailHandler。詳細說明見 event_actions。
handler map:
| template_type | handler |
|---|---|
email | EmailHandler |
sms | SmsHandler |
push | PushHandler |
web_push | WebPushHandler |
moengage | MoengageHandler |
fet_push | FetnetPushHandler |
常用 SQL
查待處理 queue
SELECT
id,
created,
event_key,
params,
priority,
retry
FROM event_queue
WHERE event_key = '<event_key>'
ORDER BY id DESC
LIMIT 20;查近期分表 log
SELECT
id,
inserted,
executed,
event_key,
params,
message,
status_id,
retry
FROM event_queue_log_1
WHERE event_key = '<event_key>'
ORDER BY id DESC
LIMIT 20;實務上要依環境使用中的分表查 event_queue_log_1 到 event_queue_log_4。為了避免漏查,建議直接 union 四張分片表:
SELECT * FROM (
SELECT 'event_queue_log_1' AS log_table, id, inserted, executed, event_key, user_id, event_queue_id, status_id, retry, message, params
FROM event_queue_log_1
WHERE event_key = '<event_key>'
AND params LIKE '%<quote_bid_id_or_request_id>%'
UNION ALL
SELECT 'event_queue_log_2' AS log_table, id, inserted, executed, event_key, user_id, event_queue_id, status_id, retry, message, params
FROM event_queue_log_2
WHERE event_key = '<event_key>'
AND params LIKE '%<quote_bid_id_or_request_id>%'
UNION ALL
SELECT 'event_queue_log_3' AS log_table, id, inserted, executed, event_key, user_id, event_queue_id, status_id, retry, message, params
FROM event_queue_log_3
WHERE event_key = '<event_key>'
AND params LIKE '%<quote_bid_id_or_request_id>%'
UNION ALL
SELECT 'event_queue_log_4' AS log_table, id, inserted, executed, event_key, user_id, event_queue_id, status_id, retry, message, params
FROM event_queue_log_4
WHERE event_key = '<event_key>'
AND params LIKE '%<quote_bid_id_or_request_id>%'
) logs
ORDER BY inserted DESC
LIMIT 20;查下游通知產物
如果 event_queue_log_* 顯示 success,也可以用下游表佐證:
| 通知類型 | 常見佐證表 | 常用線索 |
|---|---|---|
email_logs, email_queue | to, subject, template_key, sent_time | |
| SMS | sms_logs | phone, result |
| push | push_logs, push_sent_logs, fetnet_push_logs | name, user_id, template, result |
注意:push_logs 多半是 daily counter,不一定能精準對到單一 bid;精準佐證優先看 event_queue_log_*.params / message,再看 push_sent_logs 或 provider-specific push log。
重跑 event_queue_wait
event_queue_wait 用來暫存 retry 太多次、或第三方服務異常時先移出的 notification 事件。
相關 worker:
Endpoint/V1/EventQueue.php::retry_wait_event_queue()內建重跑條件:
retry < 7
AND created < NOW() - INTERVAL 30 MINUTE
AND created > NOW() - INTERVAL 1 DAY因此超過 1 天的 event_queue_wait row 不會被內建 retry 自動撈回,需要人工確認後手動搬回 event_queue。
先確認 event_queue worker 版本:
SELECT id, p_key, p_value
FROM event_queue_params
WHERE p_key = 'run_version'
LIMIT 1;若只是重跑 wait row,通常不需要更新 run_version。只有調整 queue worker 程式、且程式會依版本切換邏輯時,才更新 run_version。
確認要重跑的 wait rows:
SELECT id, created, event_key, params, priority, retry
FROM event_queue_wait
WHERE id IN (<event_queue_wait_ids>)
ORDER BY id;手動搬回 event_queue:
START TRANSACTION;
INSERT INTO event_queue (
created,
event_key,
params,
priority,
retry
)
SELECT
NOW(),
event_key,
params,
priority,
retry
FROM event_queue_wait
WHERE id IN (<event_queue_wait_ids>);
DELETE FROM event_queue_wait
WHERE id IN (<event_queue_wait_ids>);
COMMIT;確認已進 queue:
SELECT id, created, event_key, params, priority, retry
FROM event_queue
WHERE created >= NOW() - INTERVAL 5 MINUTE
AND event_key IN (<event_keys>)
ORDER BY id DESC;重跑時建議保留原 retry,不要重設為 1。這些 row 通常已失敗多次,保留 retry 可以避免第三方服務尚未恢復時無限重送;若再次失敗,worker 會依原流程提高 retry 並再次放回 event_queue_wait。
排查重點
event_queuerow 可能很快被搬到event_queue_log_*。- 測試機若有 cron / worker 正在跑,
event_queuerow 可能送出後立刻被派發通知。 - event 是否會送 email / SMS / push,要查
event_actions。 - handler 失敗時看
event_queue_log_*.message與 application log channelEventQueue_index。 event_queue_log_*.message中各 template value 為true時,代表該 handler 成功;若是字串錯誤,代表該 template 沒有設定或 handler 失敗。- 查 notification payload 時要從
params.tasks看實際 task,不要只看event_actions。 - 調整 queue worker 程式後,要確認
event_queue_params.run_version的p_value是否符合新版程式預期。 event_queue_wait內建 retry 只撈 30 分鐘以上、1 天以內、retry < 7的 row;超過 1 天要人工確認後手動搬回。
實測誤判案例
provider_accept_narrow_match staging 實測中,quote_bid_id = 2147684964 的主流程與通知都正常:
event_key = Receive_New_Leads_Quote
status_id = 1
log table = event_queue_log_2一開始只查:
event_queue
event_queue_log會查不到 notification,因為 worker 已消費 event_queue,且 log 寫在 event_queue_log_2。正確查法是:
event_queue
event_queue_log_1
event_queue_log_2
event_queue_log_3
event_queue_log_4該案例的 event_queue_log_2.message 顯示:
email.112 Accept New Leads Notification = true
sms.sms.new.leads.consumer = true
web_push.push.content.new.leads.consumer = true
push.push.content.new.leads.consumer = true並且可從 email_logs / sms_logs / push_logs 看到下游產物。