Updating timeouts for automations, making it easier to manage/find the various timeout limits. Also adding a new environment variable AUTOMATION_THREAD_TIMEOUT which can be used to control how long automations can run for.
This commit is contained in:
parent
169fec29c6
commit
cfd1c98c85
|
@ -1,4 +1,3 @@
|
||||||
import * as actions from "../../automations/actions"
|
|
||||||
import * as triggers from "../../automations/triggers"
|
import * as triggers from "../../automations/triggers"
|
||||||
import {
|
import {
|
||||||
getAutomationParams,
|
getAutomationParams,
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
removeDeprecated,
|
removeDeprecated,
|
||||||
} from "../../automations/utils"
|
} from "../../automations/utils"
|
||||||
import { deleteEntityMetadata } from "../../utilities"
|
import { deleteEntityMetadata } from "../../utilities"
|
||||||
import { MetadataTypes } from "../../constants"
|
import { MetadataTypes, AUTOMATION_SYNC_TIMEOUT } from "../../constants"
|
||||||
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
||||||
import { context, cache, events, db as dbCore } from "@budibase/backend-core"
|
import { context, cache, events, db as dbCore } from "@budibase/backend-core"
|
||||||
import { automations, features } from "@budibase/pro"
|
import { automations, features } from "@budibase/pro"
|
||||||
|
@ -20,7 +19,7 @@ import {
|
||||||
Automation,
|
Automation,
|
||||||
AutomationActionStepId,
|
AutomationActionStepId,
|
||||||
AutomationResults,
|
AutomationResults,
|
||||||
BBContext,
|
UserCtx,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
@ -72,7 +71,7 @@ function cleanAutomationInputs(automation: Automation) {
|
||||||
return automation
|
return automation
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(ctx: BBContext) {
|
export async function create(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = ctx.request.body
|
let automation = ctx.request.body
|
||||||
automation.appId = ctx.appId
|
automation.appId = ctx.appId
|
||||||
|
@ -141,7 +140,7 @@ export async function handleStepEvents(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update(ctx: BBContext) {
|
export async function update(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = ctx.request.body
|
let automation = ctx.request.body
|
||||||
automation.appId = ctx.appId
|
automation.appId = ctx.appId
|
||||||
|
@ -192,7 +191,7 @@ export async function update(ctx: BBContext) {
|
||||||
builderSocket?.emitAutomationUpdate(ctx, automation)
|
builderSocket?.emitAutomationUpdate(ctx, automation)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetch(ctx: BBContext) {
|
export async function fetch(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
getAutomationParams(null, {
|
getAutomationParams(null, {
|
||||||
|
@ -202,12 +201,12 @@ export async function fetch(ctx: BBContext) {
|
||||||
ctx.body = response.rows.map(row => row.doc)
|
ctx.body = response.rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function find(ctx: BBContext) {
|
export async function find(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
ctx.body = await db.get(ctx.params.id)
|
ctx.body = await db.get(ctx.params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: BBContext) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const automationId = ctx.params.id
|
const automationId = ctx.params.id
|
||||||
const oldAutomation = await db.get<Automation>(automationId)
|
const oldAutomation = await db.get<Automation>(automationId)
|
||||||
|
@ -221,11 +220,11 @@ export async function destroy(ctx: BBContext) {
|
||||||
builderSocket?.emitAutomationDeletion(ctx, automationId)
|
builderSocket?.emitAutomationDeletion(ctx, automationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logSearch(ctx: BBContext) {
|
export async function logSearch(ctx: UserCtx) {
|
||||||
ctx.body = await automations.logs.logSearch(ctx.request.body)
|
ctx.body = await automations.logs.logSearch(ctx.request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function clearLogError(ctx: BBContext) {
|
export async function clearLogError(ctx: UserCtx) {
|
||||||
const { automationId, appId } = ctx.request.body
|
const { automationId, appId } = ctx.request.body
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const db = context.getProdAppDB()
|
const db = context.getProdAppDB()
|
||||||
|
@ -244,15 +243,15 @@ export async function clearLogError(ctx: BBContext) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getActionList(ctx: BBContext) {
|
export async function getActionList(ctx: UserCtx) {
|
||||||
ctx.body = await getActionDefinitions()
|
ctx.body = await getActionDefinitions()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTriggerList(ctx: BBContext) {
|
export async function getTriggerList(ctx: UserCtx) {
|
||||||
ctx.body = getTriggerDefinitions()
|
ctx.body = getTriggerDefinitions()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDefinitionList(ctx: BBContext) {
|
export async function getDefinitionList(ctx: UserCtx) {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
trigger: getTriggerDefinitions(),
|
trigger: getTriggerDefinitions(),
|
||||||
action: await getActionDefinitions(),
|
action: await getActionDefinitions(),
|
||||||
|
@ -265,7 +264,7 @@ export async function getDefinitionList(ctx: BBContext) {
|
||||||
* *
|
* *
|
||||||
*********************/
|
*********************/
|
||||||
|
|
||||||
export async function trigger(ctx: BBContext) {
|
export async function trigger(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = await db.get<Automation>(ctx.params.id)
|
let automation = await db.get<Automation>(ctx.params.id)
|
||||||
|
|
||||||
|
@ -275,7 +274,7 @@ export async function trigger(ctx: BBContext) {
|
||||||
automation,
|
automation,
|
||||||
{
|
{
|
||||||
fields: ctx.request.body.fields,
|
fields: ctx.request.body.fields,
|
||||||
timeout: ctx.request.body.timeout * 1000 || 120000,
|
timeout: ctx.request.body.timeout * 1000 || AUTOMATION_SYNC_TIMEOUT,
|
||||||
},
|
},
|
||||||
{ getResponses: true }
|
{ getResponses: true }
|
||||||
)
|
)
|
||||||
|
@ -310,7 +309,7 @@ function prepareTestInput(input: any) {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function test(ctx: BBContext) {
|
export async function test(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = await db.get<Automation>(ctx.params.id)
|
let automation = await db.get<Automation>(ctx.params.id)
|
||||||
await setTestFlag(automation._id!)
|
await setTestFlag(automation._id!)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
import { ValidQueryNameRegex } from "@budibase/shared-core"
|
import { ValidQueryNameRegex } from "@budibase/shared-core"
|
||||||
|
|
||||||
const Runner = new Thread(ThreadType.QUERY, {
|
const Runner = new Thread(ThreadType.QUERY, {
|
||||||
timeoutMs: env.QUERY_THREAD_TIMEOUT || 10000,
|
timeoutMs: env.QUERY_THREAD_TIMEOUT,
|
||||||
})
|
})
|
||||||
|
|
||||||
// simple function to append "readable" to all read queries
|
// simple function to append "readable" to all read queries
|
||||||
|
|
|
@ -65,7 +65,7 @@ export async function run({ inputs, context }: AutomationStepInput) {
|
||||||
success = true
|
success = true
|
||||||
try {
|
try {
|
||||||
stdout = execSync(command, {
|
stdout = execSync(command, {
|
||||||
timeout: environment.QUERY_THREAD_TIMEOUT || 500,
|
timeout: environment.QUERY_THREAD_TIMEOUT,
|
||||||
}).toString()
|
}).toString()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
stdout = err.message
|
stdout = err.message
|
||||||
|
|
|
@ -86,10 +86,7 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.race([
|
await queryController.executeV2(ctx, { isAutomation: true })
|
||||||
queryController.executeV2(ctx, { isAutomation: true }),
|
|
||||||
utils.timeout(env.QUERY_THREAD_TIMEOUT, { reject: true }),
|
|
||||||
])
|
|
||||||
const { data, ...rest } = ctx.body
|
const { data, ...rest } = ctx.body
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -9,8 +9,9 @@ import {
|
||||||
AutomationCustomIOType,
|
AutomationCustomIOType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import * as triggers from "../triggers"
|
import * as triggers from "../triggers"
|
||||||
import { db as dbCore, context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { features } from "@budibase/pro"
|
import { features } from "@budibase/pro"
|
||||||
|
import { AUTOMATION_SYNC_TIMEOUT } from "../../constants"
|
||||||
|
|
||||||
export const definition: AutomationStepSchema = {
|
export const definition: AutomationStepSchema = {
|
||||||
name: "Trigger an automation",
|
name: "Trigger an automation",
|
||||||
|
@ -76,7 +77,7 @@ export async function run({ inputs }: AutomationStepInput) {
|
||||||
automation,
|
automation,
|
||||||
{
|
{
|
||||||
fields: { ...fieldParams },
|
fields: { ...fieldParams },
|
||||||
timeout: inputs.timeout * 1000 || 120000,
|
timeout: inputs.timeout * 1000 || AUTOMATION_SYNC_TIMEOUT,
|
||||||
},
|
},
|
||||||
{ getResponses: true }
|
{ getResponses: true }
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@ jest.spyOn(global.console, "error")
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import * as automation from "../index"
|
import * as automation from "../index"
|
||||||
import { serverLogAutomation } from "../../tests/utilities/structures"
|
import { serverLogAutomation } from "../../tests/utilities/structures"
|
||||||
|
import { AUTOMATION_ASYNC_TIMEOUT } from "../../constants"
|
||||||
|
|
||||||
describe("Test triggering an automation from another automation", () => {
|
describe("Test triggering an automation from another automation", () => {
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
@ -22,7 +23,10 @@ describe("Test triggering an automation from another automation", () => {
|
||||||
let newAutomation = await config.createAutomation(automation)
|
let newAutomation = await config.createAutomation(automation)
|
||||||
|
|
||||||
const inputs: any = {
|
const inputs: any = {
|
||||||
automation: { automationId: newAutomation._id, timeout: 12000 },
|
automation: {
|
||||||
|
automationId: newAutomation._id,
|
||||||
|
timeout: AUTOMATION_ASYNC_TIMEOUT,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const res = await setup.runStep(
|
const res = await setup.runStep(
|
||||||
setup.actions.TRIGGER_AUTOMATION_RUN.stepId,
|
setup.actions.TRIGGER_AUTOMATION_RUN.stepId,
|
||||||
|
@ -33,7 +37,12 @@ describe("Test triggering an automation from another automation", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should fail gracefully if the automation id is incorrect", async () => {
|
it("should fail gracefully if the automation id is incorrect", async () => {
|
||||||
const inputs: any = { automation: { automationId: null, timeout: 12000 } }
|
const inputs: any = {
|
||||||
|
automation: {
|
||||||
|
automationId: null,
|
||||||
|
timeout: AUTOMATION_ASYNC_TIMEOUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
const res = await setup.runStep(
|
const res = await setup.runStep(
|
||||||
setup.actions.TRIGGER_AUTOMATION_RUN.stepId,
|
setup.actions.TRIGGER_AUTOMATION_RUN.stepId,
|
||||||
inputs
|
inputs
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { Duration } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
export const AUTOMATION_SYNC_TIMEOUT = Duration.fromMinutes(2).toMs()
|
||||||
|
export const AUTOMATION_ASYNC_TIMEOUT = Duration.fromSeconds(12).toMs()
|
|
@ -6,6 +6,8 @@ import {
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
export * from "./automations"
|
||||||
|
|
||||||
export enum FilterTypes {
|
export enum FilterTypes {
|
||||||
STRING = "string",
|
STRING = "string",
|
||||||
FUZZY = "fuzzy",
|
FUZZY = "fuzzy",
|
||||||
|
|
|
@ -53,7 +53,9 @@ const environment = {
|
||||||
parseIntSafe(process.env.AUTOMATION_MAX_ITERATIONS) || 200,
|
parseIntSafe(process.env.AUTOMATION_MAX_ITERATIONS) || 200,
|
||||||
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
|
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
|
||||||
DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT,
|
DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT,
|
||||||
QUERY_THREAD_TIMEOUT: parseIntSafe(process.env.QUERY_THREAD_TIMEOUT),
|
QUERY_THREAD_TIMEOUT: parseIntSafe(process.env.QUERY_THREAD_TIMEOUT) || 10000,
|
||||||
|
AUTOMATION_THREAD_TIMEOUT:
|
||||||
|
parseIntSafe(process.env.AUTOMATION_THREAD_TIMEOUT) || 12000,
|
||||||
SQL_MAX_ROWS: process.env.SQL_MAX_ROWS,
|
SQL_MAX_ROWS: process.env.SQL_MAX_ROWS,
|
||||||
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
|
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
|
||||||
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
|
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
|
||||||
|
|
|
@ -303,7 +303,7 @@ class Orchestrator {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
timeoutFlag = true
|
timeoutFlag = true
|
||||||
}, timeout || 12000)
|
}, timeout || env.AUTOMATION_THREAD_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
|
||||||
stepCount++
|
stepCount++
|
||||||
|
@ -621,7 +621,7 @@ export async function executeInThread(job: Job<AutomationData>) {
|
||||||
const timeoutPromise = new Promise((resolve, reject) => {
|
const timeoutPromise = new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(new Error("Timeout exceeded"))
|
reject(new Error("Timeout exceeded"))
|
||||||
}, job.data.event.timeout || 12000)
|
}, job.data.event.timeout || env.AUTOMATION_THREAD_TIMEOUT)
|
||||||
})
|
})
|
||||||
|
|
||||||
return await context.doInAppContext(appId, async () => {
|
return await context.doInAppContext(appId, async () => {
|
||||||
|
|
Loading…
Reference in New Issue