I am so confused about cronExpressions

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:

  1. Wait until the next time it should run.
  2. Execute the activities
  3. Create a new workflow run with the same settings.
  4. 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.

Cron is a legacy feature. Use Schedules that are much more feature-rich.