edda-framework 0.9.0__py3-none-any.whl → 0.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
edda/app.py CHANGED
@@ -709,7 +709,6 @@ class EddaApp:
709
709
  instance_id = subscription["instance_id"]
710
710
  channel = subscription["channel"]
711
711
  timeout_at = subscription["timeout_at"]
712
- created_at = subscription["created_at"]
713
712
 
714
713
  # Lock-First pattern: Try to acquire the lock before processing
715
714
  # If we can't get the lock, another worker is processing this workflow
@@ -777,48 +776,28 @@ class EddaApp:
777
776
  # 2. Remove message subscription
778
777
  await self.storage.remove_message_subscription(instance_id, channel)
779
778
 
780
- # 3. Fail the workflow with TimeoutError
781
- import traceback
782
-
783
- # Get timeout_seconds from timeout_at and created_at
784
- # Handle both datetime objects and ISO strings
785
- try:
786
- timeout_dt = (
787
- timeout_at
788
- if isinstance(timeout_at, dt_type)
789
- else dt_type.fromisoformat(str(timeout_at))
790
- )
791
- created_dt = (
792
- created_at
793
- if isinstance(created_at, dt_type)
794
- else dt_type.fromisoformat(str(created_at))
779
+ # 3. Resume workflow (lock already held - distributed coroutine pattern)
780
+ # The workflow will replay and receive() will raise TimeoutError from cached history
781
+ workflow_name = subscription.get("workflow_name")
782
+ if not workflow_name:
783
+ logger.warning(
784
+ "No workflow_name in subscription for %s, skipping",
785
+ instance_id,
795
786
  )
796
- # Calculate the original timeout duration (timeout_at - created_at)
797
- timeout_seconds = int((timeout_dt - created_dt).total_seconds())
798
- except Exception:
799
- timeout_seconds = 0 # Fallback
787
+ continue
800
788
 
801
- error = TimeoutError(
802
- f"Message on channel '{channel}' did not arrive within {timeout_seconds} seconds"
803
- )
804
- stack_trace = "".join(
805
- traceback.format_exception(type(error), error, error.__traceback__)
806
- )
789
+ if self.replay_engine is None:
790
+ logger.error("Replay engine not initialized")
791
+ continue
807
792
 
808
- # Update workflow status to failed with error details
809
- await self.storage.update_instance_status(
810
- instance_id,
811
- "failed",
812
- {
813
- "error_message": str(error),
814
- "error_type": "TimeoutError",
815
- "stack_trace": stack_trace,
816
- },
793
+ await self.replay_engine.resume_by_name(
794
+ instance_id, workflow_name, already_locked=True
817
795
  )
818
796
 
819
797
  logger.debug(
820
- "Marked workflow %s as failed due to message timeout",
798
+ "Resumed workflow %s after message timeout on channel '%s'",
821
799
  instance_id,
800
+ channel,
822
801
  )
823
802
 
824
803
  except Exception as e:
edda/context.py CHANGED
@@ -217,6 +217,14 @@ class WorkflowContext:
217
217
  # Cache the timer result for wait_timer replay
218
218
  # Timer returns None, so we cache the result field
219
219
  self._history_cache[activity_id] = event_data.get("result")
220
+ elif event_type == "MessageTimeout":
221
+ # Cache the timeout error for receive() replay
222
+ # This allows TimeoutError to be raised and caught in workflow code
223
+ self._history_cache[activity_id] = {
224
+ "_error": True,
225
+ "error_type": event_data.get("error_type", "TimeoutError"),
226
+ "error_message": event_data.get("error_message", "Message timeout"),
227
+ }
220
228
 
221
229
  self._history_loaded = True
222
230
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edda-framework
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Lightweight Durable Execution Framework
5
5
  Project-URL: Homepage, https://github.com/i2y/edda
6
6
  Project-URL: Documentation, https://github.com/i2y/edda#readme
@@ -1,9 +1,9 @@
1
1
  edda/__init__.py,sha256=hGC6WR2R36M8LWC97F-0Rw4Ln0QUUT_1xC-7acOy_Fk,2237
2
2
  edda/activity.py,sha256=nRm9eBrr0lFe4ZRQ2whyZ6mo5xd171ITIVhqytUhOpw,21025
3
- edda/app.py,sha256=kZ-VEvjIe3GjUA8RhT6OimuezNyPf2IhrvQ2kL44zJs,45201
3
+ edda/app.py,sha256=11-IczlJpCR_G-jWUvV1DIr_Zwm6bDJnFiNBQsZ79Zo,44306
4
4
  edda/channels.py,sha256=Budi0FyxalmcAMwj50mX3WzRce5OuLKXGws0Hp_snfw,34745
5
5
  edda/compensation.py,sha256=iKLlnTxiF1YSatmYQW84EkPB1yMKUEZBtgjuGnghLtY,11824
6
- edda/context.py,sha256=IavmrbCdTAozP4QWlQ5-rCHR9yJAT-aohqyrOnbVLBU,20858
6
+ edda/context.py,sha256=pPn98-G5HgaOGDRzEhma58TzBulwsiTvmNEMLIu0XwI,21330
7
7
  edda/exceptions.py,sha256=-ntBLGpVQgPFG5N1o8m_7weejAYkNrUdxTkOP38vsHk,1766
8
8
  edda/hooks.py,sha256=HUZ6FTM__DZjwuomDfTDEroQ3mugEPuJHcGm7CTQNvg,8193
9
9
  edda/locking.py,sha256=NAFJmw-JaSVsXn4Y4czJyv_s9bWG8cdrzDBWIEag5X8,13661
@@ -36,8 +36,8 @@ edda/viewer_ui/theme.py,sha256=mrXoXLRzgSnvE2a58LuMcPJkhlvHEDMWVa8Smqtk4l0,8118
36
36
  edda/visualizer/__init__.py,sha256=DOpDstNhR0VcXAs_eMKxaL30p_0u4PKZ4o2ndnYhiRo,343
37
37
  edda/visualizer/ast_analyzer.py,sha256=plmx7C9X_X35xLY80jxOL3ljg3afXxBePRZubqUIkxY,13663
38
38
  edda/visualizer/mermaid_generator.py,sha256=XWa2egoOTNDfJEjPcwoxwQmblUqXf7YInWFjFRI1QGo,12457
39
- edda_framework-0.9.0.dist-info/METADATA,sha256=esgoKFgUTWqAZWIHxgtKGl5j8VTaWiJw_oz93Dtm064,35741
40
- edda_framework-0.9.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
41
- edda_framework-0.9.0.dist-info/entry_points.txt,sha256=dPH47s6UoJgUZxHoeSMqZsQkLaSE-SGLi-gh88k2WrU,48
42
- edda_framework-0.9.0.dist-info/licenses/LICENSE,sha256=udxb-V7_cYKTHqW7lNm48rxJ-Zpf0WAY_PyGDK9BPCo,1069
43
- edda_framework-0.9.0.dist-info/RECORD,,
39
+ edda_framework-0.9.1.dist-info/METADATA,sha256=8m1LKjz9cWbmwsa7V-FAEIQdgM3_63wan3Yml58KToE,35741
40
+ edda_framework-0.9.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
41
+ edda_framework-0.9.1.dist-info/entry_points.txt,sha256=dPH47s6UoJgUZxHoeSMqZsQkLaSE-SGLi-gh88k2WrU,48
42
+ edda_framework-0.9.1.dist-info/licenses/LICENSE,sha256=udxb-V7_cYKTHqW7lNm48rxJ-Zpf0WAY_PyGDK9BPCo,1069
43
+ edda_framework-0.9.1.dist-info/RECORD,,