Skip to content

Commit 9e8b74a

Browse files
sshaderConvex, Inc.
authored andcommitted
Update system UDFs to account for _cron_next_run table (#36386)
We migrated the backend tables but didn't update the system UDFs or dashboard UI that reads from them. GitOrigin-RevId: cb1287e72823d9e2e8d667088d2c88f7c208fcfe
1 parent 3a1e2e8 commit 9e8b74a

File tree

7 files changed

+68
-43
lines changed

7 files changed

+68
-43
lines changed

npm-packages/dashboard-common/src/features/schedules/components/crons/CronJobsContent.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import Link from "next/link";
1313
import { useRouter } from "next/router";
1414
import React, { useEffect, useRef, useState } from "react";
1515
import {
16-
CronJob,
1716
CronJobLog,
17+
CronJobWithRuns,
1818
} from "system-udfs/convex/_system/frontend/common";
1919
import { FileModal } from "@common/features/schedules/components/crons/FileModal";
2020
import { CronsTable } from "@common/features/schedules/components/crons/CronsTable";
@@ -92,15 +92,15 @@ function Details({
9292
cronJob,
9393
cronJobRuns,
9494
}: {
95-
cronJob: CronJob;
95+
cronJob: CronJobWithRuns;
9696
cronJobRuns: CronJobLog[];
9797
}) {
9898
const router = useRouter();
9999
const back = () => {
100100
delete router.query.id;
101101
void router.push({ query: router.query });
102102
};
103-
const currentlyRunning = cronJob.state.type === "inProgress";
103+
const currentlyRunning = cronJob.nextRun?.state.type === "inProgress";
104104

105105
return (
106106
<div className="flex h-full w-full max-w-6xl flex-col gap-4">
@@ -193,13 +193,19 @@ function CronJobLogListItem({ cronJobLog }: { cronJobLog: CronJobLog }) {
193193
/**
194194
* The next scheduled execution, or the currently running execution.
195195
*/
196-
export function TopCronJobLogListItem({ cronJob }: { cronJob: CronJob }) {
196+
export function TopCronJobLogListItem({
197+
cronJob,
198+
}: {
199+
cronJob: CronJobWithRuns;
200+
}) {
197201
const url = useFunctionUrl(cronJob.cronSpec.udfPath);
198202

199-
const timestamp = formatDateTime(
200-
new Date(Number(cronJob.nextTs / BigInt(1000000))),
201-
);
202-
const currentlyRunning = cronJob.state.type === "inProgress";
203+
const { nextRun } = cronJob;
204+
const nextTs = nextRun?.nextTs;
205+
const timestamp = nextTs
206+
? formatDateTime(new Date(Number(nextTs / BigInt(1000000))))
207+
: null;
208+
const currentlyRunning = nextRun?.state.type === "inProgress";
203209

204210
// Make a quickly-updating timer to make function execution feel fast.
205211
// To avoid a React render every frame (often fine but can gum things up),
@@ -210,7 +216,7 @@ export function TopCronJobLogListItem({ cronJob }: { cronJob: CronJob }) {
210216
let handle = 0;
211217
const update = () => {
212218
if (estRuntimeRef.current) {
213-
const start = new Date(Number(cronJob.nextTs) / 1000000);
219+
const start = new Date(Number(nextTs) / 1000000);
214220
const s = msFormat(Date.now() - +start);
215221
estRuntimeRef.current.textContent = s;
216222
requestAnimationFrame(update);
@@ -219,7 +225,7 @@ export function TopCronJobLogListItem({ cronJob }: { cronJob: CronJob }) {
219225
handle = requestAnimationFrame(update);
220226
return () => cancelAnimationFrame(handle);
221227
}
222-
}, [currentlyRunning, cronJob.nextTs]);
228+
}, [currentlyRunning, nextTs]);
223229

224230
const textColor = currentlyRunning
225231
? "text-content-primary"
@@ -228,7 +234,9 @@ export function TopCronJobLogListItem({ cronJob }: { cronJob: CronJob }) {
228234
<div className="flex items-start gap-4 font-mono text-xs">
229235
<div className="flex flex-col gap-2">
230236
<div className="flex h-6 items-center gap-4">
231-
<div className={`${textColor} whitespace-nowrap`}>{timestamp}</div>
237+
<div className={`${textColor} whitespace-nowrap`}>
238+
{timestamp ?? "unknown"}
239+
</div>
232240
<div
233241
className={`${textColor} w-14 whitespace-nowrap text-right text-content-secondary`}
234242
>

npm-packages/dashboard-common/src/features/schedules/components/crons/CronsTable.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ChevronRightIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
88
import {
99
CronSchedule,
1010
CronJobLog,
11-
CronJobWithLastRun,
11+
CronJobWithRuns,
1212
} from "system-udfs/convex/_system/frontend/common";
1313
import { useWasmCron } from "@common/features/schedules/lib/useWasmCron";
1414
import {
@@ -26,6 +26,7 @@ import { Button } from "@common/elements/Button";
2626
import { DetailPanel } from "@common/elements/DetailPanel";
2727
import { ReadonlyCode } from "@common/elements/ReadonlyCode";
2828
import { Sheet } from "@common/elements/Sheet";
29+
import { Doc } from "system-udfs/convex/_generated/dataModel";
2930

3031
const COLUMN_STYLES = [
3132
{ fontWeight: "500", flex: "2 0 80px", fontSize: "0.875rem" },
@@ -46,7 +47,10 @@ function Name({ value }: CellProps<CronDatum, string>) {
4647

4748
function Schedule({
4849
value: { schedule },
49-
}: CellProps<CronDatum, { schedule: CronSchedule; nextDate: Date }>) {
50+
}: CellProps<
51+
CronDatum,
52+
{ schedule: CronSchedule; nextDate: Date | undefined }
53+
>) {
5054
const literal = scheduleLiteral(schedule);
5155

5256
let formattedSchedule = "";
@@ -129,17 +133,17 @@ function PrevNextTs({
129133
}: CellProps<
130134
CronDatum,
131135
{
132-
nextDate: Date;
136+
nextDate: Date | undefined;
133137
prevDate: Date;
134138
prevRun: CronJobLog | undefined;
135-
state: CronJobWithLastRun["state"];
139+
nextRun: Doc<"_cron_next_run"> | undefined;
136140
}
137141
>) {
138-
const isRunning = value.state.type === "inProgress";
142+
const isRunning = value.nextRun?.state.type === "inProgress";
139143
return (
140144
<div className="flex flex-col truncate">
141145
<PrevTs date={value.prevDate} isRunning={isRunning} run={value.prevRun} />
142-
<NextTs value={value.nextDate} />
146+
{value.nextDate && <NextTs value={value.nextDate} />}
143147
</div>
144148
);
145149
}
@@ -202,18 +206,20 @@ function Args({ value }: CellProps<CronDatum, JSONValue[]>) {
202206
);
203207
}
204208

205-
function cronDatum(cronJob: CronJobWithLastRun) {
206-
const { name, cronSpec, nextTs, prevTs, lastRun, state } = cronJob;
207-
const nextDate = new Date(Number(nextTs / BigInt("1000000")));
208-
const prevDate = prevTs && new Date(Number(prevTs / BigInt("1000000")));
209+
function cronDatum(cronJob: CronJobWithRuns) {
210+
const { name, cronSpec, lastRun, nextRun } = cronJob;
211+
const nextDate = nextRun
212+
? new Date(Number(nextRun.nextTs / BigInt("1000000")))
213+
: undefined;
214+
const prevDate = lastRun && new Date(Number(lastRun.ts / BigInt("1000000")));
209215
return {
210216
name,
211217
schedule: { schedule: cronSpec.cronSchedule, nextDate },
212218
prevNextTs: {
213219
prevDate,
214220
nextDate,
215221
prevRun: lastRun,
216-
state,
222+
state: nextRun?.state,
217223
},
218224
udfPath: cronSpec.udfPath,
219225
udfArgs:
@@ -225,7 +231,7 @@ function cronDatum(cronJob: CronJobWithLastRun) {
225231
}
226232
type CronDatum = ReturnType<typeof cronDatum>;
227233

228-
export function CronsTable({ cronJobs }: { cronJobs: CronJobWithLastRun[] }) {
234+
export function CronsTable({ cronJobs }: { cronJobs: CronJobWithRuns[] }) {
229235
const columns = useMemo(
230236
() =>
231237
[

npm-packages/dashboard-common/src/features/schedules/lib/CronsProvider.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import udfs from "@common/udfs";
44
import {
55
CronSpec,
66
Module,
7-
CronJobWithLastRun,
7+
CronJobWithRuns,
88
CronJobLog,
99
} from "system-udfs/convex/_system/frontend/common";
1010
import { useInMemoryDocumentCache } from "@common/features/schedules/lib/useInMemoryDocumentCache";
@@ -15,7 +15,7 @@ import { DeploymentInfoContext } from "@common/lib/deploymentContext";
1515

1616
type CronJobsContextType = {
1717
cronsModule: Module | undefined;
18-
cronJobs: CronJobWithLastRun[] | undefined;
18+
cronJobs: CronJobWithRuns[] | undefined;
1919
loading: boolean;
2020
cronJobRuns: CronJobLog[] | undefined;
2121
};
@@ -34,7 +34,7 @@ export function CronJobsProviderWithCronHistory({
3434
// Get functions
3535
const modules = useListModules();
3636
// Get cron jobs
37-
const cronJobs: CronJobWithLastRun[] | undefined = useQuery(
37+
const cronJobs: CronJobWithRuns[] | undefined = useQuery(
3838
udfs.listCronJobs.default,
3939
{ componentId: useNents().selectedNent?.id || null },
4040
);
@@ -48,7 +48,7 @@ export function CronJobsProviderWithCronHistory({
4848

4949
// This might be a new typed (source mapped cron jobs) in the future.
5050
const [orderedCronJobs, cronsModule]: [
51-
CronJobWithLastRun[] | undefined,
51+
CronJobWithRuns[] | undefined,
5252
Module | undefined,
5353
] = useMemo(() => {
5454
if (!cronJobs || !modules || !cronJobRuns) return [undefined, undefined];
@@ -74,7 +74,7 @@ export function CronJobsProviderWithCronHistory({
7474
}
7575
if (!cronSpecs) return [undefined, cronsModuleInner];
7676

77-
const cronJobsMap = new Map<string, CronJobWithLastRun>();
77+
const cronJobsMap = new Map<string, CronJobWithRuns>();
7878
for (const cronJob of cronJobs) {
7979
cronJobsMap.set(cronJob.name, cronJob);
8080
}

npm-packages/dashboard/src/generatedApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1632,7 +1632,7 @@ export interface components {
16321632
warningThresholdCents?: number | null;
16331633
};
16341634
GetTokenInfoResponse: {
1635-
isDisabled: boolean;
1635+
isTeamDisabled: boolean;
16361636
/** Format: int64 */
16371637
tokensQuota: number;
16381638
/** Format: int64 */

npm-packages/system-udfs/convex/_system/frontend/common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ export type CronJob = Doc<"_cron_jobs">;
9696
export type CronSpec = Doc<"_cron_jobs">["cronSpec"];
9797
export type CronSchedule = Doc<"_cron_jobs">["cronSpec"]["cronSchedule"];
9898
export type CronJobLog = Doc<"_cron_job_logs">;
99-
export type CronJobWithLastRun = Doc<"_cron_jobs"> & {
99+
export type CronJobWithRuns = Doc<"_cron_jobs"> & {
100100
// only undefined while feature-flagged (but still might be null)
101101
lastRun: CronJobLog | null | undefined;
102+
nextRun: Doc<"_cron_next_run"> | null | undefined;
102103
};
103104

104105
export type Modules = Map<string, Module>;
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1-
import { CronJob, CronJobWithLastRun } from "./common";
1+
import { CronJob, CronJobWithRuns } from "./common";
22
import { queryPrivateSystem } from "../secretSystemTables";
33
import { v } from "convex/values";
44
export default queryPrivateSystem({
55
args: { componentId: v.optional(v.union(v.string(), v.null())) },
6-
handler: async ({ db }): Promise<CronJobWithLastRun[]> => {
6+
handler: async ({ db }): Promise<CronJobWithRuns[]> => {
77
const jobs: CronJob[] = await db.query("_cron_jobs").collect();
8-
const jobsWithLastRun: CronJobWithLastRun[] = [];
8+
const jobsWithRuns: CronJobWithRuns[] = [];
99

1010
for (const job of jobs) {
1111
const lastRun = await db
1212
.query("_cron_job_logs")
1313
.withIndex("by_name_and_ts", (q) => q.eq("name", job.name))
1414
.order("desc")
1515
.first();
16-
jobsWithLastRun.push({
16+
const nextRun = await db
17+
.query("_cron_next_run")
18+
.withIndex("by_cron_job_id", (q) => q.eq("cronJobId", job._id))
19+
.first();
20+
jobsWithRuns.push({
1721
...job,
18-
lastRun: lastRun || null,
22+
lastRun: lastRun,
23+
nextRun: nextRun,
1924
});
2025
}
2126

22-
return jobsWithLastRun;
27+
return jobsWithRuns;
2328
},
2429
});

npm-packages/system-udfs/convex/schema.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ const backendStateTable = defineTable({
241241
state: deploymentState,
242242
});
243243

244+
export const cronJobState = v.union(
245+
v.object({ type: v.literal("pending") }),
246+
v.object({ type: v.literal("inProgress") }),
247+
);
248+
244249
export default defineSchema({
245250
_tables: defineTable({
246251
name: v.string(),
@@ -319,15 +324,15 @@ export default defineSchema({
319324
_cron_jobs: defineTable({
320325
name: v.string(),
321326
cronSpec: analyzedCronSpec,
322-
state: v.union(
323-
v.object({ type: v.literal("pending") }),
324-
v.object({ type: v.literal("inProgress") }),
325-
),
326-
nextTs: v.int64(),
327+
}).index("by_name", ["name"]),
328+
_cron_next_run: defineTable({
329+
cronJobId: v.id("_cron_jobs"),
330+
state: cronJobState,
327331
prevTs: v.union(v.int64(), v.null()),
332+
nextTs: v.int64(),
328333
})
329-
.index("by_next_ts", ["nextTs"])
330-
.index("by_name", ["name"]),
334+
.index("by_cron_job_id", ["cronJobId"])
335+
.index("by_next_ts", ["nextTs"]),
331336
_cron_job_logs: defineTable({
332337
name: v.string(),
333338
ts: v.int64(),

0 commit comments

Comments
 (0)