Skip to content

Commit 85a164e

Browse files
Use Workflow runtime deadlines for intent processing (#1054)
Use workflow runtime deadlines for intent processing
1 parent 3b4add9 commit 85a164e

8 files changed

Lines changed: 155 additions & 113 deletions

File tree

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@
7474
"@tanstack/react-start": "1.168.26",
7575
"@tanstack/react-start-client": "1.168.14",
7676
"@tanstack/react-table": "^8.21.3",
77-
"@tanstack/workflow-core": "0.0.3",
78-
"@tanstack/workflow-runtime": "0.0.2",
79-
"@tanstack/workflow-store-drizzle-postgres": "0.0.4",
77+
"@tanstack/workflow-core": "0.0.4",
78+
"@tanstack/workflow-runtime": "0.0.3",
79+
"@tanstack/workflow-store-drizzle-postgres": "0.0.5",
8080
"@types/d3": "^7.4.3",
8181
"@uploadthing/react": "^7.3.3",
8282
"@visx/hierarchy": "^3.12.0",

pnpm-lock.yaml

Lines changed: 24 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/routes/admin/intent.tsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function IntentAdminPage() {
180180
processMutation.isPending ||
181181
(stats?.pendingVersions ?? 0) + (stats?.failedVersions ?? 0) === 0
182182
}
183-
title="Download tarballs and extract skills until the workflow nears its time budget"
183+
title="Download tarballs and extract skills for pending versions"
184184
>
185185
<Play
186186
className={
@@ -249,21 +249,29 @@ function IntentAdminPage() {
249249
)}
250250
{processMutation.data && (
251251
<ResultBanner
252-
title={`Processed ${processMutation.data.processed} version(s)`}
253-
items={[
254-
...(processMutation.data.deferred > 0
255-
? [
256-
`${processMutation.data.deferred} version(s) deferred by the time budget`,
257-
]
258-
: []),
259-
...processMutation.data.results.map(
260-
(r) =>
261-
`${r.packageName}@${r.version}: ${r.status === 'synced' ? `${r.skillCount} skills` : `FAILED — ${r.error}`}`,
262-
),
263-
]}
264-
errors={processMutation.data.results
265-
.filter((r) => r.status === 'failed')
266-
.map((r) => `${r.packageName}@${r.version}: ${r.error}`)}
252+
title={
253+
processMutation.data.kind === 'completed'
254+
? `Processed ${processMutation.data.summary.processed} version(s)`
255+
: 'Queue processing started'
256+
}
257+
items={
258+
processMutation.data.kind === 'completed'
259+
? processMutation.data.summary.results.map(
260+
(result) =>
261+
`${result.packageName}@${result.version}: ${result.status === 'synced' ? `${result.skillCount} skills` : `FAILED — ${result.error}`}`,
262+
)
263+
: ['Remaining work will continue during scheduled processing.']
264+
}
265+
errors={
266+
processMutation.data.kind === 'completed'
267+
? processMutation.data.summary.results
268+
.filter((result) => result.status === 'failed')
269+
.map(
270+
(result) =>
271+
`${result.packageName}@${result.version}: ${result.error}`,
272+
)
273+
: []
274+
}
267275
onDismiss={() => processMutation.reset()}
268276
/>
269277
)}

src/server/scheduled.server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import { refreshHomepageNpmStatsSummary } from '~/utils/homepage-npm-stats.serve
44
import { refreshGitHubOrgStats } from '~/utils/stats.functions'
55
import {
66
reconcileWorkflowRuntimeStore,
7+
WORKFLOW_RUNTIME_MAX_DURATION_MS,
8+
WORKFLOW_RUNTIME_MIN_REMAINING_MS,
79
workflowRuntime,
810
} from '~/utils/workflow-runtime.server'
911

1012
const CONTENT_CACHE_PRUNE_CRON = '0 9 * * *'
1113
const STATS_REFRESH_CRON = '0 */6 * * *'
1214
const WORKFLOW_SWEEP_CRON = '* * * * *'
13-
const WORKFLOW_SWEEP_MAX_DURATION_MS = 25_000
1415

1516
export async function runScheduledTasks(cron: string, scheduledTime: number) {
1617
switch (cron) {
@@ -43,7 +44,8 @@ async function runWorkflowSweep(cron: string, scheduledTime: number) {
4344
const sweep = await workflowRuntime.sweep({
4445
now: scheduledTime,
4546
leaseOwner: `cloudflare:${cron}:${scheduledTime}`,
46-
maxDurationMs: WORKFLOW_SWEEP_MAX_DURATION_MS,
47+
maxDurationMs: WORKFLOW_RUNTIME_MAX_DURATION_MS,
48+
minYieldRemainingMs: WORKFLOW_RUNTIME_MIN_REMAINING_MS,
4749
maxScheduledRuns: 10,
4850
maxTimers: 10,
4951
includeEvents: false,

src/utils/intent-admin.server.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {
3636
import {
3737
getWorkflowRuntimeHealth,
3838
reconcileWorkflowRuntimeStore,
39+
WORKFLOW_RUNTIME_MAX_DURATION_MS,
40+
WORKFLOW_RUNTIME_MIN_REMAINING_MS,
3941
workflowExecutionStore,
4042
workflowRuntime,
4143
} from '~/utils/workflow-runtime.server'
@@ -219,10 +221,25 @@ export async function triggerIntentProcess() {
219221
input: {
220222
source: 'admin',
221223
},
224+
maxDurationMs: WORKFLOW_RUNTIME_MAX_DURATION_MS,
225+
minYieldRemainingMs: WORKFLOW_RUNTIME_MIN_REMAINING_MS,
222226
includeEvents: false,
223227
})
224228

225-
return intentProcessResultSchema.parse(getCompletedWorkflowOutput(result))
229+
if (result.kind === 'paused') {
230+
return {
231+
kind: 'continuing' as const,
232+
runId: result.runId,
233+
}
234+
}
235+
236+
return {
237+
kind: 'completed' as const,
238+
runId: result.runId,
239+
summary: intentProcessResultSchema.parse(
240+
getCompletedWorkflowOutput(result),
241+
),
242+
}
226243
}
227244

228245
// ---------------------------------------------------------------------------

src/utils/intent-workflows.server.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,9 @@ const intentDiscoverInputSchema = z.object({
1616
source: z.enum(['schedule', 'admin']).default('schedule'),
1717
})
1818

19-
const PROCESS_WORKFLOW_DEFAULT_MAX_DURATION_MS = 4 * 60 * 1000
20-
const PROCESS_WORKFLOW_MAX_DURATION_MS = 4 * 60 * 1000
21-
const PROCESS_WORKFLOW_MIN_REMAINING_MS = 30_000
2219
const PROCESS_WORKFLOW_SELECT_LIMIT = 50
2320

2421
const intentProcessInputSchema = z.object({
25-
maxDurationMs: z
26-
.number()
27-
.int()
28-
.positive()
29-
.max(PROCESS_WORKFLOW_MAX_DURATION_MS)
30-
.default(PROCESS_WORKFLOW_DEFAULT_MAX_DURATION_MS),
3122
source: z.enum(['schedule', 'admin']).default('schedule'),
3223
})
3324

@@ -71,14 +62,11 @@ export function createIntentProcessWorkflow(
7162
id: INTENT_PROCESS_WORKFLOW_ID,
7263
input: intentProcessInputSchema,
7364
}).handler(async (ctx) => {
74-
const deadline = Date.now() + ctx.input.maxDurationMs
7565
const attemptedIds = new Set<number>()
7666
const results: Array<IntentVersionProcessResult> = []
77-
let deferred = 0
7867
let selectIteration = 0
79-
let shouldContinue = true
8068

81-
while (shouldContinue && hasProcessBudget(deadline)) {
69+
while (true) {
8270
const versions = await ctx.step(
8371
`select-pending-versions:${selectIteration}`,
8472
() =>
@@ -97,14 +85,7 @@ export function createIntentProcessWorkflow(
9785
)
9886
if (unattemptedVersions.length === 0) break
9987

100-
for (let index = 0; index < unattemptedVersions.length; index++) {
101-
if (!hasProcessBudget(deadline)) {
102-
deferred += unattemptedVersions.length - index
103-
shouldContinue = false
104-
break
105-
}
106-
107-
const version = unattemptedVersions[index]!
88+
for (const version of unattemptedVersions) {
10889
attemptedIds.add(version.id)
10990

11091
try {
@@ -128,7 +109,7 @@ export function createIntentProcessWorkflow(
128109
if (versions.length < PROCESS_WORKFLOW_SELECT_LIMIT) break
129110
}
130111

131-
return summarizeIntentProcessResults(results, { deferred })
112+
return summarizeIntentProcessResults(results)
132113
})
133114
}
134115

@@ -155,7 +136,6 @@ export const intentWorkflowRegistrations = {
155136
schedule: every.minutes(15),
156137
overlapPolicy: 'skip',
157138
input: {
158-
maxDurationMs: PROCESS_WORKFLOW_DEFAULT_MAX_DURATION_MS,
159139
source: 'schedule',
160140
},
161141
},
@@ -166,7 +146,3 @@ export const intentWorkflowRegistrations = {
166146
function getErrorMessage(error: unknown): string {
167147
return error instanceof Error ? error.message : String(error)
168148
}
169-
170-
function hasProcessBudget(deadline: number): boolean {
171-
return Date.now() + PROCESS_WORKFLOW_MIN_REMAINING_MS < deadline
172-
}

src/utils/workflow-runtime.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { intentWorkflowRegistrations } from '~/utils/intent-workflows.server'
1010

1111
export const workflowExecutionStore = createDrizzlePostgresWorkflowStore({ db })
1212

13+
export const WORKFLOW_RUNTIME_MAX_DURATION_MS = 25_000
14+
export const WORKFLOW_RUNTIME_MIN_REMAINING_MS = 5_000
15+
1316
export const workflowRuntime = defineWorkflowRuntime({
1417
store: workflowExecutionStore,
1518
workflows: {

0 commit comments

Comments
 (0)