edda-framework 0.9.1__tar.gz → 0.11.0__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.
- {edda_framework-0.9.1 → edda_framework-0.11.0}/Justfile +19 -2
- {edda_framework-0.9.1 → edda_framework-0.11.0}/PKG-INFO +17 -1
- {edda_framework-0.9.1 → edda_framework-0.11.0}/README.md +9 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/demo_app.py +24 -6
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/api/reference.md +8 -0
- edda_framework-0.11.0/docs/core-features/events/postgres-notify.md +166 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/getting-started/installation.md +45 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/index.md +1 -0
- edda_framework-0.11.0/docs/integrations/mirascope.md +373 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/setup.md +13 -1
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/app.py +419 -26
- edda_framework-0.11.0/edda/integrations/mirascope/__init__.py +78 -0
- edda_framework-0.11.0/edda/integrations/mirascope/agent.py +467 -0
- edda_framework-0.11.0/edda/integrations/mirascope/call.py +166 -0
- edda_framework-0.11.0/edda/integrations/mirascope/decorator.py +163 -0
- edda_framework-0.11.0/edda/integrations/mirascope/types.py +268 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/outbox/relayer.py +21 -2
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/storage/__init__.py +8 -0
- edda_framework-0.11.0/edda/storage/notify_base.py +162 -0
- edda_framework-0.11.0/edda/storage/pg_notify.py +325 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/storage/protocol.py +9 -1
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/storage/sqlalchemy_storage.py +193 -13
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/viewer_ui/app.py +26 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/viewer_ui/data_service.py +4 -0
- edda_framework-0.11.0/examples/mirascope/__init__.py +6 -0
- edda_framework-0.11.0/examples/mirascope/durable_agent.py +421 -0
- edda_framework-0.11.0/examples/mirascope/multi_turn.py +150 -0
- edda_framework-0.11.0/examples/mirascope/simple_call.py +167 -0
- edda_framework-0.11.0/examples/mirascope/with_tools.py +198 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/pyproject.toml +19 -1
- edda_framework-0.11.0/tests/integrations/mirascope/__init__.py +1 -0
- edda_framework-0.11.0/tests/integrations/mirascope/test_agent.py +403 -0
- edda_framework-0.11.0/tests/integrations/mirascope/test_call.py +183 -0
- edda_framework-0.11.0/tests/integrations/mirascope/test_decorator.py +235 -0
- edda_framework-0.11.0/tests/integrations/mirascope/test_types.py +449 -0
- edda_framework-0.11.0/tests/test_pg_notify.py +298 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_storage.py +156 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/uv.lock +399 -33
- {edda_framework-0.9.1 → edda_framework-0.11.0}/zensical.toml +1 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/.github/workflows/ci.yml +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/.github/workflows/docs.yml +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/.github/workflows/release.yml +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/.gitignore +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/.python-version +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/LICENSE +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/durable-execution/replay.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/events/cloudevents-http-binding.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/events/wait-event.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/hooks.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/messages.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/retry.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/saga-compensation.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/transactional-outbox.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/core-features/workflows-activities.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/examples/ecommerce.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/examples/events.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/examples/fastapi-integration.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/examples/saga.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/examples/simple.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/getting-started/concepts.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/getting-started/first-workflow.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/getting-started/quick-start.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/integrations/mcp.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/integrations/opentelemetry.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/integrations/pydantic-rpc.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/cloudevents-cli-trigger.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/compensation-execution.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/detail-page-loan-approval.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/detail-page-match-case.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/nested-pydantic-form.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/start-workflow-form-pydantic.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/wait-event-visualization.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/workflow-list-view.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/images/workflow-selection-dropdown.png +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/docs/viewer-ui/visualization.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/activity.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/channels.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/compensation.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/context.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/exceptions.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/hooks.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/mcp/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/mcp/decorators.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/mcp/server.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/opentelemetry/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/integrations/opentelemetry/hooks.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/locking.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/outbox/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/outbox/transactional.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/pydantic_utils.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/replay.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/retry.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/serialization/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/serialization/base.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/serialization/json.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/storage/models.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/viewer_ui/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/viewer_ui/components.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/viewer_ui/theme.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/visualizer/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/visualizer/ast_analyzer.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/visualizer/mermaid_generator.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/edda/wsgi.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/cancellable_workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/compensation_workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/event_waiting_app.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/event_waiting_workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/event_waiting_workflow_complete.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/long_running_loop.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/mcp/README.md +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/mcp/order_processing_mcp.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/mcp/prompts_example.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/mcp/remote_server_example.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/mcp/simple_mcp_server.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/message_passing.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/observability_with_logfire.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/observability_with_opentelemetry.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/pydantic_rpc_integration.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/pydantic_saga.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/retry_example.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/retry_with_compensation.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/simple_workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/typeddict_example.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/examples/with_outbox.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/conftest.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/test_cancel.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/test_integration.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/test_jsonrpc.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/test_prompts.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/mcp/test_server.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/opentelemetry/__init__.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/integrations/opentelemetry/test_hooks.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_activity.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_activity_retry.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_activity_sync.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_app.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_ast_analyzer.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_atomic_wait_event.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_auto_migration.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_binary_data.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_channel_competing.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_channel_transactional.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_cloudevents_http_binding.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_compensation.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_compensation_crash_recovery.py.wip +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_concurrent_outbox.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_context.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_ctx_session.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_distributed_event_delivery.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_events.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_instance_id_routing.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_lock_race_condition.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_lock_timeout_customization.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_locking.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_message_cleanup.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_message_delivery_lock.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_messages.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_multidb_storage.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_outbox.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_pydantic_activity.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_pydantic_enum.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_pydantic_events.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_pydantic_saga.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_pydantic_utils.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_receive_timeout.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_received_event.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_recur.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_recur_cleanup.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_replay.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_retry_policy.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_saga_parameter_extraction.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_serialization.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_skip_locked.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_stale_workflow_recovery.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_storage_mysql.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_storage_postgresql.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_transactions.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_viewer_pagination.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_viewer_pydantic_form.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_viewer_start_saga.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_wait_timer.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_workflow.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_workflow_auto_register.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_workflow_cancellation.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/tests/test_workflow_resumption.py +0 -0
- {edda_framework-0.9.1 → edda_framework-0.11.0}/viewer_app.py +0 -0
|
@@ -11,7 +11,7 @@ install:
|
|
|
11
11
|
|
|
12
12
|
# Install all dependencies including database drivers
|
|
13
13
|
install-all:
|
|
14
|
-
uv sync --extra dev --extra postgresql --extra mysql
|
|
14
|
+
uv sync --extra dev --extra postgresql --extra mysql --extra postgres-notify
|
|
15
15
|
|
|
16
16
|
# Install viewer dependencies
|
|
17
17
|
install-viewer:
|
|
@@ -71,15 +71,32 @@ demo:
|
|
|
71
71
|
# Run demo application with PostgreSQL (requires local PostgreSQL)
|
|
72
72
|
# Usage: just demo-postgresql
|
|
73
73
|
# Requires: EDDA_POSTGRES_PASSWORD environment variable
|
|
74
|
+
# Uses LISTEN/NOTIFY for instant notifications (auto-detected)
|
|
74
75
|
demo-postgresql:
|
|
76
|
+
@echo "Stopping existing demo app on port 8001..."
|
|
77
|
+
@lsof -ti :8001 | xargs kill -15 2>/dev/null || true
|
|
78
|
+
@sleep 1
|
|
79
|
+
@lsof -ti :8001 | xargs kill -9 2>/dev/null || true
|
|
80
|
+
@echo "Ensuring server and PostgreSQL dependencies are installed..."
|
|
81
|
+
@uv sync --extra server --extra postgresql --extra postgres-notify --quiet
|
|
82
|
+
@echo "Starting demo app with PostgreSQL (NOTIFY enabled)..."
|
|
83
|
+
EDDA_DB_URL="postgresql+asyncpg://postgres:{{env_var('EDDA_POSTGRES_PASSWORD')}}@localhost:5432/edda" \
|
|
84
|
+
uv run tsuno demo_app:application --bind 127.0.0.1:8001
|
|
85
|
+
|
|
86
|
+
# Run demo application with PostgreSQL but WITHOUT NOTIFY (polling only)
|
|
87
|
+
# Usage: just demo-postgresql-polling
|
|
88
|
+
# Requires: EDDA_POSTGRES_PASSWORD environment variable
|
|
89
|
+
# Uses polling-only mode (no LISTEN/NOTIFY) for comparison testing
|
|
90
|
+
demo-postgresql-polling:
|
|
75
91
|
@echo "Stopping existing demo app on port 8001..."
|
|
76
92
|
@lsof -ti :8001 | xargs kill -15 2>/dev/null || true
|
|
77
93
|
@sleep 1
|
|
78
94
|
@lsof -ti :8001 | xargs kill -9 2>/dev/null || true
|
|
79
95
|
@echo "Ensuring server and PostgreSQL dependencies are installed..."
|
|
80
96
|
@uv sync --extra server --extra postgresql --quiet
|
|
81
|
-
@echo "Starting demo app with PostgreSQL..."
|
|
97
|
+
@echo "Starting demo app with PostgreSQL (polling only, NOTIFY disabled)..."
|
|
82
98
|
EDDA_DB_URL="postgresql+asyncpg://postgres:{{env_var('EDDA_POSTGRES_PASSWORD')}}@localhost:5432/edda" \
|
|
99
|
+
EDDA_USE_NOTIFY=false \
|
|
83
100
|
uv run tsuno demo_app:application --bind 127.0.0.1:8001
|
|
84
101
|
|
|
85
102
|
# Run viewer with demo_app using PostgreSQL (requires local PostgreSQL)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: edda-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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
|
|
@@ -28,6 +28,8 @@ Requires-Dist: httpx>=0.28.1
|
|
|
28
28
|
Requires-Dist: pydantic>=2.0.0
|
|
29
29
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.0
|
|
30
30
|
Requires-Dist: uvloop>=0.22.1
|
|
31
|
+
Provides-Extra: cpu-monitor
|
|
32
|
+
Requires-Dist: psutil>=5.9.0; extra == 'cpu-monitor'
|
|
31
33
|
Provides-Extra: dev
|
|
32
34
|
Requires-Dist: black>=25.9.0; extra == 'dev'
|
|
33
35
|
Requires-Dist: mcp>=1.22.0; extra == 'dev'
|
|
@@ -42,12 +44,17 @@ Requires-Dist: testcontainers[postgres]>=4.0.0; extra == 'dev'
|
|
|
42
44
|
Requires-Dist: tsuno>=0.1.3; extra == 'dev'
|
|
43
45
|
Provides-Extra: mcp
|
|
44
46
|
Requires-Dist: mcp>=1.22.0; extra == 'mcp'
|
|
47
|
+
Provides-Extra: mirascope
|
|
48
|
+
Requires-Dist: mirascope[anthropic,google,openai]>=2.0.0a0; extra == 'mirascope'
|
|
49
|
+
Requires-Dist: pydantic-settings>=2.0.0; extra == 'mirascope'
|
|
45
50
|
Provides-Extra: mysql
|
|
46
51
|
Requires-Dist: aiomysql>=0.2.0; extra == 'mysql'
|
|
47
52
|
Provides-Extra: opentelemetry
|
|
48
53
|
Requires-Dist: opentelemetry-api>=1.20.0; extra == 'opentelemetry'
|
|
49
54
|
Requires-Dist: opentelemetry-exporter-otlp>=1.20.0; extra == 'opentelemetry'
|
|
50
55
|
Requires-Dist: opentelemetry-sdk>=1.20.0; extra == 'opentelemetry'
|
|
56
|
+
Provides-Extra: postgres-notify
|
|
57
|
+
Requires-Dist: asyncpg>=0.30.0; extra == 'postgres-notify'
|
|
51
58
|
Provides-Extra: postgresql
|
|
52
59
|
Requires-Dist: asyncpg>=0.30.0; extra == 'postgresql'
|
|
53
60
|
Provides-Extra: server
|
|
@@ -87,7 +94,9 @@ For detailed documentation, visit [https://i2y.github.io/edda/](https://i2y.gith
|
|
|
87
94
|
- ☁️ **CloudEvents Support**: Native support for CloudEvents protocol
|
|
88
95
|
- ⏱️ **Event & Timer Waiting**: Free up worker resources while waiting for events or timers, resume on any available worker
|
|
89
96
|
- 📬 **Channel-based Messaging**: Actor-model style communication with competing (job queue) and broadcast (fan-out) modes
|
|
97
|
+
- ⚡ **Instant Notifications**: PostgreSQL LISTEN/NOTIFY for near-instant event delivery (optional)
|
|
90
98
|
- 🤖 **MCP Integration**: Expose durable workflows as AI tools via Model Context Protocol
|
|
99
|
+
- 🧠 **Mirascope Integration**: Durable LLM calls
|
|
91
100
|
- 🌍 **ASGI/WSGI Support**: Deploy with your preferred server (uvicorn, gunicorn, uWSGI)
|
|
92
101
|
|
|
93
102
|
## Use Cases
|
|
@@ -221,6 +230,9 @@ uv add edda-framework --extra mysql
|
|
|
221
230
|
# With Viewer UI
|
|
222
231
|
uv add edda-framework --extra viewer
|
|
223
232
|
|
|
233
|
+
# With PostgreSQL instant notifications (LISTEN/NOTIFY)
|
|
234
|
+
uv add edda-framework --extra postgres-notify
|
|
235
|
+
|
|
224
236
|
# All extras (PostgreSQL, MySQL, Viewer UI)
|
|
225
237
|
uv add edda-framework --extra postgresql --extra mysql --extra viewer
|
|
226
238
|
```
|
|
@@ -272,6 +284,8 @@ pip install "git+https://github.com/i2y/edda.git[postgresql,viewer]"
|
|
|
272
284
|
|
|
273
285
|
**Important**: For multi-process or multi-pod deployments (K8s, Docker Compose with multiple replicas, etc.), you **must** use PostgreSQL or MySQL. SQLite supports multiple async workers within a single process, but its table-level locking makes it unsuitable for multi-process/multi-pod scenarios.
|
|
274
286
|
|
|
287
|
+
> **Tip**: For PostgreSQL, install the `postgres-notify` extra for near-instant event delivery using LISTEN/NOTIFY instead of polling.
|
|
288
|
+
|
|
275
289
|
### Development Installation
|
|
276
290
|
|
|
277
291
|
If you want to contribute to Edda or modify the framework itself:
|
|
@@ -492,6 +506,8 @@ app = EddaApp(
|
|
|
492
506
|
# Connection pool settings (optional)
|
|
493
507
|
pool_size=5, # Concurrent connections
|
|
494
508
|
max_overflow=10, # Additional burst capacity
|
|
509
|
+
# Batch processing (optional)
|
|
510
|
+
max_workflows_per_batch=10, # Or "auto" / "auto:cpu" for dynamic scaling
|
|
495
511
|
)
|
|
496
512
|
```
|
|
497
513
|
|
|
@@ -29,7 +29,9 @@ For detailed documentation, visit [https://i2y.github.io/edda/](https://i2y.gith
|
|
|
29
29
|
- ☁️ **CloudEvents Support**: Native support for CloudEvents protocol
|
|
30
30
|
- ⏱️ **Event & Timer Waiting**: Free up worker resources while waiting for events or timers, resume on any available worker
|
|
31
31
|
- 📬 **Channel-based Messaging**: Actor-model style communication with competing (job queue) and broadcast (fan-out) modes
|
|
32
|
+
- ⚡ **Instant Notifications**: PostgreSQL LISTEN/NOTIFY for near-instant event delivery (optional)
|
|
32
33
|
- 🤖 **MCP Integration**: Expose durable workflows as AI tools via Model Context Protocol
|
|
34
|
+
- 🧠 **Mirascope Integration**: Durable LLM calls
|
|
33
35
|
- 🌍 **ASGI/WSGI Support**: Deploy with your preferred server (uvicorn, gunicorn, uWSGI)
|
|
34
36
|
|
|
35
37
|
## Use Cases
|
|
@@ -163,6 +165,9 @@ uv add edda-framework --extra mysql
|
|
|
163
165
|
# With Viewer UI
|
|
164
166
|
uv add edda-framework --extra viewer
|
|
165
167
|
|
|
168
|
+
# With PostgreSQL instant notifications (LISTEN/NOTIFY)
|
|
169
|
+
uv add edda-framework --extra postgres-notify
|
|
170
|
+
|
|
166
171
|
# All extras (PostgreSQL, MySQL, Viewer UI)
|
|
167
172
|
uv add edda-framework --extra postgresql --extra mysql --extra viewer
|
|
168
173
|
```
|
|
@@ -214,6 +219,8 @@ pip install "git+https://github.com/i2y/edda.git[postgresql,viewer]"
|
|
|
214
219
|
|
|
215
220
|
**Important**: For multi-process or multi-pod deployments (K8s, Docker Compose with multiple replicas, etc.), you **must** use PostgreSQL or MySQL. SQLite supports multiple async workers within a single process, but its table-level locking makes it unsuitable for multi-process/multi-pod scenarios.
|
|
216
221
|
|
|
222
|
+
> **Tip**: For PostgreSQL, install the `postgres-notify` extra for near-instant event delivery using LISTEN/NOTIFY instead of polling.
|
|
223
|
+
|
|
217
224
|
### Development Installation
|
|
218
225
|
|
|
219
226
|
If you want to contribute to Edda or modify the framework itself:
|
|
@@ -434,6 +441,8 @@ app = EddaApp(
|
|
|
434
441
|
# Connection pool settings (optional)
|
|
435
442
|
pool_size=5, # Concurrent connections
|
|
436
443
|
max_overflow=10, # Additional burst capacity
|
|
444
|
+
# Batch processing (optional)
|
|
445
|
+
max_workflows_per_batch=10, # Or "auto" / "auto:cpu" for dynamic scaling
|
|
437
446
|
)
|
|
438
447
|
```
|
|
439
448
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Demo Edda ASGI application for tsuno."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import logging
|
|
4
5
|
import os
|
|
5
6
|
import sys
|
|
6
7
|
from datetime import datetime
|
|
@@ -8,9 +9,17 @@ from enum import Enum
|
|
|
8
9
|
from typing import Any, cast
|
|
9
10
|
|
|
10
11
|
import uvloop
|
|
11
|
-
from pydantic import BaseModel, Field, field_validator
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# Configure logging to see Edda startup messages (must be before edda import)
|
|
14
|
+
logging.basicConfig(
|
|
15
|
+
level=logging.INFO,
|
|
16
|
+
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
|
|
17
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from pydantic import BaseModel, Field, field_validator # noqa: E402
|
|
21
|
+
|
|
22
|
+
from edda import ( # noqa: E402
|
|
14
23
|
EddaApp,
|
|
15
24
|
RetryPolicy,
|
|
16
25
|
WorkflowContext,
|
|
@@ -24,7 +33,7 @@ from edda import (
|
|
|
24
33
|
wait_until,
|
|
25
34
|
workflow,
|
|
26
35
|
)
|
|
27
|
-
from edda.wsgi import create_wsgi_app
|
|
36
|
+
from edda.wsgi import create_wsgi_app # noqa: E402
|
|
28
37
|
|
|
29
38
|
# Python 3.12+ uses asyncio.set_event_loop_policy() instead of uvloop.install()
|
|
30
39
|
if sys.version_info >= (3, 12):
|
|
@@ -408,9 +417,18 @@ class ScheduledShipmentResult(BaseModel):
|
|
|
408
417
|
|
|
409
418
|
# Create Edda application
|
|
410
419
|
# Use environment variable EDDA_DB_URL if available, otherwise default to sqlite:///demo.db
|
|
420
|
+
# Use EDDA_USE_NOTIFY to control LISTEN/NOTIFY (true/false/auto, default: auto)
|
|
421
|
+
_use_notify_env = os.getenv("EDDA_USE_NOTIFY", "auto").lower()
|
|
422
|
+
_use_listen_notify: bool | None = None # Auto-detect
|
|
423
|
+
if _use_notify_env == "true":
|
|
424
|
+
_use_listen_notify = True
|
|
425
|
+
elif _use_notify_env == "false":
|
|
426
|
+
_use_listen_notify = False
|
|
427
|
+
|
|
411
428
|
app = EddaApp(
|
|
412
429
|
service_name="demo-service",
|
|
413
430
|
db_url=os.getenv("EDDA_DB_URL", "sqlite:///demo.db"),
|
|
431
|
+
use_listen_notify=_use_listen_notify,
|
|
414
432
|
)
|
|
415
433
|
|
|
416
434
|
|
|
@@ -2115,7 +2133,7 @@ async def job_publisher_workflow(
|
|
|
2115
2133
|
"""
|
|
2116
2134
|
print(f"\n[PUBLISHER] Publishing job: {input.task}")
|
|
2117
2135
|
await publish(ctx, "jobs", {"task": input.task})
|
|
2118
|
-
print(
|
|
2136
|
+
print("[PUBLISHER] Job published to 'jobs' channel")
|
|
2119
2137
|
return {"published": True, "channel": "jobs", "task": input.task}
|
|
2120
2138
|
|
|
2121
2139
|
|
|
@@ -2135,7 +2153,7 @@ async def notification_publisher_workflow(
|
|
|
2135
2153
|
"""
|
|
2136
2154
|
print(f"\n[PUBLISHER] Publishing notification: {input.message}")
|
|
2137
2155
|
await publish(ctx, "notifications", {"message": input.message})
|
|
2138
|
-
print(
|
|
2156
|
+
print("[PUBLISHER] Notification published to 'notifications' channel")
|
|
2139
2157
|
return {"published": True, "channel": "notifications", "message": input.message}
|
|
2140
2158
|
|
|
2141
2159
|
|
|
@@ -2194,7 +2212,7 @@ async def direct_message_sender_workflow(
|
|
|
2194
2212
|
|
|
2195
2213
|
await send_to(ctx, input.target_instance_id, {"message": input.message})
|
|
2196
2214
|
|
|
2197
|
-
print(
|
|
2215
|
+
print("[SENDER] Message sent!")
|
|
2198
2216
|
return DirectMessageSenderResult(
|
|
2199
2217
|
sent=True,
|
|
2200
2218
|
target_instance_id=input.target_instance_id,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# PostgreSQL LISTEN/NOTIFY
|
|
2
|
+
|
|
3
|
+
Edda supports PostgreSQL's LISTEN/NOTIFY mechanism for near-instant event and message delivery. This optional feature significantly reduces latency compared to polling-based delivery.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
By default, Edda uses polling to check for new events and messages. With PostgreSQL LISTEN/NOTIFY enabled:
|
|
8
|
+
|
|
9
|
+
- **Event delivery**: Near-instant (milliseconds) instead of polling interval
|
|
10
|
+
- **Message delivery**: Near-instant instead of polling interval
|
|
11
|
+
- **Database load**: Reduced polling queries
|
|
12
|
+
- **Fallback**: Automatic fallback to polling if notifications are missed
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Install the `postgres-notify` extra:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Using uv
|
|
20
|
+
uv add edda-framework --extra postgres-notify
|
|
21
|
+
|
|
22
|
+
# Using pip
|
|
23
|
+
pip install "edda-framework[postgres-notify]"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This installs `asyncpg`, which is required for LISTEN/NOTIFY support.
|
|
27
|
+
|
|
28
|
+
## Configuration
|
|
29
|
+
|
|
30
|
+
### Basic Usage (Auto-detection)
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from edda import EddaApp
|
|
34
|
+
|
|
35
|
+
# LISTEN/NOTIFY is auto-detected for PostgreSQL
|
|
36
|
+
app = EddaApp(
|
|
37
|
+
service_name="demo-service",
|
|
38
|
+
db_url="postgresql://user:password@localhost/workflows",
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
When using PostgreSQL, LISTEN/NOTIFY is automatically enabled if `asyncpg` is installed.
|
|
43
|
+
|
|
44
|
+
### Explicit Configuration
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from edda import EddaApp
|
|
48
|
+
|
|
49
|
+
app = EddaApp(
|
|
50
|
+
service_name="demo-service",
|
|
51
|
+
db_url="postgresql://user:password@localhost/workflows",
|
|
52
|
+
use_listen_notify=True, # Force enable
|
|
53
|
+
notify_fallback_interval=30, # Fallback polling every 30 seconds
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Configuration Options
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Default | Description |
|
|
60
|
+
|-----------|------|---------|-------------|
|
|
61
|
+
| `use_listen_notify` | `bool \| None` | `None` | Notification mode: `None` = auto-detect, `True` = force enable, `False` = force disable |
|
|
62
|
+
| `notify_fallback_interval` | `int` | `30` | Fallback polling interval in seconds when NOTIFY is enabled |
|
|
63
|
+
| `max_workflows_per_batch` | `int \| "auto" \| "auto:cpu"` | `10` | Workflows per resume cycle. `"auto"` scales by queue depth, `"auto:cpu"` by CPU usage |
|
|
64
|
+
|
|
65
|
+
### Auto-detection Behavior
|
|
66
|
+
|
|
67
|
+
| Database | `use_listen_notify=None` | `use_listen_notify=True` | `use_listen_notify=False` |
|
|
68
|
+
|----------|--------------------------|--------------------------|---------------------------|
|
|
69
|
+
| PostgreSQL | Enabled (if asyncpg installed) | Enabled (error if asyncpg missing) | Disabled (polling only) |
|
|
70
|
+
| MySQL | Disabled | Error | Disabled |
|
|
71
|
+
| SQLite | Disabled | Error | Disabled |
|
|
72
|
+
|
|
73
|
+
## How It Works
|
|
74
|
+
|
|
75
|
+
### Architecture
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
┌─────────────────┐ NOTIFY ┌─────────────────┐
|
|
79
|
+
│ Worker Pod 1 │ <────────────── │ PostgreSQL │
|
|
80
|
+
│ (Edda App) │ │ Database │
|
|
81
|
+
└─────────────────┘ └─────────────────┘
|
|
82
|
+
│ │
|
|
83
|
+
│ LISTEN │
|
|
84
|
+
└───────────────────────────────────┘
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
1. **Dedicated Connection**: Edda maintains a separate asyncpg connection for LISTEN/NOTIFY
|
|
88
|
+
2. **Channel Subscription**: Subscribes to notification channels on startup
|
|
89
|
+
3. **Instant Wakeup**: When a notification arrives, waiting workflows are immediately resumed
|
|
90
|
+
4. **Fallback Polling**: Periodic polling ensures no notifications are missed
|
|
91
|
+
|
|
92
|
+
### Notification Channels
|
|
93
|
+
|
|
94
|
+
Edda uses the following PostgreSQL channels:
|
|
95
|
+
|
|
96
|
+
- `edda_workflow_resume`: Notifies when workflows should be resumed
|
|
97
|
+
- `edda_outbox_ready`: Notifies when outbox events are ready for relay
|
|
98
|
+
|
|
99
|
+
## Performance Comparison
|
|
100
|
+
|
|
101
|
+
| Metric | Polling Only | With LISTEN/NOTIFY |
|
|
102
|
+
|--------|--------------|-------------------|
|
|
103
|
+
| Event delivery latency | 0.5-1s (polling interval) | ~10-50ms |
|
|
104
|
+
| Message delivery latency | 0.5-1s | ~10-50ms |
|
|
105
|
+
| Database queries per idle workflow | Every 1s | Every 30s (fallback only) |
|
|
106
|
+
| Connection overhead | 1 per pool | +1 dedicated LISTEN connection |
|
|
107
|
+
|
|
108
|
+
## Reliability Features
|
|
109
|
+
|
|
110
|
+
### Automatic Reconnection
|
|
111
|
+
|
|
112
|
+
The LISTEN connection automatically reconnects on failure with configurable retry settings.
|
|
113
|
+
|
|
114
|
+
### Fallback Polling
|
|
115
|
+
|
|
116
|
+
Even with NOTIFY enabled, Edda maintains fallback polling:
|
|
117
|
+
|
|
118
|
+
- **Default interval**: 30 seconds (`notify_fallback_interval`)
|
|
119
|
+
- **Purpose**: Catch any missed notifications
|
|
120
|
+
- **Behavior**: Polling runs in parallel with NOTIFY
|
|
121
|
+
|
|
122
|
+
## Troubleshooting
|
|
123
|
+
|
|
124
|
+
### asyncpg Not Installed
|
|
125
|
+
|
|
126
|
+
If you see a warning about asyncpg not being installed:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
WARNING: asyncpg not installed, falling back to polling-only mode.
|
|
130
|
+
Install with: pip install edda-framework[postgres-notify]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Solution**: Install the postgres-notify extra.
|
|
134
|
+
|
|
135
|
+
### Force Enable on Non-PostgreSQL
|
|
136
|
+
|
|
137
|
+
If you see an error about LISTEN/NOTIFY requiring PostgreSQL:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
ValueError: use_listen_notify=True requires PostgreSQL database.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Solution**: Use `use_listen_notify=None` (auto-detect) or `False` for non-PostgreSQL databases.
|
|
144
|
+
|
|
145
|
+
### Connection Lost
|
|
146
|
+
|
|
147
|
+
If you see reconnection messages in logs:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
INFO: Connection lost, attempting reconnection...
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This is normal behavior. Edda automatically reconnects, and workflows continue with polling fallback during reconnection.
|
|
154
|
+
|
|
155
|
+
## Best Practices
|
|
156
|
+
|
|
157
|
+
1. **Use auto-detection**: Set `use_listen_notify=None` (default) for portability across different database backends
|
|
158
|
+
2. **Install postgres-notify in production**: For best performance with PostgreSQL
|
|
159
|
+
3. **Monitor reconnections**: Frequent reconnection warnings may indicate network issues
|
|
160
|
+
4. **Adjust fallback interval**: Lower values provide more reliability but increase database load
|
|
161
|
+
|
|
162
|
+
## Related Topics
|
|
163
|
+
|
|
164
|
+
- [Event Waiting](wait-event.md) - How workflows wait for events
|
|
165
|
+
- [Channel-based Messaging](../messages.md) - Workflow-to-workflow communication
|
|
166
|
+
- [Installation](../../getting-started/installation.md) - Database setup
|
|
@@ -38,6 +38,9 @@ uv add edda-framework --extra mysql
|
|
|
38
38
|
# With Viewer UI (workflow visualization)
|
|
39
39
|
uv add edda-framework --extra viewer
|
|
40
40
|
|
|
41
|
+
# With PostgreSQL instant notifications (LISTEN/NOTIFY)
|
|
42
|
+
uv add edda-framework --extra postgres-notify
|
|
43
|
+
|
|
41
44
|
# All extras (PostgreSQL + MySQL + Viewer UI)
|
|
42
45
|
uv add edda-framework --extra postgresql --extra mysql --extra viewer
|
|
43
46
|
```
|
|
@@ -48,6 +51,7 @@ uv add edda-framework --extra postgresql --extra mysql --extra viewer
|
|
|
48
51
|
- **postgresql**: `asyncpg` driver for PostgreSQL
|
|
49
52
|
- **mysql**: `aiomysql` driver for MySQL
|
|
50
53
|
- **viewer**: `nicegui` and `httpx` for workflow visualization UI
|
|
54
|
+
- **postgres-notify**: `asyncpg` driver for PostgreSQL LISTEN/NOTIFY instant notifications
|
|
51
55
|
|
|
52
56
|
### Using pip
|
|
53
57
|
|
|
@@ -66,6 +70,9 @@ pip install "edda-framework[mysql]"
|
|
|
66
70
|
# With Viewer UI
|
|
67
71
|
pip install "edda-framework[viewer]"
|
|
68
72
|
|
|
73
|
+
# With PostgreSQL instant notifications
|
|
74
|
+
pip install "edda-framework[postgres-notify]"
|
|
75
|
+
|
|
69
76
|
# All extras
|
|
70
77
|
pip install "edda-framework[postgresql,mysql,viewer]"
|
|
71
78
|
```
|
|
@@ -244,6 +251,44 @@ app = EddaApp(
|
|
|
244
251
|
)
|
|
245
252
|
```
|
|
246
253
|
|
|
254
|
+
#### Enabling Instant Notifications (LISTEN/NOTIFY)
|
|
255
|
+
|
|
256
|
+
For near-instant event and message delivery, enable PostgreSQL LISTEN/NOTIFY:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Install the postgres-notify extra
|
|
260
|
+
uv add edda-framework --extra postgres-notify
|
|
261
|
+
# or
|
|
262
|
+
pip install "edda-framework[postgres-notify]"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from edda import EddaApp
|
|
267
|
+
|
|
268
|
+
app = EddaApp(
|
|
269
|
+
service_name="demo-service",
|
|
270
|
+
db_url="postgresql://user:password@localhost/edda_workflows",
|
|
271
|
+
use_listen_notify=True, # Enable LISTEN/NOTIFY (auto-detected by default)
|
|
272
|
+
notify_fallback_interval=30, # Fallback polling interval in seconds
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Configuration options:**
|
|
277
|
+
|
|
278
|
+
| Parameter | Type | Default | Description |
|
|
279
|
+
|-----------|------|---------|-------------|
|
|
280
|
+
| `use_listen_notify` | `bool \| None` | `None` | `None` = auto-detect (enabled for PostgreSQL), `True` = force enable, `False` = force disable |
|
|
281
|
+
| `notify_fallback_interval` | `int` | `30` | Fallback polling interval in seconds when NOTIFY is enabled |
|
|
282
|
+
|
|
283
|
+
**Benefits:**
|
|
284
|
+
|
|
285
|
+
- Near-instant event delivery (milliseconds vs. seconds with polling)
|
|
286
|
+
- Reduced database load (fewer polling queries)
|
|
287
|
+
- Automatic fallback to polling if NOTIFY fails
|
|
288
|
+
- Automatic reconnection on connection loss
|
|
289
|
+
|
|
290
|
+
See [PostgreSQL LISTEN/NOTIFY](../core-features/events/postgres-notify.md) for detailed documentation.
|
|
291
|
+
|
|
247
292
|
### MySQL
|
|
248
293
|
|
|
249
294
|
1. **Install MySQL** (if not already installed)
|
|
@@ -27,6 +27,7 @@ Edda is a lightweight durable execution framework for Python that runs as a **li
|
|
|
27
27
|
- ☁️ **CloudEvents Support**: Native support for CloudEvents protocol
|
|
28
28
|
- ⏱️ **Event & Timer Waiting**: Free up worker resources while waiting for events or timers, resume on any available worker
|
|
29
29
|
- 📬 **Message Passing**: Channel-based messaging (broadcast/competing modes) and direct workflow-to-workflow communication
|
|
30
|
+
- ⚡ **Instant Notifications**: Optional PostgreSQL LISTEN/NOTIFY for near-instant event delivery
|
|
30
31
|
|
|
31
32
|
## Use Cases
|
|
32
33
|
|