Profiles / Programming
AD

Async developer

Asynchronous processing with the stack: push runs now or later, priorities and parallel limits, errors and replays, flows and cleanup.

10 recipes on this page · open in the interactive view

Recipes
  1. Push a script to the stack and watch it close
  2. Plan reminders at J+7, J+15 and J+30, then cancel them
  3. Priorities and parallel limit of the stack
  4. A run fails, the API comes back, replay it
  5. Replay a whole batch of failed exports
  6. Track the steps of a long import with a stack flow
  7. Dashboard of the stack queue
  8. A dispatcher pushes child runs and you follow the whole tree
  9. Clean the stack after a test campaign
  10. Same script: execute now or hand it to the stack
executed on MentDB

Push a script to the stack and watch it close

An invoice must be computed in the background instead of blocking the caller. The script pushes a typed exe script to the stack with the date now, reads the waiting entry, waits for the stack cycle, then reads the closed entry and the activity log of that process.

Pipeline
Script
admin
#1. A worker script: typed parameters, activity log on, 1 attempt;
script create exe "tuto.as.invoice" true 1
	(param
		(var "[order]" {type is_number [order]} "Order number" is_null:false is_empty:false "1001")
		(var "[amount]" {type is_double [amount]} "Net amount" is_null:false is_empty:false "250.00")
	;)
	"Compute the gross amount of an order in the background"
{
	-> "[gross]" (math round (* [amount] 1.2) 2);
	log write (concat "invoice " [order] " gross " [gross]) "OK" "order" [order];
	sequence increment "tuto_as_invoice_done";
	[gross];
} "The gross amount";
sequence add "tuto_as_invoice_done" 0;
#2. Push it now: stack returns the process id;
-> "[pid]" (stack (date now) "tuto.as.invoice.exe" "[order]" 1001 "[amount]" 250);
json load "tuto_as_p" (stack get [pid]);
-> "[before]" (concat "state=" (json select "tuto_as_p" "/state") ", attempts=" (json select "tuto_as_p" "/nbattempt") "/" (json select "tuto_as_p" "/nbmaxattempt"));
#3. The stack runs every 30 s: poll the counter written by the script;
-> "[i]" 0;
while (and (< [i] 40) (== (sequence get current "tuto_as_invoice_done") 0)) {wait "1000"; ++ "[i]";};
json load "tuto_as_p" (stack get [pid]);
json load "tuto_as_log" (string substring (log show_time [pid]) 39);
-> "[out]" (concat "pid " [pid] " pushed, " [before] [_n_] "closed at " (string substring (json select "tuto_as_p" "/dtclosed") 0 19) " after about " [i] " s, attempts=" (json select "tuto_as_p" "/nbattempt") [_n_] "activity log: " (json select "tuto_as_log" "/data/[0]/msg") " / " (json select "tuto_as_log" "/data/[1]/msg") " / " (json select "tuto_as_log" "/data/[2]/msg"));
#4. Cleanup;
json unload "tuto_as_p";
json unload "tuto_as_log";
stack delete_closed_id [pid];
sequence remove "tuto_as_invoice_done";
script delete "tuto.as.invoice.exe";
[out]
Result
mentdb
pid 1i pushed, state=W, attempts=0/1
closed at 2026-11-01 17:17:00 after about 35 s, attempts=1
activity log: [---- Begin script ----] / invoice 1001 gross 300 / [---- End script ----]
Go further
  • Read the closed run later with stack search "closed" "tuto.as.invoice.exe" ....
  • Use stack_execute with a future date to run it tomorrow instead of now.
executed on MentDB

Plan reminders at J+7, J+15 and J+30, then cancel them

A dunning process queues three payment reminders in the future. The script generates the call template, queues the runs with stack_execute and stack, lists the queue, reads the variables of one run, and cancels them when the customer pays.

Pipeline
Script
admin
#1. The reminder script;
script create exe "tuto.as.remind" false 1
	(param
		(var "[customer]" {true} "Customer id" is_null:false is_empty:false "C042")
		(var "[step]" {true} "Reminder step" is_null:false is_empty:false "J+7")
	;)
	"Send a payment reminder"
{
	log write (concat "reminder " [step] " for " [customer]) "OK" "customer" [customer];
} "1";
#2. The call template, then three runs in the future;
-> "[tpl]" (script generate stack "tuto.as.remind.exe");
-> "[w0]" (stack count_wait);
-> "[p7]" (stack_execute (date dateaddt (date now) "DAY" 7) "tuto.as.remind.exe" "[customer]" "C042" "[step]" "J+7");
-> "[p15]" (stack (date dateaddt (date now) "DAY" 15) "tuto.as.remind.exe" "[customer]" "C042" "[step]" "J+15");
-> "[p30]" (stack (date dateaddt (date now) "DAY" 30) "tuto.as.remind.exe" "[customer]" "C042" "[step]" "J+30");
-> "[w1]" (stack count_wait);
#3. The queue of this script, by execution date;
json load "tuto_as_q" (string substring (stack search "wait" "tuto.as.remind.exe" "exe" (date now) (date dateaddt (date now) "DAY" 31) ASC 1 10) 39);
-> "[list]" "";
for (-> "[i]" 0) (< [i] (json count "tuto_as_q" "/data")) (++ "[i]") {
	-> "[list]" (concat [list] (string substring (json select "tuto_as_q" (concat "/data/[" [i] "]/dtexe")) 0 10) " ");
};
-> "[nq]" (json count "tuto_as_q" "/data");
-> "[vars]" (stack var_show [p15]);
#4. The customer paid: cancel J+15 by id, then the rest of the script;
stack delete_wait_id [p15];
-> "[left]" (stack delete_wait_script "tuto.as.remind.exe");
json unload "tuto_as_q";
script delete "tuto.as.remind.exe";
concat "waiting runs on the server: " [w0] " -> " [w1] ", this script: " [nq] " runs on " [list] [_n_] "variables of J+15: " [vars] [_n_] "J+15 cancelled, delete_wait_script removed " [left] " more" [_n_] "template:" [_n_] [tpl]
Result
mentdb
waiting runs on the server: 0 -> 3, this script: 3 runs on 2026-11-08 2026-11-16 2026-12-01
variables of J+15: [{"param":"[customer]","value":"C042"},{"param":"[step]","value":"J+15"}]
J+15 cancelled, delete_wait_script removed 2 more
template:
stack_execute "2026-11-01 17:18:29" "tuto.as.remind.exe"
	"[customer]" "C042"
	"[step]" "J+7"
;
Go further
  • stack get on one pid shows its priority and the number of attempts.
  • Store the pids in a tuto_ node to cancel them from another script.
executed on MentDB

Priorities and parallel limit of the stack

Alerts must overtake heavy reports, and at most two reports may run at the same time. The script sets the stack options of both scripts, pushes three reports then two alerts due now, and looks at the queue during the cycle.

Pipeline
Script
admin
#1. A slow report and a fast alert;
script create exe "tuto.as.report" false 1
	(param
		(var "[n]" {type is_number [n]} "Report number" is_null:false is_empty:false "1")
	;)
	"Build a heavy report"
{
	wait "4000";
	sequence increment "tuto_as_report_done";
} "1";
script create exe "tuto.as.alert" false 1
	(param
		(var "[n]" {type is_number [n]} "Alert number" is_null:false is_empty:false "1")
	;)
	"Send an alert"
{
	sequence increment "tuto_as_alert_done";
} "1";
sequence add "tuto_as_report_done" 0;
sequence add "tuto_as_alert_done" 0;
#2. Alerts first with priority 9, at most 2 reports in parallel;
script set stack "tuto.as.report.exe" 0 2 false;
script set stack "tuto.as.alert.exe" 9 3 true;
#3. Three reports, then two alerts, all due now;
-> "[t0]" (date now);
stack (date now) "tuto.as.report.exe" "[n]" 1;
stack (date now) "tuto.as.report.exe" "[n]" 2;
stack (date now) "tuto.as.report.exe" "[n]" 3;
-> "[a1]" (stack (date now) "tuto.as.alert.exe" "[n]" 1);
stack (date now) "tuto.as.alert.exe" "[n]" 2;
json load "tuto_as_a" (stack get [a1]);
-> "[opt]" (concat "alert entry: priority=" (json select "tuto_as_a" "/priority") ", nb_in_thread=" (json select "tuto_as_a" "/nb_in_thread"));
#4. Wait for the cycle, then look while the reports run;
-> "[i]" 0;
while (and (< [i] 80) (< (sequence get current "tuto_as_alert_done") 2)) {wait "500"; ++ "[i]";};
wait "1000";
json load "tuto_as_r" (string substring (stack search "running" "tuto.as.report.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 10) 39);
json load "tuto_as_w" (string substring (stack search "wait" "tuto.as.report.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 10) 39);
-> "[mid]" (concat "1 s after the alerts: alerts done=" (sequence get current "tuto_as_alert_done") ", reports done=" (sequence get current "tuto_as_report_done") ", running=" (json count "tuto_as_r" "/data") ", waiting=" (json count "tuto_as_w" "/data"));
wait "5000";
-> "[end]" (concat "5 s later: reports done=" (sequence get current "tuto_as_report_done") ", the third waits for the next cycle");
#5. Cleanup;
-> "[del]" (concat "deleted: wait=" (stack delete_wait_script "tuto.as.report.exe") ", closed=" (+ (stack delete_closed_script "tuto.as.report.exe") (stack delete_closed_script "tuto.as.alert.exe")));
json unload "tuto_as_a";
json unload "tuto_as_r";
json unload "tuto_as_w";
sequence remove "tuto_as_report_done";
sequence remove "tuto_as_alert_done";
script delete "tuto.as.report.exe";
script delete "tuto.as.alert.exe";
concat [opt] [_n_] [mid] [_n_] [end] [_n_] [del]
Result
mentdb
alert entry: priority=9, nb_in_thread=3
1 s after the alerts: alerts done=2, reports done=0, running=2, waiting=1
5 s later: reports done=2, the third waits for the next cycle
deleted: wait=1, closed=4
Go further
executed on MentDB

A run fails, the API comes back, replay it

A CRM synchronisation fails while the CRM is down. The script reads the error of the process, then once the CRM is back gives it one more attempt and replays that pid only.

Pipeline
Script
admin
#1. The worker fails while the CRM is down, its stack runs are replayable;
script create exe "tuto.as.sync" false 1
	(param
		(var "[customer]" {true} "Customer id" is_null:false is_empty:false "C042")
	;)
	"Push a customer to the CRM"
{
	if (== (sequence get current "tuto_as_crm_up") 0) {exception (1) ("CRM unreachable: HTTP 503");};
	sequence increment "tuto_as_sync_done";
	[customer];
} "The customer id";
script set stack "tuto.as.sync.exe" 0 1 true;
sequence add "tuto_as_crm_up" 0;
sequence add "tuto_as_sync_done" 0;
#2. Push and wait until it lands in the error table;
-> "[t0]" (date now);
-> "[pid]" (stack (date now) "tuto.as.sync.exe" "[customer]" "C042");
-> "[i]" 0;
-> "[n]" 0;
while (and (< [i] 40) (< [n] 1)) {
	wait "1000";
	++ "[i]";
	json load "tuto_as_e" (string substring (stack search "error" "tuto.as.sync.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 20) 39);
	-> "[n]" (json count "tuto_as_e" "/data");
};
json load "tuto_as_p" (stack get [pid]);
-> "[err]" (concat "error after about " [i] " s, attempts " (json select "tuto_as_p" "/nbattempt") "/" (json select "tuto_as_p" "/nbmaxattempt") ": " (string regexp_extract (json select "tuto_as_p" "/lasterrormsg") "1: (.*)$" 1));
#3. The CRM is back: one more attempt, then replay this pid;
sequence update "tuto_as_crm_up" 1;
stack reset_error_nbattempt_id [pid];
-> "[rep]" (stack replay_error_id [pid]);
-> "[i]" 0;
while (and (< [i] 40) (== (sequence get current "tuto_as_sync_done") 0)) {wait "1000"; ++ "[i]";};
json load "tuto_as_p" (stack get [pid]);
-> "[ok]" (concat "replayed=" [rep] ", closed at " (string substring (json select "tuto_as_p" "/dtclosed") 0 19) ", attempts=" (json select "tuto_as_p" "/nbattempt"));
#4. Cleanup;
stack delete_closed_id [pid];
json unload "tuto_as_p";
json unload "tuto_as_e";
sequence remove "tuto_as_crm_up";
sequence remove "tuto_as_sync_done";
script delete "tuto.as.sync.exe";
concat "pid " [pid] [_n_] [err] [_n_] [ok]
Result
mentdb
pid 2a
error after about 1 s, attempts 1/1: CRM unreachable: HTTP 503
replayed=1, closed at 2026-11-01 17:19:00, attempts=1
Go further
executed on MentDB

Replay a whole batch of failed exports

Three nightly exports fail because the target share is offline. One file is known to be corrupt and is dropped; the others get one more attempt and are replayed by script once the share is back.

Pipeline
Script
admin
#1. The export fails while the share is offline;
script create exe "tuto.as.export" false 1
	(param
		(var "[file]" {true} "File to export" is_null:false is_empty:false "sales_A.csv")
	;)
	"Export a file to the share"
{
	if (== (sequence get current "tuto_as_share_up") 0) {exception (1) (concat "share offline, " [file] " not sent");};
	sequence increment "tuto_as_export_done";
} "1";
script set stack "tuto.as.export.exe" 0 3 true;
sequence add "tuto_as_share_up" 0;
sequence add "tuto_as_export_done" 0;
#2. Three exports, all fail at the next cycle;
-> "[t0]" (date now);
-> "[e0]" (stack count_error);
stack (date now) "tuto.as.export.exe" "[file]" "sales_A.csv";
-> "[pb]" (stack (date now) "tuto.as.export.exe" "[file]" "sales_B.csv");
stack (date now) "tuto.as.export.exe" "[file]" "sales_C.csv";
-> "[i]" 0;
-> "[n]" 0;
while (and (< [i] 40) (< [n] 3)) {
	wait "1000";
	++ "[i]";
	json load "tuto_as_e" (string substring (stack search "error" "tuto.as.export.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 20) 39);
	-> "[n]" (json count "tuto_as_e" "/data");
};
-> "[e1]" (stack count_error);
#3. B is corrupt and dropped, the others get one more attempt and are replayed;
stack delete_error_id [pb];
sequence update "tuto_as_share_up" 1;
-> "[reset]" (stack reset_error_nbattempt_script "tuto.as.export.exe");
-> "[replayed]" (stack replay_error_script "tuto.as.export.exe");
-> "[i]" 0;
while (and (< [i] 40) (< (sequence get current "tuto_as_export_done") 2)) {wait "1000"; ++ "[i]";};
#4. Cleanup;
-> "[closed]" (stack delete_closed_script "tuto.as.export.exe");
json unload "tuto_as_e";
sequence remove "tuto_as_share_up";
sequence remove "tuto_as_export_done";
script delete "tuto.as.export.exe";
concat "errors on the server: " [e0] " -> " [e1] " (" [n] " of this script)" [_n_] "B dropped, reset=" [reset] ", replayed=" [replayed] [_n_] "closed after replay: " [closed] " (exports done in about " [i] " s)"
Result
mentdb
errors on the server: 0 -> 4 (3 of this script)
B dropped, reset=2, replayed=OK: 2/2
closed after replay: 2 (exports done in about 30 s)
Go further
executed on MentDB

Track the steps of a long import with a stack flow

An import runs in the stack and must report where it is: the worker names its flow, marks each step and keeps a JSON state; the caller reads the flow from the stack entry when it is closed.

Pipeline
Script
admin
#1. The worker names its flow, marks its steps and saves its state;
script create exe "tuto.as.import" false 1
	(param
		(var "[file]" {true} "File to import" is_null:false is_empty:false "orders_20261101.csv")
	;)
	"Import a file in tracked steps"
{
	stack flow_init [PID] "tuto_as_orders_import" "{}";
	stack flow_step [PID] 1 "parse";
	json load "tuto_as_flow" (stack flow_json_get [PID]);
	json iobject "tuto_as_flow" / "file" [file] STR;
	json iobject "tuto_as_flow" / "rows" 42 INT;
	stack flow_json_set [PID] (json doc "tuto_as_flow");
	json unload "tuto_as_flow";
	stack flow_step [PID] 2 "loaded";
	sequence increment "tuto_as_import_done";
} "1";
sequence add "tuto_as_import_done" 0;
#2. Push and wait;
-> "[pid]" (stack (date now) "tuto.as.import.exe" "[file]" "orders_20261101.csv");
-> "[i]" 0;
while (and (< [i] 40) (== (sequence get current "tuto_as_import_done") 0)) {wait "1000"; ++ "[i]";};
#3. The flow is stored in the stack entry;
json load "tuto_as_p" (stack get [pid]);
json load "tuto_as_s" (json select "tuto_as_p" "/json");
-> "[out]" (concat "flow " (json select "tuto_as_p" "/flowname") ", last step " (json select "tuto_as_p" "/pos") " = " (json select "tuto_as_p" "/posname") [_n_] "state: file=" (json select "tuto_as_s" "/file") ", rows=" (json select "tuto_as_s" "/rows"));
#4. Cleanup;
stack delete_closed_id [pid];
json unload "tuto_as_p";
json unload "tuto_as_s";
sequence remove "tuto_as_import_done";
script delete "tuto.as.import.exe";
[out]
Result
mentdb
flow tuto_as_orders_import, last step 2 = loaded
state: file=orders_20261101.csv, rows=42
Go further
  • Children pushed with stack from the worker inherit the flow root: follow them with restricted show_pid.
  • Read a running flow with stack search "running" ... to build a progress bar.
executed on MentDB

Dashboard of the stack queue

Before a release, the developer checks the load of the stack and pages through the waiting runs of a script. The script reads the status, the limit and the four counters, queues three runs for tomorrow and pages through them two by two.

Pipeline
Script
admin
#1. Load of the stack;
-> "[inuse]" (string regexp_extract (stack status) "NB_PROCESS_IN_STACK=([0-9]+)" 1);
-> "[limit]" (stack process_limit);
-> "[load]" (concat "threads " [inuse] "/" [limit] ", wait=" (stack count_wait) ", running=" (stack count_running) ", closed=" (stack count_closed) ", error=" (stack count_error));
#2. Three runs of a nightly script for tomorrow at 01:00, 01:10, 01:20;
script create exe "tuto.as.nightly" false 1
	(param
		(var "[part]" {true} "Partition to rebuild" is_null:false is_empty:false "P1")
	;)
	"Rebuild one partition"
{
	[part];
} "The partition";
-> "[day]" (date dateadd (date sysdate) "DAY" 1);
stack (concat [day] " 01:00:00") "tuto.as.nightly.exe" "[part]" "P1";
stack (concat [day] " 01:10:00") "tuto.as.nightly.exe" "[part]" "P2";
stack (concat [day] " 01:20:00") "tuto.as.nightly.exe" "[part]" "P3";
#3. Page through them, 2 per page;
-> "[pages]" "";
for (-> "[p]" 1) (<= [p] 2) (++ "[p]") {
	json load "tuto_as_pg" (string substring (stack search "wait" "tuto.as.nightly.exe" "exe" (concat [day] " 00:00:00") (concat [day] " 23:59:59") ASC [p] 2) 39);
	-> "[pages]" (concat [pages] "page " [p] ":");
	for (-> "[i]" 0) (< [i] (json count "tuto_as_pg" "/data")) (++ "[i]") {
		-> "[pages]" (concat [pages] " " (string substring (json select "tuto_as_pg" (concat "/data/[" [i] "]/dtexe")) 11 16));
	};
	-> "[pages]" (concat [pages] [_n_]);
};
#4. Cleanup;
-> "[del]" (stack delete_wait_script "tuto.as.nightly.exe");
json unload "tuto_as_pg";
script delete "tuto.as.nightly.exe";
concat [load] [_n_] [pages] "test runs removed: " [del]
Result
mentdb
threads 0/10, wait=6, running=0, closed=0, error=0
page 1: 01:00 01:10
page 2: 01:20
test runs removed: 3
Go further
  • stack get on a pid of a page gives its variables and attempts.
  • Turn this into a get web service script to feed an admin screen.
executed on MentDB

A dispatcher pushes child runs and you follow the whole tree

A dispatcher run in the stack splits a job into three child runs. Each child knows its parent; the developer then reads the logs of the parent and of all its linked processes in one call.

Pipeline
Script
admin
#1. A child prices one region, the dispatcher pushes one child per region;
script create exe "tuto.as.price_region" true 1
	(param
		(var "[region]" {true} "Region code" is_null:false is_empty:false "NORTH")
	;)
	"Recompute the prices of one region"
{
	log write (concat "prices of " [region] " recomputed") "OK" "region" [region];
	sequence increment "tuto_as_regions_done";
} "1";
script create exe "tuto.as.dispatch" true 1
	(param
	;)
	"Split the repricing by region"
{
	-> "[regions]" "NORTH,SOUTH,WEST";
	for (-> "[i]" 1) (<= [i] (atom size [regions] ",")) (++ "[i]") {
		stack (date now) "tuto.as.price_region.exe" "[region]" (atom get [regions] [i] ",");
	};
	log write "3 regions dispatched" "OK" "regions" 3;
} "1";
script set stack "tuto.as.price_region.exe" 0 3 false;
sequence add "tuto_as_regions_done" 0;
#2. Run the dispatcher through the stack: two cycles, parent then children;
-> "[t0]" (date now);
-> "[pid]" (stack (date now) "tuto.as.dispatch.exe");
-> "[i]" 0;
while (and (< [i] 75) (< (sequence get current "tuto_as_regions_done") 3)) {wait "1000"; ++ "[i]";};
#3. A child knows its parent, show_pid gives the whole tree;
json load "tuto_as_c" (string substring (stack search "closed" "tuto.as.price_region.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 10) 39);
json load "tuto_as_c1" (stack get (atom get_last (json select "tuto_as_c" "/data/[0]/pid") " "));
json load "tuto_as_t" (string substring (restricted show_pid [pid]) 39);
-> "[tree]" "";
for (-> "[k]" 0) (< [k] (json count "tuto_as_t" "/data")) (++ "[k]") {
	-> "[m]" (json select "tuto_as_t" (concat "/data/[" [k] "]/msg"));
	if (not (string starts_with [m] "[----")) {-> "[tree]" (concat [tree] "  " (json select "tuto_as_t" (concat "/data/[" [k] "]/script")) ": " [m] [_n_]);};
};
-> "[out]" (concat "dispatcher pid " [pid] ", children closed: " (json count "tuto_as_c" "/data") " in about " [i] " s, parent_id of a child = " (json select "tuto_as_c1" "/parent_id") [_n_] "show_pid lines: " (json count "tuto_as_t" "/data") [_n_] [tree]);
#4. Cleanup;
stack delete_wait_script "tuto.as.price_region.exe";
stack delete_closed_script "tuto.as.price_region.exe";
stack delete_closed_script "tuto.as.dispatch.exe";
json unload "tuto_as_c";
json unload "tuto_as_c1";
json unload "tuto_as_t";
sequence remove "tuto_as_regions_done";
script delete "tuto.as.price_region.exe";
script delete "tuto.as.dispatch.exe";
[out]
Result
mentdb
dispatcher pid 2c, children closed: 3 in about 31 s, parent_id of a child = 2c
show_pid lines: 12
  tuto.as.dispatch.exe: 3 regions dispatched
  tuto.as.price_region.exe: prices of SOUTH recomputed
  tuto.as.price_region.exe: prices of WEST recomputed
  tuto.as.price_region.exe: prices of NORTH recomputed
Go further
  • Inside a child, [PPID] holds the pid of the dispatcher.
  • log show_time gives the same lines for one pid only.
executed on MentDB

Clean the stack after a test campaign

After testing a worker, the developer leaves runs in every table: closed, in error and planned. The script creates that situation, counts the runs of the script per table, then empties each table for that script only and checks that nothing is left.

Pipeline
Script
admin
#1. A worker that fails on demand;
script create exe "tuto.as.campaign" false 1
	(param
		(var "[mode]" {true} "ok or fail" is_null:false is_empty:false "ok")
	;)
	"Test worker of the campaign"
{
	if (equal [mode] "fail") {exception (1) ("simulated failure");};
	sequence increment "tuto_as_campaign_done";
} "1";
script set stack "tuto.as.campaign.exe" 0 4 false;
sequence add "tuto_as_campaign_done" 0;
#2. Two ok and one failing run now, two runs next week;
-> "[t0]" (date now);
stack (date now) "tuto.as.campaign.exe" "[mode]" "ok";
stack (date now) "tuto.as.campaign.exe" "[mode]" "ok";
stack (date now) "tuto.as.campaign.exe" "[mode]" "fail";
stack (date dateaddt (date now) "DAY" 7) "tuto.as.campaign.exe" "[mode]" "ok";
stack (date dateaddt (date now) "DAY" 8) "tuto.as.campaign.exe" "[mode]" "ok";
-> "[i]" 0;
-> "[n]" 0;
while (and (< [i] 40) (< [n] 1)) {
	wait "1000";
	++ "[i]";
	json load "tuto_as_e" (string substring (stack search "error" "tuto.as.campaign.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 20) 39);
	-> "[n]" (json count "tuto_as_e" "/data");
};
while (and (< [i] 45) (< (sequence get current "tuto_as_campaign_done") 2)) {wait "1000"; ++ "[i]";};
#3. Runs of this script per table;
-> "[tables]" "wait,closed,error";
-> "[before]" "";
for (-> "[k]" 1) (<= [k] 3) (++ "[k]") {
	-> "[tb]" (atom get [tables] [k] ",");
	json load "tuto_as_q" (string substring (stack search [tb] "tuto.as.campaign.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 20) 39);
	-> "[before]" (concat [before] [tb] "=" (json count "tuto_as_q" "/data") " ");
};
#4. Empty each table for this script only;
-> "[del]" (concat "deleted wait=" (stack delete_wait_script "tuto.as.campaign.exe") " closed=" (stack delete_closed_script "tuto.as.campaign.exe") " error=" (stack delete_error_script "tuto.as.campaign.exe"));
-> "[after]" "";
for (-> "[k]" 1) (<= [k] 3) (++ "[k]") {
	-> "[tb]" (atom get [tables] [k] ",");
	json load "tuto_as_q" (string substring (stack search [tb] "tuto.as.campaign.exe" "create" [t0] (date dateaddt (date now) "HOUR" 1) ASC 1 20) 39);
	-> "[after]" (concat [after] [tb] "=" (json count "tuto_as_q" "/data") " ");
};
json unload "tuto_as_q";
json unload "tuto_as_e";
sequence remove "tuto_as_campaign_done";
script delete "tuto.as.campaign.exe";
concat "before: " [before] [_n_] [del] [_n_] "after:  " [after]
Result
mentdb
before: wait=2 closed=2 error=1
deleted wait=2 closed=2 error=1
after:  wait=0 closed=0 error=0
Go further
executed on MentDB

Same script: execute now or hand it to the stack

A VAT script is called synchronously by a web service and asynchronously by a batch. The script generates both call templates, runs it with execute and call, pushes it to the stack, and compares what the caller gets back in each case.

Pipeline
Script
admin
#1. A VAT script that also keeps the last gross amount in a sequence;
script create exe "tuto.as.vat" false 1
	(param
		(var "[net]" {type is_number [net]} "Net amount in cents" is_null:false is_empty:false "10000")
	;)
	"Gross amount in cents with 20 percent VAT"
{
	-> "[gross]" (/ (* [net] 120) 100);
	sequence update "tuto_as_vat_last" [gross];
	[gross];
} "The gross amount in cents";
sequence add "tuto_as_vat_last" 0;
-> "[gen]" (concat (script generate execute "tuto.as.vat.exe") [_n_] (script generate stack "tuto.as.vat.exe"));
#2. Synchronous: the caller waits and gets the value;
-> "[r1]" (execute "tuto.as.vat.exe" "[net]" 10000);
-> "[r2]" (call "tuto.as.vat.exe" "[net]" 2500);
#3. Asynchronous: the caller only gets a pid;
-> "[pid]" (stack (date now) "tuto.as.vat.exe" "[net]" 99900);
-> "[i]" 0;
while (and (< [i] 40) (not (equal (sequence get current "tuto_as_vat_last") "119880"))) {wait "1000"; ++ "[i]";};
-> "[r3]" (sequence get current "tuto_as_vat_last");
#4. Cleanup;
stack delete_closed_id [pid];
sequence remove "tuto_as_vat_last";
script delete "tuto.as.vat.exe";
concat "execute -> " [r1] ", call -> " [r2] [_n_] "stack -> pid " [pid] ", result read " [i] " s later in the sequence: " [r3] [_n_] "templates:" [_n_] [gen]
Result
mentdb
execute -> 12000, call -> 3000
stack -> pid 4o, result read 16 s later in the sequence: 119880
templates:
execute "tuto.as.vat.exe"
	"[net]" "10000"
;
stack_execute "2026-11-01 17:28:14" "tuto.as.vat.exe"
	"[net]" "10000"
;
Go further
  • include runs the script inline: its variables stay in the caller after the call.
  • A stack run returns nothing to the caller: write its result in a node, a sequence or a table.

Also in Programming

MB
MQL beginner
Variables, operators, if, loops and your first try/exception: learn MQL step by step.
10 recipes
MD
MQL developer
Atom lists, data types, constants, translations, session variables and error patterns.
10 recipes
BW
Backend & web service developer
Typed scripts, execute / call / include, generated calls and URLs, parameters and logs.
10 recipes
DT
Date & time developer
Calendars, business days, time zones, ISO and epoch, durations, quarters and week numbers.
10 recipes
SE
String & encoding developer
Cleaning, case styles, regex, URL/HTML/Base64 encodings, number bases and encryption.
10 recipes
DV
Data validation developer
IBAN, BIC, SIRET, Luhn, email, IP, UUID, JSON and date checks gathered in a validation report.
10 recipes
JD
JSON & document store developer
In-memory JSON documents, persistent nodes and sequences: build, walk, sort, clean up.
10 recipes
SD
Script developer
Full script lifecycle in the MQL editor: typed parameters, read back, generate, patch, deploy, copy, grant, delete.
10 recipes
EM
Execution modes
execute, call, include, sub_include, parallel, stack: which environment, which checks, which logs.
11 recipes
WS
Web service developer
A small REST resource with get, post, put and delete scripts: validation, JSON answers, codes, grants, URLs and API docs.
10 recipes
SD
Scheduler developer
Cron jobs and delayed runs: add, resume, pause, update and remove jobs, prove they ran, and postpone stack runs with a delay condition.
10 recipes
ED
Event developer
Event-driven scripts: file watchers that push each file event to the stack, validation, archiving, errors and safe restarts.
10 recipes
CC
Configuration & connections
Parameters (private, described, locked, deployed), environment variables, sessions and connections used by your scripts.
12 recipes
VD
Versioning & deployment
Group your scripts, commit, compare, roll back, export, import and package a release.
10 recipes
DE
Debugging & error handling
Coded errors with try/exception, logs with correlation ids, timing, metrics and defensive checks.
10 recipes
SD
SQL developer in MQL
Connections, DDL and DML with sql encode, transactions, cursors, parse, and exports to JSON, CSV, Excel and PDF on a real local database. Run the recipes in order: the first one creates the tuto_db_h2 database.
10 recipes
CD
CRUD & data API developer
Generate SCRUD scripts from a table, patch and install them, write typed read and write APIs, check data quality and manage connections per environment. Uses the tuto_db_h2 database of the SQL developer recipes; the last recipe removes it.
10 recipes
WA
Web application developer
Create a web app, its host name, pages, ajax forms, menu and grants, then remove it all.
10 recipes
AI
Algorithms in MQL
Sorting, searching, recursion, data structures and number algorithms: MQL is a real programming language.
10 recipes
CF
Control flow
if, if force, case, switch, mode, for, while, repeat, break, continue, parallel and synchronized on real cases.
10 recipes
TM
The MQL language in depth
Data types and null, booleans, comments, variables and scope, eval and mql blocks, coded errors and try.
10 recipes
SU
Screen & UI developer (sajax)
Build the screens of MentDB web apps in MQL: Bootstrap grids, forms and every control, ajax buttons, alerts, refreshes and charts.
10 recipes