Skip to content

Commit 35b1a35

Browse files
authored
Fix memo payload metadata memory sharing bug (#901)
1 parent 13b27be commit 35b1a35

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

temporalio/bridge/worker.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,11 @@ async def decode_activation(
392392
for val in job.initialize_workflow.memo.fields.values():
393393
# This uses API payload not bridge payload
394394
new_payload = (await codec.decode([val]))[0]
395+
# Make a shallow copy, in case new_payload.metadata and val.metadata are
396+
# references to the same memory, e.g. decode() returns its input unchanged.
397+
new_metadata = dict(new_payload.metadata)
395398
val.metadata.clear()
396-
val.metadata.update(new_payload.metadata)
399+
val.metadata.update(new_metadata)
397400
val.data = new_payload.data
398401
elif job.HasField("do_update"):
399402
await _decode_payloads(job.do_update.input, codec)

tests/worker/test_workflow.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,31 @@ async def test_workflow_with_passthrough_codec(client: Client):
15371537
await test_workflow_simple_activity(client)
15381538

15391539

1540+
@workflow.defn
1541+
class MemoDecodingWorkflow:
1542+
@workflow.run
1543+
async def run(self, memo_key: str) -> Any:
1544+
return workflow.memo_value(memo_key)
1545+
1546+
1547+
async def test_workflow_memo_decoding_with_passthrough_codec(client: Client):
1548+
# This used to fail because activation decoding accidentally cleared the memo
1549+
# payload metadata (containing the encoding) due to memory sharing between the
1550+
# before-decoding and after-decoding value
1551+
config = client.config()
1552+
config["data_converter"] = DataConverter(payload_codec=PassThroughCodec())
1553+
client = Client(**config)
1554+
async with new_worker(client, MemoDecodingWorkflow) as worker:
1555+
memo_value = await client.execute_workflow(
1556+
MemoDecodingWorkflow.run,
1557+
"memokey",
1558+
id=f"workflow-{uuid.uuid4()}",
1559+
task_queue=worker.task_queue,
1560+
memo={"memokey": {"memoval_key": "memoval_value"}},
1561+
)
1562+
assert memo_value == {"memoval_key": "memoval_value"}
1563+
1564+
15401565
class CustomWorkflowRunner(WorkflowRunner):
15411566
def __init__(self) -> None:
15421567
super().__init__()

0 commit comments

Comments
 (0)