pyworkflow-engine 0.2.8__tar.gz → 0.2.10__tar.gz

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.
Files changed (232) hide show
  1. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/PKG-INFO +1 -1
  2. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyproject.toml +1 -1
  3. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/__init__.py +1 -1
  4. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/app.py +85 -0
  5. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/citus.py +18 -0
  6. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/migrations/base.py +43 -1
  7. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/postgres.py +12 -0
  8. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/CLAUDE.md +0 -0
  9. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/DISTRIBUTED.md +0 -0
  10. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/LICENSE +0 -0
  11. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/MANIFEST.in +0 -0
  12. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/README.md +0 -0
  13. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/RELEASING.md +0 -0
  14. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/architecture.md +0 -0
  15. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/cancellation.mdx +0 -0
  16. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/continue-as-new.mdx +0 -0
  17. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/events.mdx +0 -0
  18. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/fault-tolerance.mdx +0 -0
  19. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/hooks.mdx +0 -0
  20. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/limitations.mdx +0 -0
  21. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/schedules.mdx +0 -0
  22. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/sleep.mdx +0 -0
  23. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/step-context.mdx +0 -0
  24. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/steps.mdx +0 -0
  25. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/stream-steps.mdx +0 -0
  26. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/stream-workflows.mdx +0 -0
  27. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/concepts/workflows.mdx +0 -0
  28. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/conventions.md +0 -0
  29. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/guides/brokers.mdx +0 -0
  30. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/guides/cli.mdx +0 -0
  31. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/guides/configuration.mdx +0 -0
  32. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/harness-gaps.md +0 -0
  33. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/introduction.mdx +0 -0
  34. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/layers.md +0 -0
  35. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/docs/quickstart.mdx +0 -0
  36. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/__init__.py +0 -0
  37. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/__init__.py +0 -0
  38. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/docker-compose.yml +0 -0
  39. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/pyworkflow.config.yaml +0 -0
  40. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/__init__.py +0 -0
  41. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/basic.py +0 -0
  42. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/batch_processing.py +0 -0
  43. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/cancellation.py +0 -0
  44. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/child_workflow_from_step.py +0 -0
  45. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/child_workflow_patterns.py +0 -0
  46. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/child_workflows.py +0 -0
  47. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/continue_as_new.py +0 -0
  48. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/fault_tolerance.py +0 -0
  49. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/hooks.py +0 -0
  50. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/idempotency.py +0 -0
  51. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/long_running.py +0 -0
  52. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/retries.py +0 -0
  53. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/schedules.py +0 -0
  54. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/sleep_in_step.py +0 -0
  55. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/durable/workflows/step_context.py +0 -0
  56. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/transient/01_basic_workflow.py +0 -0
  57. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/transient/02_fault_tolerance.py +0 -0
  58. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/transient/__init__.py +0 -0
  59. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/celery/transient/pyworkflow.config.yaml +0 -0
  60. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/__init__.py +0 -0
  61. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/01_basic_workflow.py +0 -0
  62. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/02_file_storage.py +0 -0
  63. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/03_retries.py +0 -0
  64. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/04_long_running.py +0 -0
  65. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/05_event_log.py +0 -0
  66. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/06_idempotency.py +0 -0
  67. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/07_hooks.py +0 -0
  68. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/08_cancellation.py +0 -0
  69. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/09_child_workflows.py +0 -0
  70. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/10_child_workflow_patterns.py +0 -0
  71. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/11_continue_as_new.py +0 -0
  72. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/12_schedules.py +0 -0
  73. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/13_step_context.py +0 -0
  74. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/14_child_workflow_from_step.py +0 -0
  75. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/durable/__init__.py +0 -0
  76. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/transient/01_quick_tasks.py +0 -0
  77. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/transient/02_retries.py +0 -0
  78. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/transient/03_sleep.py +0 -0
  79. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/examples/local/transient/__init__.py +0 -0
  80. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/aws/__init__.py +0 -0
  81. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/aws/context.py +0 -0
  82. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/aws/handler.py +0 -0
  83. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/aws/testing.py +0 -0
  84. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/__init__.py +0 -0
  85. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/loop.py +0 -0
  86. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/scheduler.py +0 -0
  87. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/singleton.py +0 -0
  88. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/celery/tasks.py +0 -0
  89. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/__init__.py +0 -0
  90. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/__main__.py +0 -0
  91. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/__init__.py +0 -0
  92. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/hooks.py +0 -0
  93. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/quickstart.py +0 -0
  94. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/runs.py +0 -0
  95. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/scheduler.py +0 -0
  96. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/schedules.py +0 -0
  97. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/setup.py +0 -0
  98. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/worker.py +0 -0
  99. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/commands/workflows.py +0 -0
  100. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/output/__init__.py +0 -0
  101. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/output/formatters.py +0 -0
  102. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/output/styles.py +0 -0
  103. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/__init__.py +0 -0
  104. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/async_helpers.py +0 -0
  105. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/config.py +0 -0
  106. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/config_generator.py +0 -0
  107. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/discovery.py +0 -0
  108. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/docker_manager.py +0 -0
  109. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/interactive.py +0 -0
  110. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/cli/utils/storage.py +0 -0
  111. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/config.py +0 -0
  112. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/__init__.py +0 -0
  113. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/aws.py +0 -0
  114. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/base.py +0 -0
  115. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/local.py +0 -0
  116. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/mock.py +0 -0
  117. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/context/step_context.py +0 -0
  118. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/__init__.py +0 -0
  119. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/exceptions.py +0 -0
  120. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/registry.py +0 -0
  121. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/scheduled.py +0 -0
  122. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/step.py +0 -0
  123. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/validation.py +0 -0
  124. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/core/workflow.py +0 -0
  125. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/discovery.py +0 -0
  126. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/engine/__init__.py +0 -0
  127. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/engine/events.py +0 -0
  128. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/engine/executor.py +0 -0
  129. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/engine/replay.py +0 -0
  130. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/observability/__init__.py +0 -0
  131. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/observability/logging.py +0 -0
  132. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/__init__.py +0 -0
  133. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/child_handle.py +0 -0
  134. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/child_workflow.py +0 -0
  135. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/continue_as_new.py +0 -0
  136. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/define_hook.py +0 -0
  137. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/hooks.py +0 -0
  138. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/resume_hook.py +0 -0
  139. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/schedule.py +0 -0
  140. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/shield.py +0 -0
  141. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/sleep.py +0 -0
  142. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/step_checkpoint.py +0 -0
  143. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/primitives/step_hook.py +0 -0
  144. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/runtime/__init__.py +0 -0
  145. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/runtime/base.py +0 -0
  146. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/runtime/celery.py +0 -0
  147. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/runtime/factory.py +0 -0
  148. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/runtime/local.py +0 -0
  149. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/scheduler/__init__.py +0 -0
  150. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/scheduler/local.py +0 -0
  151. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/serialization/__init__.py +0 -0
  152. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/serialization/decoder.py +0 -0
  153. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/serialization/encoder.py +0 -0
  154. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/__init__.py +0 -0
  155. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/base.py +0 -0
  156. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/cassandra.py +0 -0
  157. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/config.py +0 -0
  158. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/dynamodb.py +0 -0
  159. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/file.py +0 -0
  160. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/memory.py +0 -0
  161. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/migrations/__init__.py +0 -0
  162. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/mysql.py +0 -0
  163. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/schemas.py +0 -0
  164. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/storage/sqlite.py +0 -0
  165. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/__init__.py +0 -0
  166. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/checkpoint.py +0 -0
  167. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/consumer.py +0 -0
  168. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/context.py +0 -0
  169. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/decorator.py +0 -0
  170. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/dispatcher.py +0 -0
  171. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/emit.py +0 -0
  172. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/registry.py +0 -0
  173. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/runtime.py +0 -0
  174. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/signal.py +0 -0
  175. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/streams/step_context.py +0 -0
  176. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/utils/__init__.py +0 -0
  177. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/utils/duration.py +0 -0
  178. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/utils/helpers.py +0 -0
  179. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow/utils/schedule.py +0 -0
  180. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/pyworkflow_engine.egg-info/SOURCES.txt +0 -0
  181. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/setup.cfg +0 -0
  182. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/__init__.py +0 -0
  183. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_cancellation.py +0 -0
  184. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_cassandra_storage.py +0 -0
  185. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_child_workflows.py +0 -0
  186. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_continue_as_new.py +0 -0
  187. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_dynamodb_storage.py +0 -0
  188. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_fault_tolerance.py +0 -0
  189. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_schedule_storage.py +0 -0
  190. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_schema_migrations.py +0 -0
  191. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_singleton.py +0 -0
  192. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_stream_e2e.py +0 -0
  193. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/integration/test_workflow_suspended.py +0 -0
  194. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/__init__.py +0 -0
  195. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/__init__.py +0 -0
  196. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/test_cassandra_storage.py +0 -0
  197. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/test_citus_storage.py +0 -0
  198. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/test_dynamodb_storage.py +0 -0
  199. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/test_postgres_storage.py +0 -0
  200. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/backends/test_sqlite_storage.py +0 -0
  201. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/conftest.py +0 -0
  202. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/storage/__init__.py +0 -0
  203. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/storage/test_migrations.py +0 -0
  204. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_cancellation.py +0 -0
  205. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_child_workflows.py +0 -0
  206. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_cli_worker.py +0 -0
  207. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_continue_as_new.py +0 -0
  208. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_emit.py +0 -0
  209. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_event_limits.py +0 -0
  210. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_executor.py +0 -0
  211. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_fault_tolerance.py +0 -0
  212. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_force_local.py +0 -0
  213. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_hooks.py +0 -0
  214. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_parent_run_id.py +0 -0
  215. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_primitives_from_steps.py +0 -0
  216. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_registry.py +0 -0
  217. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_replay.py +0 -0
  218. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_retention.py +0 -0
  219. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_schedule_schemas.py +0 -0
  220. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_schedule_utils.py +0 -0
  221. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_scheduled_workflow.py +0 -0
  222. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_signal.py +0 -0
  223. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_singleton.py +0 -0
  224. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_step.py +0 -0
  225. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_step_checkpoint.py +0 -0
  226. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_step_context.py +0 -0
  227. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_step_hook.py +0 -0
  228. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_stream_storage.py +0 -0
  229. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_stream_workflow.py +0 -0
  230. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_validation.py +0 -0
  231. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_workflow.py +0 -0
  232. {pyworkflow_engine-0.2.8 → pyworkflow_engine-0.2.10}/tests/unit/test_workflow_suspended.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyworkflow-engine
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: A Python implementation of durable, event-sourced workflows inspired by Vercel Workflow
5
5
  Author: PyWorkflow Contributors
6
6
  License: MIT
@@ -7,7 +7,7 @@ packages = [{include = "pyworkflow"}]
7
7
 
8
8
  [project]
9
9
  name = "pyworkflow-engine"
10
- version = "0.2.8"
10
+ version = "0.2.10"
11
11
  description = "A Python implementation of durable, event-sourced workflows inspired by Vercel Workflow"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11"
@@ -29,7 +29,7 @@ Quick Start:
29
29
  >>> run_id = await start(my_workflow, "Alice")
30
30
  """
31
31
 
32
- __version__ = "0.2.8"
32
+ __version__ = "0.2.10"
33
33
 
34
34
  # Configuration
35
35
  from pyworkflow.config import (
@@ -61,6 +61,82 @@ def _run_warmup_callable(dotted_path: str) -> None:
61
61
  )
62
62
 
63
63
 
64
+ def _run_startup_migrations() -> None:
65
+ """Run pending DB migrations before the worker accepts tasks.
66
+
67
+ Loads storage configuration from environment variables, creates a throwaway
68
+ backend, calls ``connect()`` (which triggers ``_initialize_schema()`` →
69
+ ``run_migrations()``), and disconnects. The pool is bound to a temporary
70
+ event loop that lives only for this function, so the forked workers below
71
+ start with no inherited state.
72
+
73
+ Behavior:
74
+ - If ``PYWORKFLOW_STORAGE_TYPE`` is unset, or points at a schema-less backend
75
+ (``memory`` / ``file``), this is a no-op and tasks fall back to lazy init.
76
+ - If migrations raise, the exception propagates — the worker fails to start
77
+ rather than accepting tasks against a schema it can't read. That is the
78
+ desired failure mode: a restart loop is louder than silent corruption.
79
+ - Controllable via ``PYWORKFLOW_SKIP_STARTUP_MIGRATIONS=1`` for deployments
80
+ that intentionally migrate out-of-band (e.g. a dedicated job).
81
+ """
82
+ import asyncio
83
+
84
+ from loguru import logger as loguru_logger
85
+
86
+ if os.getenv("PYWORKFLOW_SKIP_STARTUP_MIGRATIONS", "").lower() in ("1", "true", "yes"):
87
+ loguru_logger.info(
88
+ "STARTUP_MIGRATIONS: skipped (PYWORKFLOW_SKIP_STARTUP_MIGRATIONS set)"
89
+ )
90
+ return
91
+
92
+ from pyworkflow.config import _load_env_storage_config
93
+
94
+ config = _load_env_storage_config()
95
+ if not config:
96
+ loguru_logger.info(
97
+ "STARTUP_MIGRATIONS: PYWORKFLOW_STORAGE_TYPE not set — skipping. "
98
+ "Migrations will run lazily on first task that touches storage."
99
+ )
100
+ return
101
+
102
+ storage_type = config.get("type")
103
+ if storage_type in ("memory", "file"):
104
+ loguru_logger.info(
105
+ f"STARTUP_MIGRATIONS: skipping for type={storage_type} (schema-less backend)"
106
+ )
107
+ return
108
+
109
+ loguru_logger.info(
110
+ f"STARTUP_MIGRATIONS: connecting to {storage_type} to apply pending migrations"
111
+ )
112
+
113
+ async def _run() -> None:
114
+ # Create the backend directly (bypassing the cache) so that the pool we
115
+ # open here does not leak into the forked workers' storage cache. The
116
+ # worker processes will each create their own pool on first task.
117
+ from pyworkflow.storage.config import _create_storage_backend
118
+
119
+ backend = _create_storage_backend(config)
120
+ try:
121
+ await backend.connect()
122
+ finally:
123
+ try:
124
+ if hasattr(backend, "disconnect"):
125
+ await backend.disconnect()
126
+ except Exception as e:
127
+ loguru_logger.warning(f"STARTUP_MIGRATIONS: disconnect failed: {e}")
128
+
129
+ try:
130
+ asyncio.run(_run())
131
+ except Exception:
132
+ loguru_logger.opt(exception=True).error(
133
+ "STARTUP_MIGRATIONS: FAILED — worker will not start"
134
+ )
135
+ raise
136
+
137
+ loguru_logger.info("STARTUP_MIGRATIONS: completed successfully")
138
+
139
+
64
140
  def is_sentinel_url(url: str) -> bool:
65
141
  """Check if URL uses sentinel:// or sentinel+ssl:// protocol."""
66
142
  return url.startswith("sentinel://") or url.startswith("sentinel+ssl://")
@@ -420,6 +496,15 @@ def on_worker_init(**kwargs):
420
496
  if warmup_path:
421
497
  _run_warmup_callable(warmup_path)
422
498
 
499
+ # Apply pending DB migrations once per pod, BEFORE any task is dispatched.
500
+ # This replaces the old lazy "first task triggers connect() triggers
501
+ # migrations" path, which was fragile: backend instances cached across
502
+ # restarts (or inherited via fork) would skip migrations entirely because
503
+ # _initialized was True without the migration check ever having run in
504
+ # this process. Running at startup makes migration state observable in
505
+ # the pod logs and fails the pod fast if the schema is incompatible.
506
+ _run_startup_migrations()
507
+
423
508
 
424
509
  @worker_process_init.connect
425
510
  def on_worker_process_init(**kwargs):
@@ -21,6 +21,7 @@ See: https://docs.citusdata.com/en/stable/develop/api.html
21
21
  """
22
22
 
23
23
  import asyncpg
24
+ from loguru import logger
24
25
 
25
26
  from pyworkflow.storage.migrations import Migration
26
27
  from pyworkflow.storage.postgres import PostgresMigrationRunner, PostgresStorageBackend
@@ -39,6 +40,10 @@ class CitusMigrationRunner(PostgresMigrationRunner):
39
40
  """Apply a migration with Citus-specific handling."""
40
41
  from datetime import UTC, datetime
41
42
 
43
+ logger.info(
44
+ f"CITUS_MIGRATION: opening transaction for v{migration.version}",
45
+ version=migration.version,
46
+ )
42
47
  async with self._pool.acquire() as conn, conn.transaction():
43
48
  if migration.version == 2:
44
49
  # V2: Add step_id column to events table
@@ -122,14 +127,17 @@ class CitusMigrationRunner(PostgresMigrationRunner):
122
127
  # DDL inside PL/pgSQL DO blocks is executed via SPI and bypasses the
123
128
  # hook, which would leave worker shards missing the column while the
124
129
  # coordinator catalog appears correct.
130
+ logger.info("CITUS_MIGRATION v5: ALTER stream_subscriptions ADD stream_run_id")
125
131
  await conn.execute(
126
132
  "ALTER TABLE stream_subscriptions "
127
133
  "ADD COLUMN IF NOT EXISTS stream_run_id TEXT NULL"
128
134
  )
135
+ logger.info("CITUS_MIGRATION v5: CREATE INDEX idx_subscriptions_stream_run")
129
136
  await conn.execute(
130
137
  "CREATE INDEX IF NOT EXISTS idx_subscriptions_stream_run "
131
138
  "ON stream_subscriptions(stream_id, stream_run_id)"
132
139
  )
140
+ logger.info("CITUS_MIGRATION v5: CREATE TABLE scheduled_signals")
133
141
  await conn.execute("""
134
142
  CREATE TABLE IF NOT EXISTS scheduled_signals (
135
143
  id TEXT PRIMARY KEY,
@@ -143,6 +151,7 @@ class CitusMigrationRunner(PostgresMigrationRunner):
143
151
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
144
152
  )
145
153
  """)
154
+ logger.info("CITUS_MIGRATION v5: CREATE INDEX idx_scheduled_signals_due")
146
155
  await conn.execute(
147
156
  "CREATE INDEX IF NOT EXISTS idx_scheduled_signals_due "
148
157
  "ON scheduled_signals(delivered, due_at)"
@@ -155,21 +164,30 @@ class CitusMigrationRunner(PostgresMigrationRunner):
155
164
  WHERE c.relname = 'scheduled_signals'
156
165
  )
157
166
  """)
167
+ logger.info(
168
+ f"CITUS_MIGRATION v5: scheduled_signals already distributed = {already}"
169
+ )
158
170
  if not already:
171
+ logger.info(
172
+ "CITUS_MIGRATION v5: calling create_reference_table('scheduled_signals')"
173
+ )
159
174
  await conn.execute("SELECT create_reference_table('scheduled_signals')")
160
175
  elif migration.version == 6:
161
176
  # V6: Add parent_run_id + parent_hook_token to stream_subscriptions.
162
177
  # Top-level ALTERs so Citus propagates to all worker shards (see V5 note).
178
+ logger.info("CITUS_MIGRATION v6: ALTER stream_subscriptions ADD parent_run_id")
163
179
  await conn.execute(
164
180
  "ALTER TABLE stream_subscriptions "
165
181
  "ADD COLUMN IF NOT EXISTS parent_run_id TEXT NULL"
166
182
  )
183
+ logger.info("CITUS_MIGRATION v6: ALTER stream_subscriptions ADD parent_hook_token")
167
184
  await conn.execute(
168
185
  "ALTER TABLE stream_subscriptions "
169
186
  "ADD COLUMN IF NOT EXISTS parent_hook_token TEXT NULL"
170
187
  )
171
188
  elif migration.version == 7:
172
189
  # V7: Add result column. Top-level ALTER for Citus propagation (see V5 note).
190
+ logger.info("CITUS_MIGRATION v7: ALTER stream_subscriptions ADD result")
173
191
  await conn.execute(
174
192
  "ALTER TABLE stream_subscriptions "
175
193
  "ADD COLUMN IF NOT EXISTS result JSONB NULL"
@@ -11,6 +11,8 @@ from dataclasses import dataclass
11
11
  from datetime import UTC, datetime
12
12
  from typing import Any
13
13
 
14
+ from loguru import logger
15
+
14
16
 
15
17
  @dataclass
16
18
  class Migration:
@@ -238,11 +240,18 @@ class MigrationRunner(ABC):
238
240
  Raises:
239
241
  Exception: If any migration fails (partial migrations are rolled back)
240
242
  """
243
+ runner_name = type(self).__name__
244
+ logger.info(f"MIGRATION: run_migrations() called on {runner_name}")
245
+
241
246
  # Ensure we have a schema_versions table
242
247
  await self.ensure_schema_versions_table()
243
248
 
244
249
  # Get current version
245
250
  current_version = await self.get_current_version()
251
+ logger.info(
252
+ f"MIGRATION: current schema version = {current_version}",
253
+ runner=runner_name,
254
+ )
246
255
 
247
256
  # If no migrations recorded, check for existing schema
248
257
  if current_version == 0:
@@ -250,15 +259,43 @@ class MigrationRunner(ABC):
250
259
  if has_existing_schema:
251
260
  # Database has tables but no version tracking
252
261
  # Assume it's at V1 (original schema)
262
+ logger.info(
263
+ "MIGRATION: existing schema detected — recording baseline v1",
264
+ runner=runner_name,
265
+ )
253
266
  await self.record_baseline_version(1, "Baseline: original schema")
254
267
  current_version = 1
255
268
 
256
269
  # Get and apply pending migrations
257
270
  pending = self.registry.get_pending(current_version)
271
+ pending_versions = [m.version for m in pending]
272
+ logger.info(
273
+ f"MIGRATION: pending migrations = {pending_versions}",
274
+ runner=runner_name,
275
+ current_version=current_version,
276
+ )
258
277
  applied: list[AppliedMigration] = []
259
278
 
260
279
  for migration in pending:
261
- await self.apply_migration(migration)
280
+ logger.info(
281
+ f"MIGRATION: applying v{migration.version} — {migration.description}",
282
+ runner=runner_name,
283
+ version=migration.version,
284
+ )
285
+ try:
286
+ await self.apply_migration(migration)
287
+ except Exception:
288
+ logger.exception(
289
+ f"MIGRATION: v{migration.version} FAILED — {migration.description}",
290
+ runner=runner_name,
291
+ version=migration.version,
292
+ )
293
+ raise
294
+ logger.info(
295
+ f"MIGRATION: v{migration.version} applied successfully",
296
+ runner=runner_name,
297
+ version=migration.version,
298
+ )
262
299
  applied.append(
263
300
  AppliedMigration(
264
301
  version=migration.version,
@@ -267,6 +304,11 @@ class MigrationRunner(ABC):
267
304
  )
268
305
  )
269
306
 
307
+ logger.info(
308
+ f"MIGRATION: run_migrations() completed, applied {len(applied)} migration(s)",
309
+ runner=runner_name,
310
+ applied_versions=[a.version for a in applied],
311
+ )
270
312
  return applied
271
313
 
272
314
 
@@ -21,6 +21,7 @@ from datetime import UTC, datetime
21
21
  from typing import Any
22
22
 
23
23
  import asyncpg
24
+ from loguru import logger
24
25
 
25
26
  from pyworkflow.engine.events import Event, EventType
26
27
  from pyworkflow.storage.base import StorageBackend
@@ -92,6 +93,10 @@ class PostgresMigrationRunner(MigrationRunner):
92
93
 
93
94
  async def apply_migration(self, migration: Migration) -> None:
94
95
  """Apply a migration with PostgreSQL-specific handling."""
96
+ logger.info(
97
+ f"PG_MIGRATION: opening transaction for v{migration.version}",
98
+ version=migration.version,
99
+ )
95
100
  async with self._pool.acquire() as conn, conn.transaction():
96
101
  if migration.version == 2:
97
102
  # V2: Add step_id column to events table
@@ -176,14 +181,17 @@ class PostgresMigrationRunner(MigrationRunner):
176
181
  # and bypasses the Citus ProcessUtility hook, so worker shards
177
182
  # silently miss the column. Plain Postgres is unaffected either way,
178
183
  # but keeping both runners symmetric avoids future foot-guns.
184
+ logger.info("PG_MIGRATION v5: ALTER stream_subscriptions ADD stream_run_id")
179
185
  await conn.execute(
180
186
  "ALTER TABLE stream_subscriptions "
181
187
  "ADD COLUMN IF NOT EXISTS stream_run_id TEXT NULL"
182
188
  )
189
+ logger.info("PG_MIGRATION v5: CREATE INDEX idx_subscriptions_stream_run")
183
190
  await conn.execute(
184
191
  "CREATE INDEX IF NOT EXISTS idx_subscriptions_stream_run "
185
192
  "ON stream_subscriptions(stream_id, stream_run_id)"
186
193
  )
194
+ logger.info("PG_MIGRATION v5: CREATE TABLE scheduled_signals")
187
195
  await conn.execute("""
188
196
  CREATE TABLE IF NOT EXISTS scheduled_signals (
189
197
  id TEXT PRIMARY KEY,
@@ -197,6 +205,7 @@ class PostgresMigrationRunner(MigrationRunner):
197
205
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
198
206
  )
199
207
  """)
208
+ logger.info("PG_MIGRATION v5: CREATE INDEX idx_scheduled_signals_due")
200
209
  await conn.execute(
201
210
  "CREATE INDEX IF NOT EXISTS idx_scheduled_signals_due "
202
211
  "ON scheduled_signals(delivered, due_at)"
@@ -206,16 +215,19 @@ class PostgresMigrationRunner(MigrationRunner):
206
215
  # stream-step dispatcher can resume the parent @workflow via
207
216
  # resume_hook() instead of pinning a worker on an asyncio.Event.
208
217
  # Top-level ALTERs for Citus propagation symmetry (see V5 note).
218
+ logger.info("PG_MIGRATION v6: ALTER stream_subscriptions ADD parent_run_id")
209
219
  await conn.execute(
210
220
  "ALTER TABLE stream_subscriptions "
211
221
  "ADD COLUMN IF NOT EXISTS parent_run_id TEXT NULL"
212
222
  )
223
+ logger.info("PG_MIGRATION v6: ALTER stream_subscriptions ADD parent_hook_token")
213
224
  await conn.execute(
214
225
  "ALTER TABLE stream_subscriptions "
215
226
  "ADD COLUMN IF NOT EXISTS parent_hook_token TEXT NULL"
216
227
  )
217
228
  elif migration.version == 7:
218
229
  # V7: result column on stream_subscriptions for step output payloads.
230
+ logger.info("PG_MIGRATION v7: ALTER stream_subscriptions ADD result")
219
231
  await conn.execute(
220
232
  "ALTER TABLE stream_subscriptions "
221
233
  "ADD COLUMN IF NOT EXISTS result JSONB NULL"