Profiles / Programming
EM

Execution modes

execute, call, include, sub_include, parallel, stack: which environment, which checks, which logs.

11 recipes on this page · open in the interactive view

Recipes
  1. execute runs in a new environment
  2. call shares the caller's variables
  3. include vs call: checks and activity log
  4. sub_include: build include requests with current values
  5. Errors propagate through nested executes
  6. JSON results computed in parallel
  7. Run the generated execute, call and include
  8. Stack run postponed until a gate opens
  9. Activity log of a run and its children
  10. Lost updates and synchronized
  11. Context of a run: user, session, pid, traces
executed on MentDB

execute runs in a new environment

A script reads a variable it does not declare and sets another one. Called with `execute`, it only sees its parameters: the caller's currency is undefined inside and the caller's tax is untouched after.

Pipeline
Script
admin
#1. A script that reads a variable it does not declare and sets another one;
script create post "tuto.sc.env.probe" false 1
	(param
		(var "[amount]" {type is_double [amount]} "Amount" is_null:false is_empty:false "100")
	;)
	"Show what the script sees"
{
	-> "[seen]" (if (env exist var "[currency]") {[currency]} {"undefined"});
	-> "[tax]" (math round (* [amount] 0.2) 2);
	json load "tuto_sc_env" (env show);
	-> "[vars]" (string regexp_replace (json fields "tuto_sc_env" "/") "\s+" "");
	json unload "tuto_sc_env";
	concat "inside: amount=" [amount] ", currency=" [seen] ", tax=" [tax] [_n_] "variables inside: " [vars];
} "A sentence";
#2. The caller has its own variables;
-> "[currency]" "EUR";
-> "[tax]" "caller value";
#3. execute runs the script in a NEW environment;
-> "[r]" (execute "tuto.sc.env.probe.post" "[amount]" 250);
script delete "tuto.sc.env.probe.post";
concat [r] [_n_] "caller after execute: tax=" [tax] ", currency=" [currency];
Result
mentdb
inside: amount=250, currency=undefined, tax=50
variables inside: ["[amount]","[PPID]","[tax]","[seen]"]
caller after execute: tax=caller value, currency=EUR
Go further
  • Pass everything the script needs as a parameter: that is what makes it a reusable service.
  • [PPID] inside is the pid of the caller when the caller is a logged script.
  • env del var removes a variable you do not want to leak.
executed on MentDB

call shares the caller's variables

A cart helper computes VAT and gross amounts. With `call` it reads a rate the caller prepared and writes its results back into the caller's variables; with `execute` the same script fails because the rate does not exist in a new environment.

Pipeline
Script
admin
#1. A helper that computes several values at once;
script create post "tuto.sc.cart.totals" false 1
	(param
		(var "[net]" {type is_double [net]} "Net amount" is_null:false is_empty:false "100")
	;)
	"Set the totals of a cart in the caller"
{
	-> "[vat]" (math round (* [net] (/ [vat_rate] 100)) 2);
	-> "[gross]" (math round (+ [net] [vat]) 2);
	-> "[calls]" (+ [calls] 1);
	"done";
} "done, and the variables vat, gross, calls";
#2. The caller prepares a rate that is NOT a parameter;
-> "[vat_rate]" 8.5;
-> "[calls]" 0;
#3. call shares the environment: the script reads vat_rate and writes back;
-> "[r1]" (call "tuto.sc.cart.totals.post" "[net]" 120);
-> "[line1]" (concat "call 1: " [r1] ", vat=" [vat] ", gross=" [gross] ", calls=" [calls]);
-> "[r2]" (call "tuto.sc.cart.totals.post" "[net]" 80);
-> "[line2]" (concat "call 2: " [r2] ", vat=" [vat] ", gross=" [gross] ", calls=" [calls] ", net is now " [net]);
#4. The same script with execute: nothing comes back;
-> "[vat]" "untouched";
try {execute "tuto.sc.cart.totals.post" "[net]" 50;} {-> "[line3]" (concat "execute: error, " (string regexp_extract [err] "\[\[\[ (.*) \]\]\]" 1));} "[err]";
script delete "tuto.sc.cart.totals.post";
concat [line1] [_n_] [line2] [_n_] [line3] ", vat=" [vat];
Result
mentdb
call 1: done, vat=10.2, gross=130.2, calls=1
call 2: done, vat=6.8, gross=86.8, calls=2, net is now 80
execute: error, Sorry, the variable name [vat_rate] does not exist., vat=untouched
Go further
  • call still checks the declared parameters, in order.
  • The parameters overwrite the caller's variables with the same name: net is now 80.
  • Use call for helpers of one application, execute for services.
executed on MentDB

include vs call: checks and activity log

The same logged fee script is run with `call` and `include`. call refuses a missing or invalid parameter and gets an activity pid; include just copies the variables and runs the code: it uses the caller's pct, has no pid, and the bad value fails inside the code.

Pipeline
Script
admin
#1. A logged script with two typed parameters;
script create post "tuto.sc.fee.calc" true 1
	(param
		(var "[base]" {type is_double [base]} "Base amount" is_null:false is_empty:false "100")
		(var "[pct]" {type is_double [pct]} "Fee in percent" is_null:false is_empty:false "2.5")
	;)
	"Compute a fee"
{
	concat "fee=" (math round (* [base] (/ [pct] 100)) 2) ", pid=" (if (is null or empty [PID]) {"none"} {"set"});
} "fee and whether an activity pid exists";
-> "[pct]" 3;
#2. call checks every parameter, in order, and writes the activity log;
-> "[r1]" (call "tuto.sc.fee.calc.post" "[base]" 200 "[pct]" 1.5);
try {call "tuto.sc.fee.calc.post" "[base]" 200;} {-> "[r2]" (string regexp_extract [err] "Sorry, (.*)\." 1);} "[err]";
try {call "tuto.sc.fee.calc.post" "[base]" "abc" "[pct]" 1;} {-> "[r3]" (string regexp_extract [err] "Sorry, (.*) \(" 1);} "[err]";
#3. include only copies the variables, then runs the code: no check, no log;
-> "[pct]" 3;
-> "[i1]" (include "tuto.sc.fee.calc.post" "[base]" 200);
try {include "tuto.sc.fee.calc.post" "[base]" "abc";} {-> "[i2]" (concat "error inside the code: " (string regexp_extract [err] "\[\[\[ (.*) \]\]\]" 1));} "[err]";
script delete "tuto.sc.fee.calc.post";
concat "call OK: " [r1] [_n_] "call with 1 param: " [r2] [_n_] "call with abc: " [r3] [_n_] "include with 1 param, pct from the caller: " [i1] [_n_] "include with abc: " [i2];
Result
mentdb
call OK: fee=3, pid=set
call with 1 param: the script 'tuto.sc.fee.calc.post' does not has a valid number of parameters
call with abc: the variable '[base]' in the script 'tuto.sc.fee.calc.post' is not valid
include with 1 param, pct from the caller: fee=6, pid=none
include with abc: error inside the code: Sorry, the number to round cannot be null.
Go further
  • For a restricted user, include checks the parameters like call.
  • Use include for code fragments of your own scripts, never for public input.
  • log search with the script name shows that include writes no Begin/End lines.
executed on MentDB

sub_include: build include requests with current values

`script generate sub_include` gives an expression that writes an include request with the values of the current variables, safely encoded. Two requests are built for two products, kept as text, and run later.

Pipeline
Script
admin
#1. A script that formats a label;
script create post "tuto.sc.label.format" false 1
	(param
		(var "[name]" {type is_varchar [name] 40} "Product name" is_null:false is_empty:false "Desk")
		(var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "99")
	;)
	"Format a product label"
{
	concat (string upper [name]) " - " [price] " EUR";
} "The label";
#2. sub_include gives an expression that writes the include request with the CURRENT values;
-> "[template]" (script generate sub_include "tuto.sc.label.format.post");
#3. Build requests for two products, quotes are encoded safely;
-> "[name]" "Desk \"Oslo\"";
-> "[price]" 249;
-> "[req1]" (eval [template]);
-> "[name]" "Lamp";
-> "[price]" 39.9;
-> "[req2]" (eval [template]);
#4. Run the generated requests later, for example from a job or a queue;
-> "[out]" (concat (eval [req1]) " | " (eval [req2]));
script delete "tuto.sc.label.format.post";
concat "template:" [_n_] [template] [_n_] "request 1:" [_n_] [req1] [_n_] "results: " [out];
Result
mentdb
template:
concat "include \"tuto.sc.label.format.post\"
	\"[name]\" \"" (mql encode [name]) "\"
	\"[price]\" \"" (mql encode [price]) "\"
;"
request 1:
include "tuto.sc.label.format.post"
	"[name]" "Desk \"Oslo\""
	"[price]" "249"
;
results: DESK "OSLO" - 249 EUR | LAMP - 39.9 EUR
Go further
  • The quotes of Desk "Oslo" are encoded by mql encode.
  • Put the requests in a job or a stack entry to run them later.
  • script generate include gives the same request with the example values.
executed on MentDB

Errors propagate through nested executes

An order script executes a stock script that raises business errors with an id. The top-level caller catches the error two levels down, maps the id to a code and counts the stack lines of the trace.

Pipeline
Script
admin
#1. A low-level script that raises business errors with an id;
script create post "tuto.sc.stock.reserve" false 1
	(param
		(var "[sku]" {type is_varchar [sku] 12} "Product code" is_null:false is_empty:false "BK-0042")
		(var "[qty]" {type is_integer [qty] 4} "Quantity" is_null:false is_empty:false "2")
	;)
	"Reserve stock"
{
	-> "[stock]" (switch [sku] ("BK-0042") {5} ("BK-0099") {0} {-1});
	if (== [stock] -1) {exception ("404") (concat "unknown product " [sku]);};
	if (> [qty] [stock]) {exception ("409") (concat "only " [stock] " left for " [sku]);};
	concat "reserved " [qty] " x " [sku];
} "A confirmation";
#2. A top-level script that executes it without catching anything;
script create post "tuto.sc.order.place" false 1
	(param
		(var "[sku]" {type is_varchar [sku] 12} "Product code" is_null:false is_empty:false "BK-0042")
		(var "[qty]" {type is_integer [qty] 4} "Quantity" is_null:false is_empty:false "2")
	;)
	"Place an order"
{
	-> "[res]" (execute "tuto.sc.stock.reserve.post" "[sku]" [sku] "[qty]" [qty]);
	concat "order OK, " [res];
} "A confirmation";
#3. The caller catches the error two levels down and maps it to a code;
-> "[report]" "";
for (-> "[i]" 0) (< [i] 3) (++ "[i]") {
	-> "[sku]" (atom get "BK-0042,BK-0099,ZZ-1" (+ [i] 1) ",");
	try {
		-> "[line]" (execute "tuto.sc.order.place.post" "[sku]" [sku] "[qty]" 3);
	} {
		-> "[code]" (string regexp_extract [err] "([0-9]{3}): ([^\n]*)$" 1);
		-> "[line]" (concat "error " [code] ": " (string regexp_extract [err] "([0-9]{3}): ([^\n]*)$" 2) " - " (string regexp_count [err] ">>>") " stack lines");
	} "[err]";
	-> "[report]" (concat [report] [sku] " -> " [line] [_n_]);
};
script delete "tuto.sc.order.place.post";
script delete "tuto.sc.stock.reserve.post";
[report];
Result
mentdb
BK-0042 -> order OK, reserved 3 x BK-0042
BK-0099 -> error 409: only 0 left for BK-0099 - 6 stack lines
ZZ-1 -> error 404: unknown product ZZ-1 - 6 stack lines
Go further
  • The message keeps every level as a line n >>> line: useful in logs, too long for an API answer.
  • Catch at the level that knows what to answer, let the others propagate.
  • stack replay_error_id replays a stacked run that failed with an error.
executed on MentDB

JSON results computed in parallel

The KPI of three regions are computed at the same time with `parallel` and `execute`. Each run returns a JSON document; the caller reads the status and result of each run and consolidates sales, orders and the average basket.

Pipeline
Script
admin
#1. A script that returns a JSON document;
script create post "tuto.sc.region.kpi" false 1
	(param
		(var "[region]" {type is_enum [region] "north,south,east"} "Region" is_null:false is_empty:false "north")
	;)
	"KPI of a region"
{
	-> "[sales]" (switch [region] ("north") {1250.5} ("south") {980} {1430.25});
	-> "[orders]" (switch [region] ("north") {41} ("south") {37} {52});
	concat "{\"region\": \"" [region] "\", \"sales\": " [sales] ", \"orders\": " [orders] "}";
} "JSON: region, sales, orders";
#2. Three regions computed at the same time;
json load "tuto_sc_par" (parallel
	{execute "tuto.sc.region.kpi.post" "[region]" "north";}
	{execute "tuto.sc.region.kpi.post" "[region]" "south";}
	{execute "tuto.sc.region.kpi.post" "[region]" "east";}
);
#3. Read each returned JSON and consolidate;
json load "tuto_sc_tot" "{\"regions\": [], \"sales\": 0, \"orders\": 0}";
for (-> "[i]" 0) (< [i] (json count "tuto_sc_par" "/")) (++ "[i]") {
	json load "tuto_sc_one" (json select "tuto_sc_par" (concat "/[" [i] "]/result"));
	json iarray "tuto_sc_tot" "/regions" (concat (json select "tuto_sc_one" "/region") " " (json select "tuto_sc_par" (concat "/[" [i] "]/status"))) STR;
	json uobject "tuto_sc_tot" "/" "sales" (+ (json select "tuto_sc_tot" "/sales") (json select "tuto_sc_one" "/sales")) NUM;
	json uobject "tuto_sc_tot" "/" "orders" (+ (json select "tuto_sc_tot" "/orders") (json select "tuto_sc_one" "/orders")) INT;
	json unload "tuto_sc_one";
};
json iobject "tuto_sc_tot" "/" "avg_basket" (math round (/ (json select "tuto_sc_tot" "/sales") (json select "tuto_sc_tot" "/orders")) 2) NUM;
-> "[out]" (json doc "tuto_sc_tot");
json unload "tuto_sc_par";
json unload "tuto_sc_tot";
script delete "tuto.sc.region.kpi.post";
[out];
Result
mentdb
{
  "regions": [
    "north OK",
    "south OK",
    "east OK"
  ],
  "orders": 130,
  "avg_basket": 28.16,
  "sales": 3660.75
}
Go further
  • parallel returns an array of {result, status}: check status before reading result.
  • Use synchronized if the parallel runs write to the same object.
  • json sort can rank the regions in the final document.
executed on MentDB

Run the generated execute, call and include

The editor generates the three kinds of calls of a loan script, filled with the documented examples. They are run as they are, then with other values after a string replacement.

Pipeline
Script
admin
#1. A script with documented examples on its parameters;
script create post "tuto.sc.loan.monthly" false 1
	(param
		(var "[capital]" {type is_double [capital]} "Borrowed amount" is_null:false is_empty:false "200000")
		(var "[rate]" {type is_double [rate]} "Yearly rate in percent" is_null:false is_empty:false "3.6")
		(var "[years]" {type is_integer [years] 2} "Duration in years" is_null:false is_empty:false "25")
	;)
	"Monthly payment of a loan"
{
	-> "[r]" (/ [rate] 1200);
	-> "[n]" (* [years] 12);
	math round (/ (* [capital] [r]) (- 1 (math pow (+ 1 [r]) (- 0 [n])))) 2;
} "The monthly payment";
#2. The editor can generate the three kinds of calls, filled with the examples;
-> "[gen_exe]" (script generate execute "tuto.sc.loan.monthly.post");
-> "[gen_call]" (script generate call "tuto.sc.loan.monthly.post");
-> "[gen_inc]" (script generate include "tuto.sc.loan.monthly.post");
#3. Run them as they are, then with other values;
-> "[a]" (eval [gen_exe]);
-> "[b]" (eval (string replace [gen_call] "\"25\"" "\"15\""));
-> "[c]" (eval (string replace (string replace [gen_inc] "\"200000\"" "\"150000\"") "\"3.6\"" "\"3.1\""));
script delete "tuto.sc.loan.monthly.post";
concat [gen_exe] [_n_] "example values: " [a] [_n_] "call over 15 years: " [b] [_n_] "include, 150000 at 3.1%: " [c];
Result
mentdb
execute "tuto.sc.loan.monthly.post"
	"[capital]" "200000"
	"[rate]" "3.6"
	"[years]" "25"
;
example values: 1012.01
call over 15 years: 1439.61
include, 150000 at 3.1%: 719.14
Go further
  • Good examples on the parameters make the generated calls runnable test cases.
  • script generate stack gives the stack version of the same call.
  • Store the generated calls as smoke tests of a deployment.
executed on MentDB

Stack run postponed until a gate opens

A nightly import waits for a gate parameter. It is pushed in the stack now; on the first pass of the stack the delay condition is false, so the run is postponed by 5 s; the gate is opened and the next pass imports the file. Takes about 30 to 60 seconds.

Pipeline
Script
admin
#1. A gate parameter and a node to receive the result;
parameter add "tuto_sc_gate" "closed" "Tutorial: opens the nightly import" false;
node create "tuto_sc_import";
#2. The import must wait until the gate is open;
script create post "tuto.sc.import.batch" false 5
	(param
		(var "[file]" {type is_varchar [file] 40} "File to import" is_null:false is_empty:false "orders.csv")
	;)
	"Import a file when the gate is open"
{
	node iobject "tuto_sc_import" "/" "done" (concat [file] " imported at " (date systime)) STR;
	"imported";
} "imported";
script set delay "tuto.sc.import.batch.post" 5 sec {
	equal (parameter get value "tuto_sc_gate") "open";
};
#3. Push it in the stack now, the server checks the stack every 30 s;
-> "[pid]" (stack (date now) "tuto.sc.import.batch.post" "[file]" "orders_2026-11-01.csv");
-> "[t0]" (date systimestamp);
#4. First pass: the condition is false, the run is postponed;
-> "[n]" 0;
while (and (< [n] 40) (is null (json load select "tuto_sc_st" (stack get [pid]) "/lastattempt"))) {wait 1000; ++ "[n]";};
-> "[first]" (concat "pass 1 after ~" [n] " s: state " (json load select "tuto_sc_st" (stack get [pid]) "/state") ", next run " (json load select "tuto_sc_st" (stack get [pid]) "/dtexe"));
#5. Open the gate and wait for the next pass;
parameter update "tuto_sc_gate" "open";
-> "[n]" 0;
while (and (< [n] 40) (not (node exist "tuto_sc_import") )) {wait 1000; ++ "[n]";};
while (and (< [n] 40) (is null (node select "tuto_sc_import" "/done"))) {wait 1000; ++ "[n]";};
-> "[second]" (concat "pass 2 after ~" [n] " s: " (node select "tuto_sc_import" "/done") ", closed: " (not (is null (json load select "tuto_sc_st" (stack get [pid]) "/dtclosed"))));
#6. Clean up the stack history, the script, the node and the parameter;
stack delete_closed_script "tuto.sc.import.batch.post";
script delete "tuto.sc.import.batch.post";
node delete "tuto_sc_import";
parameter remove "tuto_sc_gate";
concat [first] [_n_] [second];
Result
mentdb
pass 1 after ~19 s: state W, next run 2026-11-01 17:26:46
pass 2 after ~30 s: orders_2026-11-01.csv imported at 17:27:30, closed: 1
Go further
  • The stack is processed every 30 s, so the postponed run waits for the next pass.
  • stack search lists the closed or failed runs of a script.
  • Clean the history with stack delete_closed_script for your own script only.
executed on MentDB

Activity log of a run and its children

A logged batch executes three logged steps; step 3 fails and the batch goes on. The batch returns its pid, and `restricted show_pid` returns the log lines of the run and of its child processes: begin, messages, errors, end.

Pipeline
Script
admin
#1. Two logged scripts: a batch that executes a step;
script create post "tuto.sc.batch.step" true 1
	(param (var "[n]" {type is_integer [n] 3} "Step number" is_null:false is_empty:false "1");)
	"One step of the batch"
{
	log write (concat "step " [n] " processed") "OK" "step" [n];
	if (== [n] 3) {exception ("500") ("step 3 failed: disk full");};
	"ok";
} "ok";
script create post "tuto.sc.batch.run" true 1
	(param (var "[steps]" {type is_integer [steps] 3} "Number of steps" is_null:false is_empty:false "3");)
	"Run the steps of the batch"
{
	for (-> "[i]" 1) (<= [i] [steps]) (++ "[i]") {
		try {execute "tuto.sc.batch.step.post" "[n]" [i];} {log write "a step failed, batch continues" "KO" "step" [i];} "[e]";
	};
	[PID];
} "The pid of the run";
#2. Run the batch, it returns its own pid;
-> "[pid]" (execute "tuto.sc.batch.run.post" "[steps]" 3);
#3. Read the activity log of the run and of its linked processes;
json load "tuto_sc_log" (string regexp_extract (restricted show_pid [pid]) "(\{.*)$" 1);
-> "[trace]" "";
for (-> "[i]" 0) (< [i] (json count "tuto_sc_log" "/data")) (++ "[i]") {
	-> "[row]" (concat "/data/[" [i] "]");
	-> "[trace]" (concat [trace] [_n_] (string upper (json select "tuto_sc_log" (concat [row] "/status"))) " " (string replace (json select "tuto_sc_log" (concat [row] "/script")) "tuto.sc." "") " " (if (equal (json select "tuto_sc_log" (concat [row] "/parent_pid")) "") {"root "} {"child"}) " " (string regexp_extract (json select "tuto_sc_log" (concat [row] "/msg")) "(?:.*; )?([^;\n]*)$" 1));
};
json unload "tuto_sc_log";
#4. Server settings of the log;
-> "[cfg]" (concat "retention " (log retention day) " days, archive files of " (log archive size) " MB");
script delete "tuto.sc.batch.run.post";
script delete "tuto.sc.batch.step.post";
concat (string trim [trace]) [_n_] [cfg];
Result
mentdb
OK batch.run.post root  [---- Begin script ----]
OK batch.step.post child [---- Begin script ----]
OK batch.step.post child step 1 processed
OK batch.step.post child [---- End script ----]
OK batch.step.post child [---- Begin script ----]
OK batch.step.post child step 2 processed
OK batch.step.post child [---- End script ----]
OK batch.step.post child [---- Begin script ----]
OK batch.step.post child step 3 processed
KO batch.step.post child 500: step 3 failed: disk full
KO batch.run.post root a step failed, batch continues
OK batch.run.post root  [---- End script ----]
retention 50 days, archive files of 30 MB
Go further
  • After a failed step the caller's script name is restored: the KO line written by the batch is stamped batch.run.post, not with the step's name.
  • log search filters by status, script, client key and value.
  • log show_time gives the same lines for a pid.
executed on MentDB

Lost updates and synchronized

Four parallel hits increment a shared counter stored in a node. Without a lock they read the same value and the counter ends at 1; inside a `synchronized` block the four updates are kept.

Pipeline
Script
admin
#1. A shared counter in a node;
node create "tuto_sc_counter";
node iobject "tuto_sc_counter" "/" "unsafe" 0 INT;
node iobject "tuto_sc_counter" "/" "safe" 0 INT;
#2. A script that reads, waits, then writes back: a classic lost update;
script create post "tuto.sc.counter.hit" false 1
	(param (var "[mode]" {type is_enum [mode] "unsafe,safe"} "Protection" is_null:false is_empty:false "safe");)
	"Increment a shared counter"
{
	if (equal [mode] "safe") {
		synchronized ("tuto_sc_counter_lock") {
			-> "[v]" (node select "tuto_sc_counter" "/safe");
			wait 100;
			node uobject "tuto_sc_counter" "/" "safe" (+ [v] 1) INT;
		};
	} {
		-> "[v]" (node select "tuto_sc_counter" "/unsafe");
		wait 100;
		node uobject "tuto_sc_counter" "/" "unsafe" (+ [v] 1) INT;
	};
	"ok";
} "ok";
#3. Four hits at the same time, without and with synchronized;
parallel
	{execute "tuto.sc.counter.hit.post" "[mode]" "unsafe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "unsafe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "unsafe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "unsafe";};
parallel
	{execute "tuto.sc.counter.hit.post" "[mode]" "safe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "safe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "safe";}
	{execute "tuto.sc.counter.hit.post" "[mode]" "safe";};
-> "[out]" (concat "4 parallel hits - without lock: " (node select "tuto_sc_counter" "/unsafe") ", with synchronized: " (node select "tuto_sc_counter" "/safe"));
script delete "tuto.sc.counter.hit.post";
node delete "tuto_sc_counter";
[out];
Result
mentdb
4 parallel hits - without lock: 1, with synchronized: 4
Go further
  • The lock key is a string: use one key per protected object.
  • Keep the synchronized block short, other runs wait.
  • For a simple id, sequence increment is the usual tool.
executed on MentDB

Context of a run: user, session, pid, traces

A logged script reports who runs it, in which session and with which pid. Two executes run in the caller's session with a new pid each; the open sessions are counted without printing them, and the two traces are found back in the text log by pid.

Pipeline
Script
admin
#1. A logged script that reports its context;
script create post "tuto.sc.context.probe" true 1
	(param (var "[step]" {type is_varchar [step] 20} "Step name" is_null:false is_empty:false "check");)
	"Report the run context"
{
	log trace (concat "tuto.sc context " [step] " pid " [PID]);
	concat "{\"user\": \"" (who) "\", \"sid\": " (sid) ", \"pid\": \"" [PID] "\"}";
} "JSON";
#2. The caller context, before;
-> "[sid]" (sid);
#3. Two runs: same user and session, a new pid each time;
json load "tuto_sc_a" (execute "tuto.sc.context.probe.post" "[step]" "first");
json load "tuto_sc_b" (execute "tuto.sc.context.probe.post" "[step]" "second");
-> "[p1]" (json select "tuto_sc_a" "/pid");
-> "[p2]" (json select "tuto_sc_b" "/pid");
-> "[runs]" (concat "run 1 pid " [p1] ", run 2 pid " [p2] ", same session as the caller: " (and (equal (json select "tuto_sc_a" "/sid") [sid]) (equal (json select "tuto_sc_b" "/sid") [sid])) ", user " (json select "tuto_sc_b" "/user"));
json unload "tuto_sc_a";
json unload "tuto_sc_b";
#4. The other sessions open on the server, counted but not printed;
json load "tuto_sc_ses" (sessions);
-> "[ctx]" (concat "open sessions: count sessions " (count sessions) ", sessions list " (json count "tuto_sc_ses" "/") ", commands of this connection " (cmdid));
json unload "tuto_sc_ses";
#5. The traces of these two runs in the text log;
-> "[log]" (log show 50);
-> "[traces]" (concat (string regexp_extract [log] (concat "(tuto\.sc context \w+ pid " [p1] ")(\s|$)") 1) [_n_] (string regexp_extract [log] (concat "(tuto\.sc context \w+ pid " [p2] ")(\s|$)") 1));
script delete "tuto.sc.context.probe.post";
concat [runs] [_n_] [ctx] [_n_] (string trim [traces]);
Result
mentdb
run 1 pid 47, run 2 pid 48, same session as the caller: 1, user admin
open sessions: count sessions 4, sessions list 4, commands of this connection 2
tuto.sc context first pid 47
tuto.sc context second pid 48
Go further
  • sessions lists the open sessions with their users and IPs: do not show it to end users.
  • [PID] exists only when the activity log of the script is on.
  • log write goes to the log database, log trace to the text log.

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
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
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
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