Cron scheduler seems to throw a timeout

I created a workflow that is triggered every hour by the scheduler.

The workflow sometimes fails due to a timeout. It seems like the timeout is not thrown by any of my steps but instead by the scheduler component (see screenshot #1 below) that triggers the execution. None of the steps have been executed and the trigger step has an error icon attached.

UPDATE #1: it seems like the scheduler component also threw an Out of memory error once (cannot post a screenshot since I am limited to one image per post).

Am I misunderstanding the error details or is it a bug?

Hi @xlarry

The design of the error message might be a bit misleading. For general timeout and out of memory errors, the error appears above the trigger which makes it look as if the trigger is the culprit but in fact it’s just the workflow in general.

But, 0/5 steps executed is very strange.

Could you share the workflow ID of this affected workflow? It’s in the workflow’s URL when you have it open, and it looks like p_*****.

Hi @pierce

Thanks for clarification!

The workflow ID is p_V9CG5qO. Please note that the timeout setting has just been increased from 60 to 90s (but still failing occasionally). Also, there are some failed executions caused by an HTTP 429 response. Hope it doesn’t clutter your analysis too much.

Thanks @xlarry

Yes, the 429 responses and the attempted retrying by manually waiting within the code step is most likely the culprit here.

When using a 3rd party NPM package or manually using setTimeout in a code step, it’s still affecting the total execution time of the workflow.

You might have more luck with the built in $.flow.rerun method to rerun an API request in case you hit an API limit.

The advantage there is the pausing between reruns will not add to your overall execution time.

Below is a link to the documentation:

Here’s a short video on how to use it:

Hope this helps!

Alright, I will try to handle the 429 errors with $.flow.rerun then.

Thanks for your excellent support @pierce :slight_smile:

1 Like

Great @xlarry happy to help!

Please let me know if that solves your problem :slight_smile:

I am having the same experience!

In my case, I have a workflow that calls other workflows in an attempt to mimic a for-loop.

If I run the loop with the recommended code the workflow timeouts, but that is understandable because the other workflows it triggers can take long time to return.

Thus, I have modified the code to not wait for the calls to return. The goal is to trigger multiple workflows and die right after, while the other workflows are running in the background. When I do that, however, my workflow behaves as described by @xlarry: timeouts with 0 steps completed.

The problem happens because I am not awaiting for the promises, as pipedream requires. If that’s correct, how can I trigger other workflows and not wait for them to finish?

My workflow id is p_RRCDOD7.
My modified code is this:

import { axios } from "@pipedream/platform";

export default defineComponent({
  props: {
    await: {
      type: "boolean",
      label: "Should it await?",
      description: "Wait until all processes are finished.",
    },
    records: {
      type: "any",
      label: "Records to loop",
      description: "The array of records to send to processing workflow",
    },
    limit: {
      type: "integer",
      optional: true,
      label: "Limit of records",
      description:
        "The max amount of records to send to the processing workflow",
    },
    workflow_url: {
      type: "string",
      label: "Processing workflow URL",
      description:
        "The HTTP endpoint to connect to that's processing single individual records from this workflow",
    },
  },
  async run({ steps, $ }) {
    const calls = (
      this.limit ? this.records.slice(0, this.limit) : this.records
    ).map((record) =>
      axios($, {
        url: this.workflow_url,
        method: "POST",
        data: record,
      })
    );
    console.log(`${calls.length} call(s) made to ${this.workflow_url}`);

    if (this.await) {
      await Promise.all(calls);
    } else {
      $.flow.exit(`Workflow will continue processing in the background`);
    }
  },
});

Hi @zvictor

First off, welcome to the Pipedream community. Happy to have you!

That’s a really neat way to improve the looping control, thanks for sharing.

Another tool to use in Pipedream is the $.send.http interface. Unlike a normal HTTP request in code, the $.send.http will queue the HTTP call after the workflow is finished.

That way you can send a batch of HTTP calls and not affect the workflow’s execution timeout.

I suggest switching your axios usage with a destination HTTP call instead, because you’re not using the response of the HTTP call in this case.

I like the idea of processing in the background, which is possible in the browser, but unfortunately that won’t work in Pipedream or Node.js. The workflow’s execution is terminated once the functions are finished await’ing.

You’ll need to return a Promise, or await all asynchronous operations within Node.js code steps. Otherwise they will terminate once code control has passed to the next step.

1 Like

Thanks for the info!

Here is the new code I am using:

import { axios } from '@pipedream/platform'

export default defineComponent({
  props: {
    async: {
      type: 'boolean',
      label: 'Run asynchronously?',
      description: 'Run workflows in the background',
    },
    timeout: {
      type: 'integer',
      label: 'How long should it wait? (ms)',
      description: 'Max time in ms for requests to return',
    },
    records: {
      type: 'any',
      label: 'Records to loop',
      description: 'The array of records to send to processing workflow',
    },
    limit: {
      type: 'integer',
      optional: true,
      label: 'Limit of records',
      description: 'The max amount of records to send to the processing workflow',
    },
    workflow_url: {
      type: 'string',
      label: 'Processing workflow URL',
      description:
        "The HTTP endpoint to connect to that's processing single individual records from this workflow",
    },
  },
  async run({ steps, $ }) {
    const method = `POST`

    try {
      const calls = (this.limit ? this.records.slice(0, this.limit) : this.records).map((record) =>
        this.async
          ? $.send.http({
              url: this.workflow_url,
              // timeout: this.timeout,
              method,
              data: record,
            })
          : axios($, {
              url: this.workflow_url,
              timeout: this.timeout,
              method,
              data: record,
            })
      )

      console.log(`${calls.length} call(s) made to ${this.workflow_url}`)
      await Promise.all(calls)
    } catch (error) {
      if (error.message.startsWith('timeout')) {
        return $.flow.exit(error.message)
      }

      throw error
    }
  },
})