Skip to content

Commit 7b390ef

Browse files
committed
make create_event_group() id required. label now optional.
1 parent 7a893ae commit 7b390ef

3 files changed

Lines changed: 187 additions & 291 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ to include examples, links to docs, or any other relevant information.
2323
- **Experimental**: Experimental support for _Event Groups_. **Event Groups** is a new form of
2424
Workflow-level metadata that allows for improved visibility into a Workflow execution's history
2525
by grouping logically related Events together based on user-defined or system-inferred criteria.
26-
An event group is created using `workflow.create_event_group(label)`, then attach it either per
27-
call (`workflow.start_activity(..., event_groups=[group])`) or ambiently to everything issued
28-
inside `with group.scope():`. Each signal and update handler is also implicitly wrapped in a
29-
group of its own. Requires a server that understands the Event Groups fields.
26+
`workflow.create_event_group(...)` takes the Event Group's ID as its first and only required
27+
argument; the user-provided ID is used verbatim and should not contain sensitive information.
28+
The label is optional and passed as a keyword argument; it is a codec-encoded Payload.
3029

3130
### Changed
3231

‎temporalio/workflow/_event_groups.py‎

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from __future__ import annotations
88

99
import contextvars
10-
import hashlib
1110
from abc import ABC, abstractmethod
1211
from collections.abc import Iterator, Sequence
1312
from contextlib import contextmanager
@@ -86,7 +85,7 @@ def _to_proto(self) -> temporalio.api.sdk.v1.EventGroupMarker:
8685
class _LabelEventGroup(EventGroup):
8786
"""An Event Group explicitly created by workflow code."""
8887

89-
def __init__(self, id: str, label: str) -> None:
88+
def __init__(self, id: str, label: str | None) -> None:
9089
self._id = id
9190
self._label = label
9291

@@ -97,9 +96,11 @@ def _applied_over(self, active: _ActiveEventGroups) -> _ActiveEventGroups:
9796
)
9897

9998
def _to_proto(self) -> temporalio.api.sdk.v1.EventGroupMarker:
100-
# Deliberately the SDK's default converter rather than the worker's own: the UI and CLI
101-
# rely on the label being a json/plain string, which a user-provided converter could
102-
# break.
99+
if self._label is None:
100+
return temporalio.api.sdk.v1.EventGroupMarker(
101+
label=temporalio.api.sdk.v1.EventGroupMarker.Label(id=self._id)
102+
)
103+
# Deliberately the SDK's default converter, not the user-provided one.
103104
return temporalio.api.sdk.v1.EventGroupMarker(
104105
label=temporalio.api.sdk.v1.EventGroupMarker.Label(
105106
id=self._id,
@@ -158,47 +159,29 @@ class _ActiveEventGroups:
158159
)
159160

160161

161-
def create_event_group(label: str, *, id: str | None = None) -> EventGroup:
162-
"""Create an Event Group that can be attached to commands produced by this
162+
def create_event_group(id: str, *, label: str | None = None) -> EventGroup:
163+
"""Create an Event Group that can be attached to commands scheduled by this
163164
workflow.
164165
166+
Attach the returned group via command ``event_groups`` options, or via
167+
:py:meth:`EventGroup.scope`.
168+
165169
Args:
166-
label: User-visible label for the group, surfaced in the UI and CLI.
167-
The label is converted to a payload using the SDK's default payload
168-
converter, not the one configured on the worker, then encoded using
169-
the worker's configured payload codecs.
170-
171-
Note that when no ``id`` is given, the id is derived from the label
172-
using a hash function. Given short and predictable labels,
173-
brute-forcing the hashed value may be computationally feasible,
174-
thereby recovering the label. Avoid putting sensitive information
175-
in labels, or provide an explicit ``id``.
176-
id: Opaque identifier determining whether two Event Groups are the
177-
same. Events are grouped together if and only if their groups have
178-
the same id, without regard to their labels; only the first label
179-
seen for a given id is used. Defaults to a deterministic,
180-
replay-stable value derived from the label. The id is not encoded
181-
using payload codecs.
182-
183-
Returns:
184-
The new Event Group.
170+
id: Non-empty group identity. Commands with the same ``id`` belong to
171+
the same group. The user-provided ID is stored as plain text in the
172+
workflow history and should therefore not contain sensitive
173+
information.
174+
label: Optional non-empty display text for the UI / CLI. If provided,
175+
it is persisted to history as a codec-encoded Payload.
185176
186177
.. warning::
187178
Event Groups is an experimental API and may change without notice.
188179
"""
189-
info = _Runtime.current().workflow_info()
190-
if not label:
191-
raise ValueError("Event group label cannot be empty")
192-
if id is None:
193-
# Salted with the run id so that the label cannot be recovered from the
194-
# id using precomputed hashes. This is the run id of the
195-
# WorkflowExecutionStarted event, which is preserved across resets, so
196-
# ids remain stable on replay and after a reset.
197-
id = hashlib.sha1(
198-
f"{info.original_execution_run_id}{label}".encode()
199-
).hexdigest()
200-
elif not id:
180+
_Runtime.current()
181+
if not id:
201182
raise ValueError("Event group id cannot be empty")
183+
if label is not None and not label:
184+
raise ValueError("Event group label cannot be empty")
202185
return _LabelEventGroup(id, label)
203186

204187

0 commit comments

Comments
 (0)