Skip to content

Commit 2879d03

Browse files
committed
Implement Workflow.random_uuid() and Workflow.new_random().
1 parent c90f73a commit 2879d03

File tree

5 files changed

+105
-2
lines changed

5 files changed

+105
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ version is targeted to be released in ~~September of 2019~~ January 2020.
2424
- [ ] Timers
2525
- [x] Sleep
2626
- [ ] Loggers
27-
- [ ] newRandom
28-
- [ ] UUID
27+
- [x] newRandom
28+
- [x] UUID
2929
- [ ] Workflow Versioning
3030

3131
2.0

cadence/decision_loop.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import contextvars
55
import datetime
66
import json
7+
import uuid
8+
import random
79
import logging
810
import threading
911
from asyncio.base_futures import CancelledError
@@ -420,6 +422,16 @@ def handle_timer_canceled(self, event: HistoryEvent):
420422
def set_current_run_id(self, run_id: str):
421423
self.current_run_id = run_id
422424

425+
def random_uuid(self) -> uuid.UUID:
426+
return uuid.uuid3(uuid.UUID(self.current_run_id), str(self.decider.get_and_increment_next_id()))
427+
428+
def new_random(self) -> random.Random:
429+
random_uuid = self.random_uuid()
430+
lsb = random_uuid.bytes[:8]
431+
generator = random.Random()
432+
generator.seed(lsb, version=2)
433+
return generator
434+
423435

424436
@dataclass
425437
class ReplayDecider:

cadence/tests/test_random.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import random
2+
import uuid
23

34

45
def test_python_random_determinism():
@@ -9,3 +10,7 @@ def test_python_random_determinism():
910
assert generator.randint(0, 100) == 41
1011
assert generator.randint(0, 100) == 16
1112
assert generator.randint(0, 100) == 11
13+
14+
15+
def test_python_uuid3_determinism():
16+
assert uuid.uuid3(uuid.UUID("8d3149e9-71d3-4ad3-9216-bfbf0473b7c6"), "25") == uuid.UUID('db602869-db61-341b-9e01-81393de6c9b0')

cadence/tests/test_workflow_random.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import pytest
2+
3+
from cadence.workerfactory import WorkerFactory
4+
from cadence.workflow import workflow_method, Workflow, WorkflowClient
5+
6+
TASK_LIST = "TestWorkflowRandom"
7+
DOMAIN = "sample"
8+
9+
checkpoint_values = {}
10+
11+
12+
def record_value(key, v):
13+
values = checkpoint_values.setdefault(key, [])
14+
values.append(v)
15+
16+
17+
class TestRandomWorkflow:
18+
@workflow_method(task_list=TASK_LIST)
19+
async def get_greetings(self) -> list:
20+
raise NotImplementedError
21+
22+
23+
class TestRandomWorkflowImpl(TestRandomWorkflow):
24+
25+
def __init__(self):
26+
pass
27+
28+
async def get_greetings(self) -> None:
29+
record_value("uuid-checkpoint-1", Workflow.random_uuid())
30+
Workflow.sleep(1)
31+
record_value("uuid-checkpoint-2", Workflow.random_uuid())
32+
Workflow.sleep(1)
33+
record_value("uuid-checkpoint-3", Workflow.random_uuid())
34+
Workflow.sleep(1)
35+
36+
random = Workflow.new_random()
37+
record_value("random-checkpoint-1", random.randint(0, 2 ** 64))
38+
record_value("random-checkpoint-2", random.randint(0, 2 ** 64))
39+
Workflow.sleep(1)
40+
41+
random = Workflow.new_random()
42+
record_value("random-checkpoint-3", random.randint(0, 2 ** 64))
43+
record_value("random-checkpoint-4", random.randint(0, 2 ** 64))
44+
Workflow.sleep(1)
45+
46+
random = Workflow.new_random()
47+
record_value("random-checkpoint-5", random.randint(0, 2 ** 64))
48+
record_value("random-checkpoint-6", random.randint(0, 2 ** 64))
49+
Workflow.sleep(1)
50+
51+
52+
@pytest.mark.repeat(3)
53+
def test_workflow_random():
54+
checkpoint_values.clear()
55+
factory = WorkerFactory("localhost", 7933, DOMAIN)
56+
worker = factory.new_worker(TASK_LIST)
57+
worker.register_workflow_implementation_type(TestRandomWorkflowImpl)
58+
factory.start()
59+
60+
client = WorkflowClient.new_client(domain=DOMAIN)
61+
workflow: TestRandomWorkflow = client.new_workflow_stub(TestRandomWorkflow)
62+
workflow.get_greetings()
63+
64+
# Verify that the value is always the same at each checkpoint
65+
for checkpoint, values in checkpoint_values.items():
66+
assert all(v == values[0] for v in values)
67+
68+
# Verify that each checkpoint produced a unique value
69+
values = [v[0] for k, v in checkpoint_values.items()]
70+
assert len(set(values)) == 9
71+
72+
print("Stopping workers")
73+
worker.stop()

cadence/workflow.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import datetime
33
import inspect
44
import json
5+
import random
6+
import uuid
57
from dataclasses import dataclass, field
68
from typing import Callable, List, Type, Dict, Tuple
79
from uuid import uuid4
@@ -49,6 +51,17 @@ def now() -> datetime.datetime:
4951
now_in_ms = task.decider.decision_context.current_time_millis()
5052
return datetime.datetime.fromtimestamp(now_in_ms / 1000)
5153

54+
@staticmethod
55+
def random_uuid() -> uuid.UUID:
56+
from cadence.decision_loop import ITask
57+
task: ITask = ITask.current()
58+
return task.decider.decision_context.random_uuid()
59+
60+
@staticmethod
61+
def new_random() -> random.Random:
62+
from cadence.decision_loop import ITask
63+
task: ITask = ITask.current()
64+
return task.decider.decision_context.new_random()
5265

5366
class WorkflowStub:
5467
pass

0 commit comments

Comments
 (0)