Skip to content

Commit 4c42a5f

Browse files
committed
scheduled background functions
1 parent 5b17ed5 commit 4c42a5f

File tree

5 files changed

+40
-281
lines changed

5 files changed

+40
-281
lines changed

netlify.toml

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,4 @@ command = "vite build"
33
publish = "dist/client"
44

55
[functions]
6-
directory = "netlify/functions"
7-
8-
# Scheduled + Background Function Pattern
9-
#
10-
# The pattern for long-running cron jobs:
11-
# 1. Scheduled function (30s timeout) runs on cron schedule
12-
# 2. It invokes the background function via HTTP
13-
# 3. Returns immediately (202)
14-
# 4. Background function (15 min timeout) does the actual work
15-
#
16-
# Background functions are identified by the `-background` suffix in the filename.
17-
# They can also be invoked manually from the admin panel.
18-
19-
# Scheduled function: Stats cache refresh (runs every 6 hours)
20-
[functions."scheduled/refresh-stats-cache"]
21-
schedule = "0 */6 * * *"
22-
23-
# Scheduled function: GitHub release sync (runs every 6 hours)
24-
[functions."scheduled/sync-github-releases"]
25-
schedule = "0 */6 * * *"
26-
27-
# Background Functions (invoked by scheduled functions or manually)
28-
#
29-
# 1. refresh-stats-cache-background.ts
30-
# - Refreshes NPM and GitHub stats cache for the entire TanStack org
31-
# - ~10-20 minutes to complete (200+ packages)
32-
# - Invoked by: scheduled/refresh-stats-cache
33-
# - Manual: POST /.netlify/functions/refresh-stats-cache-background
34-
# - Authorization: Bearer YOUR_CRON_SECRET
35-
#
36-
# 2. sync-github-releases-background.ts
37-
# - Syncs GitHub releases from all TanStack repos to the feed
38-
# - ~1-5 minutes to complete
39-
# - Invoked by: scheduled/sync-github-releases
40-
# - Manual: POST /.netlify/functions/sync-github-releases-background
41-
# - Authorization: Bearer YOUR_CRON_SECRET
42-
#
43-
# Note: Both functions require CRON_SECRET environment variable for authentication
6+
directory = "netlify/functions"

netlify/functions/refresh-stats-cache-background.ts

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@ import {
77
import { setCachedGitHubStats } from '~/utils/stats-db.server'
88

99
/**
10-
* Netlify Background Function - Refresh org-level stats cache
10+
* Netlify Background + Scheduled Function - Refresh org-level stats cache
1111
*
12-
* Background functions are identified by the `-background` suffix in the filename.
13-
* They have a 15-minute timeout (vs 30 seconds for regular functions) and return
14-
* a 202 response immediately while processing continues in the background.
12+
* This function combines both background and scheduled execution:
13+
* - Can be invoked via HTTP POST (returns 202 immediately, runs in background)
14+
* - Can be triggered on a schedule (runs automatically via cron)
15+
*
16+
* Background functions have a 15-minute timeout (vs 30 seconds for regular functions)
17+
* and return a 202 response immediately while processing continues in the background.
1518
*
1619
* This function refreshes pre-aggregated stats for the TanStack organization:
1720
* - GitHub: stars, contributors, dependents
1821
* - NPM: total downloads across all packages (including legacy packages)
1922
*
20-
* Invocation:
21-
* POST https://tanstack.com/.netlify/functions/refresh-stats-cache-background
22-
* Authorization: Bearer YOUR_CRON_SECRET
23-
*
24-
* Can be triggered manually from the admin panel.
23+
* Scheduled: Runs automatically every 6 hours (configured via export config)
24+
* Only called by Netlify's scheduler - not publicly accessible
2525
* Timeout: 15 minutes
2626
* Concurrency: 8 packages at a time, 500ms delay between packages
2727
*
@@ -33,36 +33,7 @@ export const handler: Handler = async (
3333
event: HandlerEvent,
3434
context: HandlerContext
3535
) => {
36-
console.log(
37-
'[refresh-stats-cache-background] Starting background stats refresh...'
38-
)
39-
40-
// Check authentication
41-
const cronSecret = process.env.CRON_SECRET
42-
const authHeader = event.headers.authorization || event.headers.Authorization
43-
44-
if (!cronSecret) {
45-
console.error('[refresh-stats-cache-background] CRON_SECRET not configured')
46-
return {
47-
statusCode: 500,
48-
body: JSON.stringify({
49-
error: 'CRON_SECRET not configured',
50-
}),
51-
}
52-
}
53-
54-
// Extract token from "Bearer TOKEN" format
55-
const providedSecret = authHeader?.replace(/^Bearer\s+/i, '')
56-
57-
if (providedSecret !== cronSecret) {
58-
console.error('[refresh-stats-cache-background] Invalid authentication')
59-
return {
60-
statusCode: 401,
61-
body: JSON.stringify({
62-
error: 'Invalid authentication',
63-
}),
64-
}
65-
}
36+
console.log('[refresh-stats-cache-background] Starting stats refresh...')
6637

6738
const startTime = Date.now()
6839

@@ -200,3 +171,13 @@ export const handler: Handler = async (
200171
}
201172
}
202173
}
174+
175+
/**
176+
* Netlify function configuration
177+
* - type: 'experimental-background' enables background execution (15 min timeout, returns 202 immediately)
178+
* - schedule: Cron expression for scheduled execution (runs every 6 hours)
179+
*/
180+
export const config = {
181+
type: 'experimental-background' as const,
182+
schedule: '0 */6 * * *', // Every 6 hours
183+
}

netlify/functions/scheduled/refresh-stats-cache.ts

Lines changed: 0 additions & 82 deletions
This file was deleted.

netlify/functions/scheduled/sync-github-releases.ts

Lines changed: 0 additions & 84 deletions
This file was deleted.

netlify/functions/sync-github-releases-background.ts

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
22
import { syncGitHubReleases } from '~/server/feed/github.functions'
33

44
/**
5-
* Netlify Background Function - Sync GitHub releases
5+
* Netlify Background + Scheduled Function - Sync GitHub releases
66
*
7-
* Background functions are identified by the `-background` suffix in the filename.
8-
* They have a 15-minute timeout (vs 30 seconds for regular functions) and return
9-
* a 202 response immediately while processing continues in the background.
7+
* This function combines both background and scheduled execution:
8+
* - Can be invoked via HTTP POST (returns 202 immediately, runs in background)
9+
* - Can be triggered on a schedule (runs automatically via cron)
10+
*
11+
* Background functions have a 15-minute timeout (vs 30 seconds for regular functions)
12+
* and return a 202 response immediately while processing continues in the background.
1013
*
1114
* This function syncs GitHub releases from all TanStack repos:
1215
* - Fetches new releases since last sync
1316
* - Creates/updates feed items in the database
1417
* - Marks releases as synced to avoid duplicates
1518
*
16-
* Invocation:
17-
* POST https://tanstack.com/.netlify/functions/sync-github-releases-background
18-
* Authorization: Bearer YOUR_CRON_SECRET
19-
*
20-
* Triggered by: .github/workflows/sync-github-releases.yml (hourly cron)
19+
* Scheduled: Runs automatically every 6 hours (configured via export config)
20+
* Only called by Netlify's scheduler - not publicly accessible
2121
* Timeout: 15 minutes
2222
*/
2323
export const handler: Handler = async (
@@ -28,35 +28,6 @@ export const handler: Handler = async (
2828
'[sync-github-releases-background] Starting GitHub release sync...'
2929
)
3030

31-
// Check authentication
32-
const cronSecret = process.env.CRON_SECRET
33-
const authHeader = event.headers.authorization || event.headers.Authorization
34-
35-
if (!cronSecret) {
36-
console.error(
37-
'[sync-github-releases-background] CRON_SECRET not configured'
38-
)
39-
return {
40-
statusCode: 500,
41-
body: JSON.stringify({
42-
error: 'CRON_SECRET not configured',
43-
}),
44-
}
45-
}
46-
47-
// Extract token from "Bearer TOKEN" format
48-
const providedSecret = authHeader?.replace(/^Bearer\s+/i, '')
49-
50-
if (providedSecret !== cronSecret) {
51-
console.error('[sync-github-releases-background] Invalid authentication')
52-
return {
53-
statusCode: 401,
54-
body: JSON.stringify({
55-
error: 'Invalid authentication',
56-
}),
57-
}
58-
}
59-
6031
const startTime = Date.now()
6132

6233
try {
@@ -103,3 +74,13 @@ export const handler: Handler = async (
10374
}
10475
}
10576
}
77+
78+
/**
79+
* Netlify function configuration
80+
* - type: 'experimental-background' enables background execution (15 min timeout, returns 202 immediately)
81+
* - schedule: Cron expression for scheduled execution (runs every 6 hours)
82+
*/
83+
export const config = {
84+
type: 'experimental-background' as const,
85+
schedule: '0 */6 * * *', // Every 6 hours
86+
}

0 commit comments

Comments
 (0)