def test_marker_handler_record_mutable_marker(): decision_context = MagicMock() handler = MarkerHandler(decision_context=decision_context, marker_name="test") handler.record_mutable_marker("theid", 20, "thedata".encode("utf-8"), 0) assert "theid" in handler.mutable_marker_results decision_context.record_marker.assert_called_once()
def test_get_marker_data_wrong_name(marker_recorded_event, decision_context): handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name-different-one") data = handler.get_marker_data_from_history(event_id=20, marker_id="the-id", expected_access_count=35) assert data is None
def test_get_marker_data_from_history(marker_recorded_event, decision_context): handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name") data = handler.get_marker_data_from_history(event_id=20, marker_id="the-id", expected_access_count=35) assert data == b'blah-blah'
def test_get_marker_data_from_history_wrong_event_type(marker_recorded_event, decision_context): decision_context.decider.decision_events.decision_events[ 0].event_type = EventType.ActivityTaskCompleted handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name") data = handler.get_marker_data_from_history(event_id=20, marker_id="the-id", expected_access_count=35) assert data is None
def test_handle_not_replaying_callback_returns_none(decision_context): def callback(stored): return None decision_context.workflow_clock.set_replaying(False) handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name") handler.mutable_marker_results["the-id"] = MarkerResult(data=b'123', access_count=35) ret = handler.handle("the-id", callback) assert ret == b'123' assert len(decision_context.decider.decisions) == 0
def test_handle_replaying_no_history(decision_context): def callback(stored): raise Exception("Should not be executed") decision_context.decider.next_decision_event_id = 25 handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name") handler.mutable_marker_results["the-id"] = MarkerResult(data=b'123', access_count=35) ret = handler.handle("the-id", callback) assert ret == b'123' assert len(decision_context.decider.decisions) == 1
def test_handle_replaying_get_from_history_before_replay(decision_context): def callback(stored): raise Exception("Should not be executed") handler = MarkerHandler(decision_context=decision_context, marker_name="the-marker-name") handler.mutable_marker_results["the-id"] = MarkerResult(data=b'123', access_count=35, replayed=True) ret = handler.handle("the-id", callback) assert ret == b'123' assert len(decision_context.decider.decisions) == 0
def test_marker_handler_mark_replayed(): handler = MarkerHandler(decision_context=Mock(), marker_name=VERSION_MARKER_NAME) handler.set_data("abc", b"stuff") handler.mark_replayed("abc") assert handler.mutable_marker_results["abc"].replayed
def __post_init__(self): self.version_handler = MarkerHandler(self.decision_context, VERSION_MARKER_NAME)
class ClockDecisionContext: decider: ReplayDecider decision_context: DecisionContext scheduled_timers: Dict[int, OpenRequestInfo] = field(default_factory=dict) replay_current_time_milliseconds: int = -1 replaying: bool = True version_handler: MarkerHandler = None def __post_init__(self): self.version_handler = MarkerHandler(self.decision_context, VERSION_MARKER_NAME) def set_replay_current_time_milliseconds(self, s): self.replay_current_time_milliseconds = s def current_time_millis(self): return self.replay_current_time_milliseconds def create_timer(self, delay_seconds: int, callback: Callable): if delay_seconds < 0: raise Exception("Negative delay seconds: " + str(delay_seconds)) if delay_seconds == 0: callback(None) return None firing_time = self.current_time_millis() + delay_seconds * 1000 context = OpenRequestInfo(user_context=firing_time) timer = StartTimerDecisionAttributes() timer.start_to_fire_timeout_seconds = delay_seconds timer.timer_id = str(self.decider.get_and_increment_next_id()) start_event_id: int = self.decider.start_timer(timer) context.completion_handle = lambda ctx, e: callback(e) self.scheduled_timers[start_event_id] = context return TimerCancellationHandler(start_event_id=start_event_id, clock_decision_context=self) def is_replaying(self): return self.replaying def set_replaying(self, replaying): self.replaying = replaying def timer_cancelled(self, start_event_id: int, reason: Exception): scheduled: OpenRequestInfo = self.scheduled_timers.pop( start_event_id, None) if not scheduled: return callback = scheduled.completion_handle exception = CancellationException("Cancelled by request") exception.init_cause(reason) callback(None, exception) def handle_timer_fired(self, attributes: TimerFiredEventAttributes): started_event_id: int = attributes.started_event_id if self.decider.handle_timer_closed(attributes): scheduled = self.scheduled_timers.pop(started_event_id, None) if scheduled: callback = scheduled.completion_handle callback(None, None) def handle_timer_canceled(self, event: HistoryEvent): attributes: TimerCanceledEventAttributes = event.timer_canceled_event_attributes started_event_id: int = attributes.started_event_id if self.decider.handle_timer_canceled(event): self.timer_cancelled(started_event_id, None) def get_version(self, change_id: str, min_supported: int, max_supported) -> int: def func(): return json.dumps(max_supported) result: bytes = self.version_handler.handle(change_id, func) if result is None: result = json.dumps(DEFAULT_VERSION) self.version_handler.set_data(change_id, result) self.version_handler.mark_replayed( change_id ) # so that we don't ever emit a MarkerRecorded for this version: int = json.loads(result) self.validate_version(change_id, version, min_supported, max_supported) return version def validate_version(self, change_id: str, version: int, min_supported: int, max_supported: int): if version < min_supported or version > max_supported: raise Exception( f"Version {version} of changeID {change_id} is not supported. " f"Supported version is between {min_supported} and {max_supported}." ) def handle_marker_recorded(self, event: HistoryEvent): """ Will be executed more than once for the same event. """ attributes = event.marker_recorded_event_attributes name: str = attributes.marker_name if SIDE_EFFECT_MARKER_NAME == name: # TODO # sideEffectResults.put(event.getEventId(), attributes.getDetails()); pass elif LOCAL_ACTIVITY_MARKER_NAME == name: # TODO # handleLocalActivityMarker(attributes); pass elif VERSION_MARKER_NAME == name: marker_data = MarkerInterface.from_event_attributes(attributes) change_id: str = marker_data.get_id() data: bytes = marker_data.get_data() self.version_handler.mutable_marker_results[ change_id] = MarkerResult(data=data) elif MUTABLE_SIDE_EFFECT_MARKER_NAME != name: # TODO # if (log.isWarnEnabled()) { # log.warn("Unexpected marker: " + event); # } pass