fix(quota): resume duplicate cloud webhooks

This commit is contained in:
saltbo
2026-08-03 05:33:03 -04:00
parent 06dc7c2479
commit 0b997e52bf
2 changed files with 48 additions and 28 deletions
+30 -6
View File
@@ -40,6 +40,16 @@ function createAsyncDb(
if (table === orgQuotaEntitlements) state.entitlementInserts += Array.isArray(values) ? values.length : 1
}
if (table === orgQuotaEntitlements) return { onConflictDoUpdate: async () => apply() }
if (table === webhookEvents) {
return {
onConflictDoNothing: () => ({
returning: async () => {
apply()
return [{ id: values.id }]
},
}),
}
}
return Promise.resolve(apply())
},
}),
@@ -75,9 +85,13 @@ function createAsyncDb(
function createFailingBeginDb() {
return {
insert: () => ({
values: async () => {
throw new Error('insert failed')
},
values: () => ({
onConflictDoNothing: () => ({
returning: async () => {
throw new Error('insert failed')
},
}),
}),
}),
} as unknown as Database
}
@@ -100,11 +114,13 @@ function createUniqueConflictDb(existing: { id: string; payloadHash: string; sta
return Promise.all(queries)
},
insert: (table: unknown) => ({
values: async (values: Record<string, unknown>) => {
if (table === webhookEvents) throw new Error('UNIQUE constraint failed: webhook_events.source, event_id')
values: (values: Record<string, unknown>) => {
if (table === webhookEvents) {
return { onConflictDoNothing: () => ({ returning: async () => [] }) }
}
if (table === auditEvents) state.audits += 1
if (table === orgQuotaEntitlements) state.entitlementInserts += Array.isArray(values) ? values.length : 1
return values
return Promise.resolve(values)
},
}),
select: () => ({
@@ -163,6 +179,14 @@ function createSyncDb(
return {
run: apply,
onConflictDoUpdate: () => ({ run: apply }),
onConflictDoNothing: () => ({
returning: () => ({
all: () => {
apply()
return [{ id: values.id }]
},
}),
}),
}
},
}
+18 -22
View File
@@ -273,22 +273,24 @@ async function beginWebhookEvent(
payloadHash: string,
): Promise<{ id: string; duplicate: boolean }> {
const id = nanoid()
try {
await db.insert(webhookEvents).values({
id,
source: 'cloud',
eventId: event.eventId,
eventType: event.eventType,
payloadHash,
rawPayload,
status: 'processing',
createdAt: new Date(),
})
return { id, duplicate: false }
} catch (error) {
if (isUniqueConflict(error)) return resumeWebhookEvent(db, event, rawPayload, payloadHash)
throw error
}
const inserted = await executeRows(
db
.insert(webhookEvents)
.values({
id,
source: 'cloud',
eventId: event.eventId,
eventType: event.eventType,
payloadHash,
rawPayload,
status: 'processing',
createdAt: new Date(),
})
.onConflictDoNothing({ target: [webhookEvents.source, webhookEvents.eventId] })
.returning({ id: webhookEvents.id }),
)
if (inserted[0]) return { id: inserted[0].id, duplicate: false }
return resumeWebhookEvent(db, event, rawPayload, payloadHash)
}
async function resumeWebhookEvent(
@@ -333,12 +335,6 @@ function parseOrgType(metadata: string | null): string {
return (JSON.parse(metadata) as { type?: string }).type ?? 'unknown'
}
function isUniqueConflict(error: unknown): boolean {
if (!(error instanceof Error)) return false
const message = error.message.toLowerCase()
return message.includes('unique') || message.includes('constraint failed')
}
export function createCloudStoreRepo(db: Database): CloudStoreRepo {
return {
getAccessibleTargets: (userId) => getAccessibleTargets(db, userId),