Wait for Attribute Match
Use WaitForAttributeMatch when a caller needs to refresh after durable business state changes. The application owns the Attribute and its meaning. It does not observe workflow history, run IDs, or Temporal event IDs.
A common pattern is an integer revision Attribute initialized to zero. Every Step and RPC that advances the represented state declares the same Attribute lock. Inside that lock, it reads the revision, adds one, writes the business state, and then writes the new revision.
The caller waits for revision greater than its last observed value. The wait returns the current matched revision. The caller then invokes its Describe or read RPC to fetch the new state.
Revision is a watermark, not an event stream. If state advances quickly from 0 to 1 to 2 to 3, one wait for a value greater than 0 may return 3. Callers must not expect to observe every intermediate value.
Dex derives the Request ID from the complete Attribute condition. Repeating the same condition on one active Flow shares one accepted durable Update, including across Continue-As-New. Leave MaximumWaitTime at 0 for ordinary waits. Consider a positive value only when dynamic predicates, many concurrent consumers, abandoned callers, or conditions that may never match could consume the Flow's in-flight capacity. An expiry releases the slot, but continued waiting creates a new -N Update generation. See Choose the durable handler lifetime for the capacity and cost tradeoff.
Advance revision under a lock
The Job Posting example serializes its update RPC with one Attribute lock and advances UpdateVersion in the same invocation that writes the state.
@rpc(lock_attributes=(update_posting_lock.lock(),))
def update(self, context: Context, input: JobInfo) -> RPCResult[int]:
version = self.update_version.get(context) + 1
self.title.set(context, input.title or "")
self.job_description.set(context, input.description or "")
self.last_update_time_millis.set(context, int(time.time() * 1000))
if input.notes is not None:
self.notes.set(context, input.notes)
self.update_version.set(context, version)
update = PostingUpdate(
version,
f"{context.flow_id}:{version}",
input,
)
self.linkedin_posting_updates.publish(context, update)
self.indeed_posting_updates.publish(context, update)
return RPCResult(version)
Example: examples/python/dex_examples/products/job-post/job_post_flow.py
Wait, then refresh
The application passes only the Flow ID. After the wait returns a revision greater than the caller's watermark, it invokes the read RPC.
revision = await app_state.client.wait_for_attribute_match(
flow_id,
app_state.job_post.update_version,
AttributeMatch.greater_than(required_int_query("lastRevision")),
WaitForAttributeOptions(),
)
job_info = await app_state.client.invoke_rpc(app_state.job_post.get, flow_id)
Example: examples/python/dex_examples/products/job-post/controller.py
String and Boolean Attributes support equal and not-equal matches. Integer and floating-point Attributes support all six comparison operators. Missing Attributes never match. Object, bytes, null, blob-backed, non-finite floating-point, cross-type, and invalid ordering comparisons are rejected.
The wait works only with Temporal and only while the Flow is active. Continue-As-New is followed automatically. Cadence returns Unimplemented.