Skip to content

Commit 626dafa

Browse files
committed
fix(db): fix slow db connection, add redis cache for gmail polling
1 parent 2c7806f commit 626dafa

File tree

4 files changed

+179
-149
lines changed

4 files changed

+179
-149
lines changed
Lines changed: 27 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,23 @@
11
import { NextRequest, NextResponse } from 'next/server'
22
import { nanoid } from 'nanoid'
33
import { Logger } from '@/lib/logs/console-logger'
4+
import { acquireLock, releaseLock } from '@/lib/redis'
45
import { pollGmailWebhooks } from '@/lib/webhooks/gmail-polling-service'
56

67
const logger = new Logger('GmailPollingAPI')
78

89
export const dynamic = 'force-dynamic'
9-
export const maxDuration = 300 // Allow up to 5 minutes for polling to complete
10+
export const maxDuration = 180 // Allow up to 3 minutes for polling to complete
1011

11-
interface PollingTask {
12-
promise: Promise<any>
13-
startedAt: number
14-
}
15-
16-
const activePollingTasks = new Map<string, PollingTask>()
17-
const STALE_TASK_THRESHOLD_MS = 10 * 60 * 1000 // 10 minutes
18-
19-
function cleanupStaleTasks() {
20-
const now = Date.now()
21-
let removedCount = 0
22-
23-
for (const [requestId, task] of activePollingTasks.entries()) {
24-
if (now - task.startedAt > STALE_TASK_THRESHOLD_MS) {
25-
activePollingTasks.delete(requestId)
26-
removedCount++
27-
}
28-
}
29-
30-
if (removedCount > 0) {
31-
logger.info(`Cleaned up ${removedCount} stale polling tasks`)
32-
}
33-
34-
return removedCount
35-
}
12+
const LOCK_KEY = 'gmail-polling-lock'
13+
const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min)
3614

3715
export async function GET(request: NextRequest) {
3816
const requestId = nanoid()
3917
logger.info(`Gmail webhook polling triggered (${requestId})`)
4018

19+
let lockValue: string | undefined
20+
4121
try {
4222
const authHeader = request.headers.get('authorization')
4323
const webhookSecret = process.env.CRON_SECRET || process.env.WEBHOOK_POLLING_SECRET
@@ -51,49 +31,42 @@ export async function GET(request: NextRequest) {
5131
return new NextResponse('Unauthorized', { status: 401 })
5232
}
5333

54-
cleanupStaleTasks()
55-
56-
const pollingTask: PollingTask = {
57-
promise: null as any,
58-
startedAt: Date.now(),
34+
lockValue = requestId // unique value to identify the holder
35+
const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS)
36+
37+
if (!locked) {
38+
return NextResponse.json(
39+
{
40+
success: true,
41+
message: 'Polling already in progress – skipped',
42+
requestId,
43+
status: 'skip',
44+
},
45+
{ status: 202 }
46+
)
5947
}
6048

61-
pollingTask.promise = pollGmailWebhooks()
62-
.then((results) => {
63-
logger.info(`Gmail polling completed successfully (${requestId})`, {
64-
userCount: results?.total || 0,
65-
successful: results?.successful || 0,
66-
failed: results?.failed || 0,
67-
})
68-
activePollingTasks.delete(requestId)
69-
return results
70-
})
71-
.catch((error) => {
72-
logger.error(`Error in background Gmail polling task (${requestId}):`, error)
73-
activePollingTasks.delete(requestId)
74-
throw error
75-
})
76-
77-
activePollingTasks.set(requestId, pollingTask)
49+
const results = await pollGmailWebhooks()
7850

7951
return NextResponse.json({
8052
success: true,
81-
message: 'Gmail webhook polling started successfully',
53+
message: 'Gmail polling completed',
8254
requestId,
83-
status: 'polling_started',
84-
activeTasksCount: activePollingTasks.size,
55+
status: 'completed',
56+
...results,
8557
})
8658
} catch (error) {
87-
logger.error(`Error initiating Gmail webhook polling (${requestId}):`, error)
88-
59+
logger.error(`Error during Gmail polling (${requestId}):`, error)
8960
return NextResponse.json(
9061
{
9162
success: false,
92-
message: 'Failed to start Gmail webhook polling',
63+
message: 'Gmail polling failed',
9364
error: error instanceof Error ? error.message : 'Unknown error',
9465
requestId,
9566
},
9667
{ status: 500 }
9768
)
69+
} finally {
70+
await releaseLock(LOCK_KEY).catch(() => {})
9871
}
9972
}

apps/sim/drizzle.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import * as dotenv from 'dotenv'
12
import type { Config } from 'drizzle-kit'
23

4+
dotenv.config({ path: '../../.env' })
5+
36
export default {
47
schema: './db/schema.ts',
58
out: './db/migrations',

0 commit comments

Comments
 (0)