@budibase/server 2.6.19-alpha.30 → 2.6.19-alpha.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automation.js +72 -28
- package/dist/automation.js.map +3 -3
- package/dist/index.js +54 -17
- package/dist/index.js.map +3 -3
- package/dist/query.js +4 -0
- package/dist/query.js.map +2 -2
- package/package.json +9 -8
- package/src/automations/logging/index.ts +21 -0
- package/src/environment.ts +1 -0
- package/src/threads/automation.ts +45 -28
- package/src/threads/index.ts +9 -3
- package/src/threads/utils.ts +2 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.6.19-alpha.
|
|
4
|
+
"version": "2.6.19-alpha.31",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"license": "GPL-3.0",
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
49
|
-
"@budibase/backend-core": "2.6.19-alpha.
|
|
50
|
-
"@budibase/client": "2.6.19-alpha.
|
|
51
|
-
"@budibase/pro": "2.6.19-alpha.
|
|
52
|
-
"@budibase/shared-core": "2.6.19-alpha.
|
|
53
|
-
"@budibase/string-templates": "2.6.19-alpha.
|
|
54
|
-
"@budibase/types": "2.6.19-alpha.
|
|
49
|
+
"@budibase/backend-core": "2.6.19-alpha.31",
|
|
50
|
+
"@budibase/client": "2.6.19-alpha.31",
|
|
51
|
+
"@budibase/pro": "2.6.19-alpha.31",
|
|
52
|
+
"@budibase/shared-core": "2.6.19-alpha.31",
|
|
53
|
+
"@budibase/string-templates": "2.6.19-alpha.31",
|
|
54
|
+
"@budibase/types": "2.6.19-alpha.31",
|
|
55
55
|
"@bull-board/api": "3.7.0",
|
|
56
56
|
"@bull-board/koa": "3.9.4",
|
|
57
57
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"mssql": "6.2.3",
|
|
101
101
|
"mysql2": "2.3.3",
|
|
102
102
|
"node-fetch": "2.6.7",
|
|
103
|
+
"object-sizeof": "2.6.1",
|
|
103
104
|
"open": "8.4.0",
|
|
104
105
|
"openai": "^3.2.1",
|
|
105
106
|
"pg": "8.10.0",
|
|
@@ -195,5 +196,5 @@
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
},
|
|
198
|
-
"gitHead": "
|
|
199
|
+
"gitHead": "46abe5917ecc3ed921f10dfd9aff09f1970f80c3"
|
|
199
200
|
}
|
|
@@ -2,6 +2,23 @@ import env from "../../environment"
|
|
|
2
2
|
import { AutomationResults, Automation, App } from "@budibase/types"
|
|
3
3
|
import { automations } from "@budibase/pro"
|
|
4
4
|
import { db as dbUtils } from "@budibase/backend-core"
|
|
5
|
+
import sizeof from "object-sizeof"
|
|
6
|
+
|
|
7
|
+
const MAX_LOG_SIZE_MB = 5
|
|
8
|
+
const MB_IN_BYTES = 1024 * 1024
|
|
9
|
+
|
|
10
|
+
function sanitiseResults(results: AutomationResults) {
|
|
11
|
+
const message = `[removed] - max results size of ${MAX_LOG_SIZE_MB}MB exceeded`
|
|
12
|
+
for (let step of results.steps) {
|
|
13
|
+
step.inputs = {
|
|
14
|
+
message,
|
|
15
|
+
}
|
|
16
|
+
step.outputs = {
|
|
17
|
+
message,
|
|
18
|
+
success: step.outputs.success,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
5
22
|
|
|
6
23
|
export async function storeLog(
|
|
7
24
|
automation: Automation,
|
|
@@ -11,6 +28,10 @@ export async function storeLog(
|
|
|
11
28
|
if (env.DISABLE_AUTOMATION_LOGS) {
|
|
12
29
|
return
|
|
13
30
|
}
|
|
31
|
+
const bytes = sizeof(results)
|
|
32
|
+
if (bytes / MB_IN_BYTES > MAX_LOG_SIZE_MB) {
|
|
33
|
+
sanitiseResults(results)
|
|
34
|
+
}
|
|
14
35
|
await automations.logs.storeLog(automation, results)
|
|
15
36
|
}
|
|
16
37
|
|
package/src/environment.ts
CHANGED
|
@@ -80,6 +80,7 @@ const environment = {
|
|
|
80
80
|
ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS,
|
|
81
81
|
SELF_HOSTED: process.env.SELF_HOSTED,
|
|
82
82
|
HTTP_MB_LIMIT: process.env.HTTP_MB_LIMIT,
|
|
83
|
+
FORKED_PROCESS_NAME: process.env.FORKED_PROCESS_NAME || "main",
|
|
83
84
|
// old
|
|
84
85
|
CLIENT_ID: process.env.CLIENT_ID,
|
|
85
86
|
_set(key: string, value: any) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
AutomationStatus,
|
|
20
20
|
AutomationMetadata,
|
|
21
21
|
AutomationJob,
|
|
22
|
+
AutomationData,
|
|
22
23
|
} from "@budibase/types"
|
|
23
24
|
import {
|
|
24
25
|
LoopStep,
|
|
@@ -37,8 +38,8 @@ const LOOP_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.LOOP.stepId
|
|
|
37
38
|
const CRON_STEP_ID = triggerDefs.CRON.stepId
|
|
38
39
|
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
|
39
40
|
|
|
40
|
-
function getLoopIterations(loopStep: LoopStep
|
|
41
|
-
|
|
41
|
+
function getLoopIterations(loopStep: LoopStep) {
|
|
42
|
+
let binding = loopStep.inputs.binding
|
|
42
43
|
if (!binding) {
|
|
43
44
|
return 0
|
|
44
45
|
}
|
|
@@ -68,7 +69,6 @@ class Orchestrator {
|
|
|
68
69
|
constructor(job: AutomationJob) {
|
|
69
70
|
let automation = job.data.automation
|
|
70
71
|
let triggerOutput = job.data.event
|
|
71
|
-
let timeout = job.data.event.timeout
|
|
72
72
|
const metadata = triggerOutput.metadata
|
|
73
73
|
this._chainCount = metadata ? metadata.automationChainCount! : 0
|
|
74
74
|
this._appId = triggerOutput.appId as string
|
|
@@ -252,7 +252,7 @@ class Orchestrator {
|
|
|
252
252
|
return
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
const start = performance.now()
|
|
256
256
|
for (let step of automation.definition.steps) {
|
|
257
257
|
if (timeoutFlag) {
|
|
258
258
|
break
|
|
@@ -277,22 +277,17 @@ class Orchestrator {
|
|
|
277
277
|
|
|
278
278
|
if (loopStep) {
|
|
279
279
|
input = await processObject(loopStep.inputs, this._context)
|
|
280
|
-
iterations = getLoopIterations(loopStep as LoopStep
|
|
280
|
+
iterations = getLoopIterations(loopStep as LoopStep)
|
|
281
281
|
}
|
|
282
282
|
for (let index = 0; index < iterations; index++) {
|
|
283
283
|
let originalStepInput = cloneDeep(step.inputs)
|
|
284
284
|
// Handle if the user has set a max iteration count or if it reaches the max limit set by us
|
|
285
285
|
if (loopStep && input.binding) {
|
|
286
|
-
let newInput: any = await processObject(
|
|
287
|
-
loopStep.inputs,
|
|
288
|
-
cloneDeep(this._context)
|
|
289
|
-
)
|
|
290
|
-
|
|
291
286
|
let tempOutput = { items: loopSteps, iterations: iterationCount }
|
|
292
287
|
try {
|
|
293
|
-
|
|
288
|
+
loopStep.inputs.binding = automationUtils.typecastForLooping(
|
|
294
289
|
loopStep as LoopStep,
|
|
295
|
-
|
|
290
|
+
loopStep.inputs as LoopInput
|
|
296
291
|
)
|
|
297
292
|
} catch (err) {
|
|
298
293
|
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
@@ -303,13 +298,12 @@ class Orchestrator {
|
|
|
303
298
|
loopStep = undefined
|
|
304
299
|
break
|
|
305
300
|
}
|
|
306
|
-
|
|
307
301
|
let item = []
|
|
308
302
|
if (
|
|
309
303
|
typeof loopStep.inputs.binding === "string" &&
|
|
310
304
|
loopStep.inputs.option === "String"
|
|
311
305
|
) {
|
|
312
|
-
item = automationUtils.stringSplit(
|
|
306
|
+
item = automationUtils.stringSplit(loopStep.inputs.binding)
|
|
313
307
|
} else if (Array.isArray(loopStep.inputs.binding)) {
|
|
314
308
|
item = loopStep.inputs.binding
|
|
315
309
|
}
|
|
@@ -351,6 +345,7 @@ class Orchestrator {
|
|
|
351
345
|
}
|
|
352
346
|
}
|
|
353
347
|
}
|
|
348
|
+
|
|
354
349
|
if (
|
|
355
350
|
index === env.AUTOMATION_MAX_ITERATIONS ||
|
|
356
351
|
index === parseInt(loopStep.inputs.iterations)
|
|
@@ -479,8 +474,22 @@ class Orchestrator {
|
|
|
479
474
|
}
|
|
480
475
|
}
|
|
481
476
|
|
|
477
|
+
const end = performance.now()
|
|
478
|
+
const executionTime = end - start
|
|
479
|
+
|
|
480
|
+
console.log(`Execution time: ${executionTime} milliseconds`)
|
|
481
|
+
|
|
482
482
|
// store the logs for the automation run
|
|
483
|
-
|
|
483
|
+
try {
|
|
484
|
+
await storeLog(this._automation, this.executionOutput)
|
|
485
|
+
} catch (e: any) {
|
|
486
|
+
if (e.status === 413 && e.request?.data) {
|
|
487
|
+
// if content is too large we shouldn't log it
|
|
488
|
+
delete e.request.data
|
|
489
|
+
e.request.data = { message: "removed due to large size" }
|
|
490
|
+
}
|
|
491
|
+
logging.logAlert("Error writing automation log", e)
|
|
492
|
+
}
|
|
484
493
|
if (isProdAppID(this._appId) && isRecurring(automation) && metadata) {
|
|
485
494
|
await this.updateMetadata(metadata)
|
|
486
495
|
}
|
|
@@ -488,23 +497,31 @@ class Orchestrator {
|
|
|
488
497
|
}
|
|
489
498
|
}
|
|
490
499
|
|
|
491
|
-
export function execute(job: Job
|
|
500
|
+
export function execute(job: Job<AutomationData>, callback: WorkerCallback) {
|
|
492
501
|
const appId = job.data.event.appId
|
|
502
|
+
const automationId = job.data.automation._id
|
|
493
503
|
if (!appId) {
|
|
494
504
|
throw new Error("Unable to execute, event doesn't contain app ID.")
|
|
495
505
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
506
|
+
if (!automationId) {
|
|
507
|
+
throw new Error("Unable to execute, event doesn't contain automation ID.")
|
|
508
|
+
}
|
|
509
|
+
return context.doInAutomationContext({
|
|
510
|
+
appId,
|
|
511
|
+
automationId,
|
|
512
|
+
task: async () => {
|
|
513
|
+
const envVars = await sdkUtils.getEnvironmentVariables()
|
|
514
|
+
// put into automation thread for whole context
|
|
515
|
+
await context.doInEnvironmentContext(envVars, async () => {
|
|
516
|
+
const automationOrchestrator = new Orchestrator(job)
|
|
517
|
+
try {
|
|
518
|
+
const response = await automationOrchestrator.execute()
|
|
519
|
+
callback(null, response)
|
|
520
|
+
} catch (err) {
|
|
521
|
+
callback(err)
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
},
|
|
508
525
|
})
|
|
509
526
|
}
|
|
510
527
|
|
package/src/threads/index.ts
CHANGED
|
@@ -38,6 +38,9 @@ export class Thread {
|
|
|
38
38
|
this.count = opts.count ? opts.count : 1
|
|
39
39
|
this.disableThreading = this.shouldDisableThreading()
|
|
40
40
|
if (!this.disableThreading) {
|
|
41
|
+
console.debug(
|
|
42
|
+
`[${env.FORKED_PROCESS_NAME}] initialising worker farm type=${type}`
|
|
43
|
+
)
|
|
41
44
|
const workerOpts: any = {
|
|
42
45
|
autoStart: true,
|
|
43
46
|
maxConcurrentWorkers: this.count,
|
|
@@ -45,6 +48,7 @@ export class Thread {
|
|
|
45
48
|
env: {
|
|
46
49
|
...process.env,
|
|
47
50
|
FORKED_PROCESS: "1",
|
|
51
|
+
FORKED_PROCESS_NAME: type,
|
|
48
52
|
},
|
|
49
53
|
},
|
|
50
54
|
}
|
|
@@ -54,6 +58,10 @@ export class Thread {
|
|
|
54
58
|
}
|
|
55
59
|
this.workers = workerFarm(workerOpts, typeToFile(type), ["execute"])
|
|
56
60
|
Thread.workerRefs.push(this.workers)
|
|
61
|
+
} else {
|
|
62
|
+
console.debug(
|
|
63
|
+
`[${env.FORKED_PROCESS_NAME}] skipping worker farm type=${type}`
|
|
64
|
+
)
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
67
|
|
|
@@ -72,9 +80,7 @@ export class Thread {
|
|
|
72
80
|
function fire(worker: any) {
|
|
73
81
|
worker.execute(job, (err: any, response: any) => {
|
|
74
82
|
if (err && err.type === "TimeoutError") {
|
|
75
|
-
reject(
|
|
76
|
-
new Error(`Query response time exceeded ${timeout}ms timeout.`)
|
|
77
|
-
)
|
|
83
|
+
reject(new Error(`Thread timeout exceeded ${timeout}ms timeout.`))
|
|
78
84
|
} else if (err) {
|
|
79
85
|
reject(err)
|
|
80
86
|
} else {
|
package/src/threads/utils.ts
CHANGED
|
@@ -26,8 +26,10 @@ function makeVariableKey(queryId: string, variable: string) {
|
|
|
26
26
|
export function threadSetup() {
|
|
27
27
|
// don't run this if not threading
|
|
28
28
|
if (env.isTest() || env.DISABLE_THREADING || !env.isInThread()) {
|
|
29
|
+
console.debug(`[${env.FORKED_PROCESS_NAME}] thread setup skipped`)
|
|
29
30
|
return
|
|
30
31
|
}
|
|
32
|
+
console.debug(`[${env.FORKED_PROCESS_NAME}] thread setup running`)
|
|
31
33
|
db.init()
|
|
32
34
|
}
|
|
33
35
|
|