I am really confused about cronExpressions. I have defined them like
await client.workflow.start(workflows.trackDeploymentsUsage, {
cronSchedule: '* * * * *',
workflowId: 'track-usage',
workflowIdConflictPolicy: WorkflowIdConflictPolicy.TERMINATE_EXISTING,
args: [],
taskQueue: `billing`,
});
In the UI they seem to be implemented like this:
The event history is really useless. It seems to be implemented like:
- Wait until the next time it should run.
- Execute the activities
- Create a new workflow run with the same settings.
- Continue with 1.
This makes the whole event history basically useless.
I have implemented then a coordinator which looks like this
import { WorkflowExecutionAlreadyStartedError } from '@temporalio/common';
import { executeChild, WorkflowIdReusePolicy } from '@temporalio/workflow';
import { todayUtcDate } from 'src/lib/time';
import { trackDeploymentsUsage } from './track-deployments-usage';
export async function trackDeploymentsUsageCoordinator(): Promise<void> {
const trackDate = todayUtcDate();
const trackHour = new Date().getUTCHours();
await executeChild(trackDeploymentsUsage, {
workflowId: `track-usage-${trackDate}-update-${trackHour}`,
args: [
{
trackDate,
trackHour,
},
],
});
}
function is<TClass>(x: any, c: new (...args: any[]) => TClass): x is TClass {
return x instanceof c;
}
This makes it already a little bit better, because now I have a concrete entry in the UI with something that is useful:
But now I get two entries each time. The coordinator and the child flow. I think it might actually be easier to implement the scheduling in a coordinator myself.
So I build this
export async function trackDeploymentsUsageCoordinator(): Promise<void> {
const cronExpression = parseCronExpression('*/5 * * * *');
while (true) {
try {
await executeChild(trackDeploymentsUsage, {
workflowId: `track-usage-${trackDate}-update-${trackHour}`,
args: [],
});
} catch (ex) {
log.error('Failed to execute child workflow', { ex });
}
const now = new Date();
const next = cronExpression.getNextDate(now);
const delayMs = next.getTime() - now.getTime();
if (delayMs > 0) {
await sleep(delayMs);
}
}
}
Then you get this useful view:
I am not sure if I make something wrong. But it is really confusing.