Octov0.11.7
ReferenceBlocks

Storage

Read, write, and delete objects; invalidate cache entries.

The storage blocks read and write JSON objects in the runtime's key-value store. They need no connector: the store is a runtime service, scoped per deployment on the platform, so a flow's objects are isolated from other deployments. The object blocks confine themselves to the user namespaces, so user keys never collide with internal runtime state (agent memory, cache entries, secrets). invalidate-cache is documented here too, but it targets cache entries rather than objects.

Key and value fields are CEL expressions evaluated against the message (body, vars, eventID, correlationID, env, now), compiled once at startup.

Picking a tier

Each of the three object blocks takes a volatile setting choosing which durability tier it operates in. invalidate-cache does not: cache entries are always volatile.

volatilePlatformStandaloneUse it for
false (default)Postgresserialized to --storage-diranything that has to survive a restart
trueRedisprocess memoryvalues whose loss costs a recompute

Volatile is faster and cheaper (no database row, no transaction) but promises nothing: a volatile object may vanish on a restart, and on the platform it may be evicted while the process is still running.

An install with no Redis falls back to Postgres for volatile objects, so they cost the same as persistent ones while still offering the weaker guarantee. Write against the guarantee, not against the backend.

The two tiers are separate keyspaces. A read has to name the same tier the write used; reading the other one is a clean miss, not an error. The default is the persistent tier, so a flow written before this setting existed keeps its behavior.

object-read

Reads an object from the store into the body or a variable. When the key is absent and default is set, the default is folded in exactly like a hit; otherwise a miss nulls the body (body mode) or leaves the variable unset. Set existsVar to also record whether the key was found.

SettingTypeRequiredDefaultDescription
keyexpression (string)YesnoneEvaluated to the object key.
asstringNononeVariable to store the object under; empty replaces the body.
defaultexpressionNononeEvaluated on a miss; its result is folded in like a hit (into the variable, or the body).
existsVarstringNononeNames a variable the block writes a boolean into: true on a hit, false when the default/null path was taken. Use it to tell a default apart from a stored value.
volatileboolNofalseRead from the volatile tier. Must match what the write used.

Errors: the block errors on a store failure, a key/default expression failure, or a stored value that cannot be decoded as JSON. A missing key is not an error.

- type: object-read
  name: load-profile
  settings:
    key: '"profile:" + body.id'
    as: profile
    default: '{"plan": "free"}'
    existsVar: profileExists

object-write

Writes an object to the store under a key. The message passes through unchanged.

SettingTypeRequiredDefaultDescription
keyexpression (string)YesnoneEvaluated to the object key.
valueexpressionNomessage bodyValue to store (JSON-encoded). Empty stores the whole body.
volatileboolNofalseWrite to the volatile tier instead of the persistent one.

Writes use optimistic concurrency: the block re-reads the current version and retries on a version conflict (up to 5 attempts) before erroring. The retry re-reads the version only; it writes the value your value expression already produced from the original message and does not re-evaluate it against what the other writer stored.

Do not build a counter this way. object-read then object-write is not an atomic increment: two runs that each read 4 will each write 5, both will succeed, and one increment is lost. These blocks are for values that tolerate a lost update.

- type: object-write
  name: save-profile
  settings:
    key: '"profile:" + body.id'
    value: '{"plan": body.plan}'

object-delete

Deletes an object from the store by key. The delete is unconditional and idempotent: a missing key is not an error. The message passes through unchanged.

SettingTypeRequiredDefaultDescription
keyexpression (string)YesnoneEvaluated to the object key to delete.
volatileboolNofalseDelete from the volatile tier. Must match the tier the object was written to; a delete addresses one tier, not both.
- type: object-delete
  settings:
    key: '"profile:" + body.id'

A volatile round trip. A fetched exchange rate may be dropped without harm, because a miss just fetches it again:

- type: object-read
  settings:
    key: '"rate:" + body.currency'
    as: rate
    existsVar: rateCached
    volatile: true
# ... fetch the rate when vars.rateCached is false ...
- type: object-write
  settings:
    key: '"rate:" + body.currency'
    value: "vars.rate"
    volatile: true

invalidate-cache

Evicts a cache-scope entry by its key so the next run recomputes. Cache entries always live in the volatile tier, so this block has no volatile setting. The key expression must produce the same value as the cache-scope's key expression: entries are stored under a hash of the evaluated key, so matching values target the same entry. The message passes through unchanged.

SettingTypeRequiredDefaultDescription
keyexpression (string)YesnoneEvaluated to the cache key to evict; matches the key of the cache-scope that wrote the entry.

Eviction is idempotent: invalidating a key with no entry is not an error.

- name: reset
  process:
    - type: invalidate-cache
      name: bust-report
      settings:
        key: '"report"'
    - type: set-payload
      settings:
        value: '{"reset": true}'

On this page