Variables
Variables are a shared key/value store available to every pipeline and script in the gateway. They hold the state that does not belong to a tag and does not deserve a database table: a counter, a last-run timestamp, a feature flag, a cached token.
Storage modes
| Mode | Survives restart | Use for |
|---|---|---|
| Persistent | Yes | Counters, last-processed markers, configuration flags |
| Volatile | No | Caches, in-flight state, anything cheap to recompute |
The workspace has a default mode; individual variables can override it at write time.
| Action | Permission |
|---|---|
| List variables and read the default mode | authenticated |
| Change the default storage mode | Configure variable storage |
| Delete a variable | Edit pipelines |
Using variables
Python
last = system.vars.get("last_export_ts", 0)
rows = system.db.runQuery(
"SELECT * FROM events WHERE ts > ?", [last], "ReportingDB"
)
if rows:
system.vars.set("last_export_ts", rows[-1]["ts"], "persistent")
count = system.vars.incr("exports_today") # atomic
JavaScript
const last = await system.vars.get("last_export_ts", 0);
await system.vars.set("last_export_ts", Date.now(), "persistent");
const count = await system.vars.incr("exports_today");
API
| Call | Returns |
|---|---|
get(name, defaultValue?) | The value, or defaultValue when unset |
set(name, value, storage?) | true. storage omitted ⇒ workspace default |
delete(name) | true |
keys() | Every variable name |
incr(name, by?) | The new value after atomically adding by (default 1) |
list() | Every variable with value, type, storage and last-updated time |
Use incr for counters
# ❌ race: two pipelines can read the same value and both write n+1
n = system.vars.get("count", 0)
system.vars.set("count", n + 1)
# ✅ atomic
n = system.vars.incr("count")
Variables are shared across concurrently executing pipelines. Read-modify-write loses updates;
incr does not.
Patterns
Incremental extract
last = system.vars.get("etl_watermark", "1970-01-01T00:00:00Z")
rows = system.db.runNamedQuery("changes_since", {"since": last})
# ... process ...
if rows:
system.vars.set("etl_watermark", rows[-1]["updated_at"], "persistent")
Restart-safe: the watermark is persistent, so a restart resumes rather than reprocessing.
Run-once-per-period guard
import datetime
today = datetime.date.today().isoformat()
if system.vars.get("daily_report_date") == today:
return None # already ran
# ... produce the report ...
system.vars.set("daily_report_date", today, "persistent")
Cached credential
token = system.vars.get("api_token")
if not token:
token = fetch_token()
system.vars.set("api_token", token, "volatile") # not written to disk
Variables, tags or a database?
| Need | Use |
|---|---|
| A process value with quality, history, alarms | A tag (internal tag if QUBIQ owns it) |
| Coordination state between pipelines | A variable |
| Something an operator should see or change on a screen | An internal tag — variables are not bindable |
| Anything queried, reported on or retained long-term | A database |
A variable is deliberately invisible to the visualization layer. If a value belongs on a screen, it belongs in a tag.
Cautions
- Not encrypted. Do not store credentials you would not want an operator with script access to read. Use a connection's credential storage instead.
- Not audited per write. Variable changes are not in the security audit journal.
- Not namespaced. One flat name space per gateway — prefix by concern
(
etl_watermark,report_lastrun) so unrelated pipelines do not collide. - Volatile means volatile. A restart clears it, including a supervisor-initiated service restart.