Merge pull request #1943 from qwibitai/fix/pending-rows-idempotent
fix(delivery): make pending_questions/approvals insert idempotent
This commit is contained in:
@@ -97,10 +97,16 @@ export function deleteSession(id: string): void {
|
|||||||
|
|
||||||
// ── Pending Questions ──
|
// ── Pending Questions ──
|
||||||
|
|
||||||
export function createPendingQuestion(pq: PendingQuestion): void {
|
/**
|
||||||
getDb()
|
* Insert a pending question row. Idempotent: when delivery fails and retries,
|
||||||
|
* the second attempt calls this with the same question_id — without `OR
|
||||||
|
* IGNORE` that would throw UNIQUE and prevent the retry from reaching the
|
||||||
|
* actual send step. Returns true if a new row was inserted.
|
||||||
|
*/
|
||||||
|
export function createPendingQuestion(pq: PendingQuestion): boolean {
|
||||||
|
const result = getDb()
|
||||||
.prepare(
|
.prepare(
|
||||||
`INSERT INTO pending_questions (question_id, session_id, message_out_id, platform_id, channel_type, thread_id, title, options_json, created_at)
|
`INSERT OR IGNORE INTO pending_questions (question_id, session_id, message_out_id, platform_id, channel_type, thread_id, title, options_json, created_at)
|
||||||
VALUES (@question_id, @session_id, @message_out_id, @platform_id, @channel_type, @thread_id, @title, @options_json, @created_at)`,
|
VALUES (@question_id, @session_id, @message_out_id, @platform_id, @channel_type, @thread_id, @title, @options_json, @created_at)`,
|
||||||
)
|
)
|
||||||
.run({
|
.run({
|
||||||
@@ -114,6 +120,7 @@ export function createPendingQuestion(pq: PendingQuestion): void {
|
|||||||
options_json: JSON.stringify(pq.options),
|
options_json: JSON.stringify(pq.options),
|
||||||
created_at: pq.created_at,
|
created_at: pq.created_at,
|
||||||
});
|
});
|
||||||
|
return result.changes > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPendingQuestion(questionId: string): PendingQuestion | undefined {
|
export function getPendingQuestion(questionId: string): PendingQuestion | undefined {
|
||||||
@@ -131,16 +138,21 @@ export function deletePendingQuestion(questionId: string): void {
|
|||||||
|
|
||||||
// ── Pending Approvals ──
|
// ── Pending Approvals ──
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a pending approval row. Idempotent for the same reason as
|
||||||
|
* createPendingQuestion: delivery retries with the same approval_id must not
|
||||||
|
* fail on UNIQUE before the send step gets a chance to succeed.
|
||||||
|
*/
|
||||||
export function createPendingApproval(
|
export function createPendingApproval(
|
||||||
pa: Partial<PendingApproval> &
|
pa: Partial<PendingApproval> &
|
||||||
Pick<
|
Pick<
|
||||||
PendingApproval,
|
PendingApproval,
|
||||||
'approval_id' | 'request_id' | 'action' | 'payload' | 'created_at' | 'title' | 'options_json'
|
'approval_id' | 'request_id' | 'action' | 'payload' | 'created_at' | 'title' | 'options_json'
|
||||||
>,
|
>,
|
||||||
): void {
|
): boolean {
|
||||||
getDb()
|
const result = getDb()
|
||||||
.prepare(
|
.prepare(
|
||||||
`INSERT INTO pending_approvals
|
`INSERT OR IGNORE INTO pending_approvals
|
||||||
(approval_id, session_id, request_id, action, payload, created_at,
|
(approval_id, session_id, request_id, action, payload, created_at,
|
||||||
agent_group_id, channel_type, platform_id, platform_message_id, expires_at, status,
|
agent_group_id, channel_type, platform_id, platform_message_id, expires_at, status,
|
||||||
title, options_json)
|
title, options_json)
|
||||||
@@ -159,6 +171,7 @@ export function createPendingApproval(
|
|||||||
status: 'pending',
|
status: 'pending',
|
||||||
...pa,
|
...pa,
|
||||||
});
|
});
|
||||||
|
return result.changes > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPendingApproval(approvalId: string): PendingApproval | undefined {
|
export function getPendingApproval(approvalId: string): PendingApproval | undefined {
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ async function deliverMessage(
|
|||||||
questionId: content.questionId,
|
questionId: content.questionId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
createPendingQuestion({
|
const inserted = createPendingQuestion({
|
||||||
question_id: content.questionId,
|
question_id: content.questionId,
|
||||||
session_id: session.id,
|
session_id: session.id,
|
||||||
message_out_id: msg.id,
|
message_out_id: msg.id,
|
||||||
@@ -332,7 +332,9 @@ async function deliverMessage(
|
|||||||
options: normalizeOptions(rawOptions as never),
|
options: normalizeOptions(rawOptions as never),
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
log.info('Pending question created', { questionId: content.questionId, sessionId: session.id });
|
if (inserted) {
|
||||||
|
log.info('Pending question created', { questionId: content.questionId, sessionId: session.id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user