Attribute
View as MarkdownAn Attribute is durable data that belongs to one Flow run. Steps and RPCs read and write it. Dex stores it with the Flow, so you do not need a database for intermediate state.
An Attribute lives as long as its Flow. Dex deletes it when it deletes the Flow, after the retention period that follows Flow closure. To keep the data longer, see Keep data after the Flow.
When to use an Attribute
Use an Attribute for state that the Flow decides on: an order status, a counter, a customer profile, or the result of an earlier Step. Use a Channel for messages that a Step waits for and consumes. Use a Stream for live progress that a user can afford to lose.
Use an Attribute
Declare each Attribute in the Flow's persistence schema. A Step or RPC can then read or write it. The Worker rejects an Attribute that the schema does not declare. Setting an Attribute to null deletes it.
Attribute writes take effect only when the Step method or RPC succeeds. See Step guarantees.
class AttributeStep(Step[str]):
def __init__(self, flow: "AttributeFlow") -> None:
self.flow = flow
def wait_for(self, context: Context, input: str) -> Wait:
self.flow.status.set(context, "processing")
return Wait.skip_immediately()
def execute(self, context: Context, input: str) -> StepDecision:
self.flow.status.set(context, "completed")
return graceful_complete(input)
class AttributeFlow(Flow[str]):
status = Attribute(
"primitive-attribute-status",
str,
index=AttributeIndex(IndexType.KEYWORD, "keyword1"),
)
def __init__(self) -> None:
self.start = AttributeStep(self)
def get_steps(self) -> StepList[str]:
return StepList.start_step(self.start)
def get_persistence_schema(self) -> PersistenceSchema:
return PersistenceSchema.of(self.status)
Example: examples/python/dex_examples/primitives/attribute/attribute_flow.py
AttributeMap
An AttributeMap is a set of Attributes with keys chosen at run time. Use it for values that grow with your data, such as the status of each payment in an order. Dex stores each key separately, so a write does not rewrite the whole collection.
progress = AttributeMap("primitive-attribute-progress", str)
progress.set(context, "payment", "authorized")
Example: examples/python/dex_examples/primitives/attribute/attribute_flow.py
Read and search Attributes
Code outside the Flow reads Attributes through the Client. See when a read needs an RPC and Read Attributes from outside a Flow. To set values when the Flow starts, see Initial values.
An indexed Attribute can be used in SearchFlows queries to find Flow runs. The example above indexes the status Attribute. See Indexes for index types, index keys, and AttributeMap indexes.
Locks
Two Steps or RPCs can write the same Attribute at the same time. Without a lock, two that read and then write it can overwrite each other's update. See Step guarantees. Use an Attribute lock to run them one after the other. Dex acquires the lock before the Step method or RPC starts and releases it when it finishes. A Step waits for its lock. An RPC that cannot get a lock fails, and you retry it.
A lock affects only the Steps and RPCs that declare it. A Step or RPC that does not declare it can still write the Attribute. Steps get a lock in the order they started waiting for it. An RPC does not queue. It takes a free lock even when Steps are waiting for it. So a steady stream of locked RPCs can delay those Steps. Keep locked RPCs short.
This Step locks a singleton Attribute and one AttributeMap instance while Execute runs:
def get_step_options(self) -> StepOptions:
return StepOptions(
execute_lock_attributes=(
self.flow.status.lock(),
self.flow.progress.lock("payment"),
),
)
Example: examples/python/dex_examples/primitives/attribute/attribute_flow.py
See Locks for lock timing and WaitFor, Execute, and RPC locks. See RPC lock conflicts and whole-map coordination for the rest.
Short-lived secrets
A hard-delete Attribute holds a short-lived secret, such as an API key that a Flow uses a few times and then discards. Deleting it erases the stored value permanently. Neither Dex APIs nor Flow history can recover it. Unlike other writes, a hard-delete write takes effect before Dex saves the handler's result. See Hard-delete Attributes for the requirements and their consequences.
Keep data after the Flow
Attribute Store sync copies selected Attributes to PostgreSQL tables that you own. Each destination is called an Attribute Store. The tables keep the data after Dex deletes the Flow. Your application and your reports query them with SQL, and you can index them for your own queries.
To sync an Attribute:
- Create the table before the server starts. Dex never creates, migrates, or backfills a table.
- Configure the store in the server configuration.
- Mark the Attribute or AttributeMap with syncToAttributeStore.
- List the store name in the Flow's FlowConfig when you start the Flow.
What to expect:
- The Flow stays the source of truth. A table row holds the latest values, not a history. Do not make Flow decisions from the table.
- Dex writes in the background. A write never delays a Step, an RPC, or a SetAttributes call. Writes for the same Flow ID keep their order.
- When a write fails, Dex retries it for a limited time. It then drops the write and logs an error. Dex does not replay old values or fill a newly configured store.
- When a Flow completes or fails on its own, it stays running until its pending writes are written or dropped. StopFlow does not wait.
- Deleting an Attribute writes NULL to its column. Dex does not delete the row when the Flow closes.
- A store can split the Flow ID into several key columns, and can write each AttributeMap instance as a row of its own table.
The Attribute Store sync reference covers configuration, table requirements, row keys, AttributeMap tables, retries, metrics, and local development. The Entity Store pattern shows a complete example.
Reference
The Attribute page of each SDK reference lists the API names in that language.
Data primitive semantics covers initial values, Client reads, indexes, hard-delete Attributes, and locks in detail.