simple-module-background-tasks 0.0.38__tar.gz → 0.0.39__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.
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/PKG-INFO +7 -7
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/_signal_support.py +2 -1
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/after_commit.py +6 -1
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/api_status.py +2 -1
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/filters.py +4 -3
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/log_purge.py +2 -1
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/log_service.py +8 -5
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_claim.py +6 -5
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_service.py +6 -5
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_worker.py +2 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/Outbox.tsx +21 -9
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/retry_service.py +3 -2
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/task_heartbeat.py +3 -2
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/tasks.py +3 -2
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/package.json +1 -1
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/pyproject.toml +7 -7
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/background_tasks_OutboxPage.test.tsx +13 -5
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/.gitignore +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/LICENSE +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/README.md +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/__init__.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/_lifecycle.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/_signal_bus.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/celery_app.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/config_guards.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/constants.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/contracts/__init__.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/contracts/events.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/contracts/logs.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/contracts/outbox.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/contracts/schemas.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/db_settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/deps.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/__init__.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/api_admin.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/logs_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/logs_views.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/outbox_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/outbox_views.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/endpoints/views.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/health.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/invalidation.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/locales/de.json +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/locales/en.json +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/log_context.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/log_sink.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/models.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/module.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_clock.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_dispatch.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_guard.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_loop.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_support.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_sweep.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/outbox_transport.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/Detail.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/Index.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/Logs.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/Workers.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/api.generated.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/api.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/DetailFacts.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/ExecutionRow.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/ExecutionsTable.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/LogLevelSelect.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/LogTable.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/LogsPanel.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/OutboxTable.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/PayloadCards.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/ProgressCard.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/RetryAllDialog.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/RetryConfirmDialog.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/RoutesTable.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/StatusPill.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/StatusStrip.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/TaskFilters.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/TasksEmpty.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/TracebackCard.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/WorkerCard.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/WorkerEmptyStates.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/components/WorkerHealthBanner.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/constants.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/logs.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/outbox.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/retry.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/pages/worker-health.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/py.typed +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/runner.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/service.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/services.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/settings_celery.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/settings_env_checks.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/settings_logs.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/signals.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/sync_db.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/task_routing.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/tenant_context.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/worker_inspector.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/worker_settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/background_tasks/workers_state.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/conftest.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_admin_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_background_tasks_after_commit.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_background_tasks_heartbeat.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_background_tasks_settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_background_tasks_tz_columns.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_beat_schedule.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_config_guards.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_db_timeouts.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_index_has_executions.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_index_worker_presence.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_inprocess_sweep.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_invalidation.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_invalidation_degraded.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_invalidation_redis.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_list_item_payload.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_log_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_log_purge.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_log_sink.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_log_sink_loop.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_log_sink_nul.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_eager_platform.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_fence_guard.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_guard_savepoint.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_loop_interrupt.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_publish_scope.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_retry_policy.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_swallowed_cancel.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_thread_loops.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_outbox_worker_chain.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_page_clamp.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_pagination_bounds.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_quiet_sweep_unrecorded.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_redis_health.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_redis_url.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_routes_override_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_runner_settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_service.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_settings_env.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_startup_log_masking.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_task_progress.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_task_progress_audit.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_task_routing.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_task_status_api.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_users_ordering.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_worker_audit.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_worker_audit_bookkeeping.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_bg_worker_settings.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_detail_props.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_execution_tenancy.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_index_filters.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_log_context.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_outbox_admin.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_outbox_concurrency.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_outbox_delivery.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_outbox_tenancy.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_outbox_worker_task.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_retry_failed_bulk.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_retry_failed_guards.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_retry_sweep_ordering.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_signal_tenant_stamp.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_signals.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_sweep_stuck.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_sync_db.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_sync_db_fork.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_tenant_context.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_worker_inspector.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_worker_tenancy.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_workers_endpoints.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests/test_workers_uptime.py +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/background_tasks_DetailPage.test.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/background_tasks_IndexPage.test.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/background_tasks_LogsTab.test.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/background_tasks_WorkerHealthBanner.test.tsx +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/outbox.test.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/retry.test.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/stub-fetch.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tests-js/task-formatters.test.ts +0 -0
- {simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: simple_module_background_tasks
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.39
|
|
4
4
|
Summary: Celery + Redis task queue with admin UI for monitoring and retrying failed/stuck tasks
|
|
5
5
|
Project-URL: Homepage, https://github.com/antosubash/simple_module_python
|
|
6
6
|
Project-URL: Repository, https://github.com/antosubash/simple_module_python
|
|
@@ -21,12 +21,12 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.12
|
|
24
|
-
Requires-Dist: celery[redis]
|
|
25
|
-
Requires-Dist: redis
|
|
26
|
-
Requires-Dist: simple-module-core==0.0.
|
|
27
|
-
Requires-Dist: simple-module-db==0.0.
|
|
28
|
-
Requires-Dist: simple-module-hosting==0.0.
|
|
29
|
-
Requires-Dist: simple-module-settings==0.0.
|
|
24
|
+
Requires-Dist: celery[redis]<6,>=5.4
|
|
25
|
+
Requires-Dist: redis<7,>=5
|
|
26
|
+
Requires-Dist: simple-module-core==0.0.39
|
|
27
|
+
Requires-Dist: simple-module-db==0.0.39
|
|
28
|
+
Requires-Dist: simple-module-hosting==0.0.39
|
|
29
|
+
Requires-Dist: simple-module-settings==0.0.39
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
# simple_module_background_tasks
|
|
@@ -15,6 +15,7 @@ from typing import Any
|
|
|
15
15
|
|
|
16
16
|
from sqlalchemy import select
|
|
17
17
|
from sqlalchemy.orm import Session
|
|
18
|
+
from sqlmodel import col
|
|
18
19
|
|
|
19
20
|
from background_tasks.constants import INTERNAL_TASK_SWEEP_OUTBOX, TaskStatus
|
|
20
21
|
from background_tasks.models import TaskExecution
|
|
@@ -53,7 +54,7 @@ def upsert_by_celery_id(
|
|
|
53
54
|
"""
|
|
54
55
|
row: TaskExecution | None = None
|
|
55
56
|
if celery_task_id:
|
|
56
|
-
stmt = select(TaskExecution).where(TaskExecution.celery_task_id == celery_task_id)
|
|
57
|
+
stmt = select(TaskExecution).where(col(TaskExecution.celery_task_id) == celery_task_id)
|
|
57
58
|
row = session.execute(stmt).scalar_one_or_none()
|
|
58
59
|
if row is None:
|
|
59
60
|
row = TaskExecution(celery_task_id=celery_task_id, **defaults)
|
|
@@ -59,6 +59,11 @@ _HOOKED_KEY = "background_tasks_after_commit_hooked"
|
|
|
59
59
|
def enqueue_after_commit(db: AsyncSession | Session, task: Any, *args: Any, **kwargs: Any) -> str:
|
|
60
60
|
"""Send *task* once *db*'s transaction commits; never if it rolls back.
|
|
61
61
|
|
|
62
|
+
Delivery is at-most-once: publish intent exists only in memory. Broker
|
|
63
|
+
failure after commit is logged without retry; a process exit between commit
|
|
64
|
+
and publish also loses the job. For must-run work use the event bus's
|
|
65
|
+
``publish_durable(event, db)`` transactional outbox with a durable handler.
|
|
66
|
+
|
|
62
67
|
*task* is a Celery task (``send_receipt``), a signature
|
|
63
68
|
(``send_receipt.s().set(queue="mail")``, for routing options), or a
|
|
64
69
|
registered task name. ``*args`` / ``**kwargs`` are the task's own
|
|
@@ -89,7 +94,7 @@ def after_commit(
|
|
|
89
94
|
request. Left off for a single publish so Celery's eager mode — which runs
|
|
90
95
|
the task body inside the publish — stays on the loop's thread. One failed
|
|
91
96
|
publish is logged and does not stop the rest: the rows are already
|
|
92
|
-
committed
|
|
97
|
+
committed. Only callers with persisted pending work can reconcile failures.
|
|
93
98
|
"""
|
|
94
99
|
batch = list(publishes)
|
|
95
100
|
if isinstance(db, RequestSession) and SESSION_START_KEY in db.info:
|
|
@@ -18,6 +18,7 @@ from simple_module_db.deps import get_db
|
|
|
18
18
|
from simple_module_hosting.permissions import ensure_resolved_permissions
|
|
19
19
|
from simple_module_hosting.token_guard import Authenticated
|
|
20
20
|
from sqlalchemy import select
|
|
21
|
+
from sqlmodel import col
|
|
21
22
|
|
|
22
23
|
from background_tasks.constants import MODULE_NAME, PERM_VIEW, STATUS_ROUTER_PREFIX
|
|
23
24
|
from background_tasks.contracts.schemas import TaskStatusDTO
|
|
@@ -56,7 +57,7 @@ async def get_task_status(
|
|
|
56
57
|
row = (
|
|
57
58
|
await db.execute(
|
|
58
59
|
select(TaskExecution)
|
|
59
|
-
.where(TaskExecution.celery_task_id == celery_task_id)
|
|
60
|
+
.where(col(TaskExecution.celery_task_id) == celery_task_id)
|
|
60
61
|
.order_by(TaskExecution.queued_at.desc())
|
|
61
62
|
.limit(1)
|
|
62
63
|
)
|
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
from simple_module_db import LIKE_ESCAPE_CHAR, like_contains_pattern
|
|
12
12
|
from sqlalchemy import ColumnElement, select
|
|
13
|
+
from sqlmodel import col
|
|
13
14
|
|
|
14
15
|
from background_tasks.constants import PLATFORM_TENANT_FILTER, RETRYABLE_STATUSES, TaskStatus
|
|
15
16
|
from background_tasks.models import TaskExecution
|
|
@@ -34,15 +35,15 @@ def execution_filters(
|
|
|
34
35
|
if tenant_id == PLATFORM_TENANT_FILTER:
|
|
35
36
|
conditions.append(TaskExecution.tenant_id.is_(None))
|
|
36
37
|
elif tenant_id:
|
|
37
|
-
conditions.append(TaskExecution.tenant_id == tenant_id)
|
|
38
|
+
conditions.append(col(TaskExecution.tenant_id) == tenant_id)
|
|
38
39
|
if status is not None:
|
|
39
|
-
conditions.append(TaskExecution.status == status)
|
|
40
|
+
conditions.append(col(TaskExecution.status) == status)
|
|
40
41
|
if task_name:
|
|
41
42
|
conditions.append(
|
|
42
43
|
TaskExecution.task_name.ilike(like_contains_pattern(task_name), escape=LIKE_ESCAPE_CHAR)
|
|
43
44
|
)
|
|
44
45
|
if queue:
|
|
45
|
-
conditions.append(TaskExecution.queue == queue)
|
|
46
|
+
conditions.append(col(TaskExecution.queue) == queue)
|
|
46
47
|
return conditions
|
|
47
48
|
|
|
48
49
|
|
|
@@ -6,11 +6,12 @@ from datetime import datetime
|
|
|
6
6
|
|
|
7
7
|
from sqlalchemy import delete
|
|
8
8
|
from sqlalchemy.orm import Session
|
|
9
|
+
from sqlmodel import col
|
|
9
10
|
|
|
10
11
|
from background_tasks.models import RetainedLog
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def purge_log_records(session: Session, cutoff: datetime) -> int:
|
|
14
15
|
"""Delete log lines older than ``cutoff``; returns the count."""
|
|
15
|
-
result = session.execute(delete(RetainedLog).where(RetainedLog.ts < cutoff))
|
|
16
|
+
result = session.execute(delete(RetainedLog).where(col(RetainedLog.ts) < cutoff))
|
|
16
17
|
return result.rowcount or 0
|
|
@@ -9,6 +9,7 @@ from typing import Any
|
|
|
9
9
|
from simple_module_db import RequestSession
|
|
10
10
|
from simple_module_db.pagination import PageParams, paginate
|
|
11
11
|
from sqlalchemy import false, select
|
|
12
|
+
from sqlmodel import col
|
|
12
13
|
|
|
13
14
|
from background_tasks.constants import LOG_LEVEL_NAMES
|
|
14
15
|
from background_tasks.contracts.logs import LogItem, LogListResponse
|
|
@@ -60,16 +61,18 @@ class LogService:
|
|
|
60
61
|
) -> LogListResponse:
|
|
61
62
|
where = [*self.scope.conditions()]
|
|
62
63
|
if task_id is not None:
|
|
63
|
-
where.append(RetainedLog.task_id == task_id)
|
|
64
|
+
where.append(col(RetainedLog.task_id) == task_id)
|
|
64
65
|
if (level := level_number(min_level)) is not None:
|
|
65
|
-
where.append(RetainedLog.level >= level)
|
|
66
|
+
where.append(col(RetainedLog.level) >= level)
|
|
66
67
|
if q:
|
|
67
|
-
where.append(RetainedLog.message.ilike(_like(q), escape="\\"))
|
|
68
|
+
where.append(col(RetainedLog.message).ilike(_like(q), escape="\\"))
|
|
68
69
|
if logger_name:
|
|
69
|
-
where.append(RetainedLog.logger.startswith(logger_name))
|
|
70
|
+
where.append(col(RetainedLog.logger).startswith(logger_name))
|
|
70
71
|
found = await paginate(
|
|
71
72
|
self.db,
|
|
72
|
-
select(RetainedLog)
|
|
73
|
+
select(RetainedLog)
|
|
74
|
+
.where(*where)
|
|
75
|
+
.order_by(col(RetainedLog.ts).desc(), col(RetainedLog.id)),
|
|
73
76
|
PageParams(page=page, per_page=per_page),
|
|
74
77
|
)
|
|
75
78
|
return LogListResponse(
|
|
@@ -20,6 +20,7 @@ from typing import Any
|
|
|
20
20
|
|
|
21
21
|
from sqlalchemy import or_, select, update
|
|
22
22
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
23
|
+
from sqlmodel import col
|
|
23
24
|
|
|
24
25
|
from background_tasks.constants import (
|
|
25
26
|
OUTBOX_BACKOFF_BASE_SECONDS,
|
|
@@ -103,12 +104,12 @@ async def reclaim_expired(
|
|
|
103
104
|
async with factory() as session:
|
|
104
105
|
now = now or await db_now(session)
|
|
105
106
|
expired = (
|
|
106
|
-
OutboxMessage.status == OutboxStatus.RUNNING,
|
|
107
|
-
or_(OutboxMessage.locked_until < now, OutboxMessage.locked_until.is_(None)),
|
|
107
|
+
col(OutboxMessage.status) == OutboxStatus.RUNNING,
|
|
108
|
+
or_(col(OutboxMessage.locked_until) < now, col(OutboxMessage.locked_until).is_(None)),
|
|
108
109
|
)
|
|
109
110
|
dead = await session.execute(
|
|
110
111
|
update(OutboxMessage)
|
|
111
|
-
.where(*expired, OutboxMessage.attempts >= max_attempts)
|
|
112
|
+
.where(*expired, col(OutboxMessage.attempts) >= max_attempts)
|
|
112
113
|
.values(
|
|
113
114
|
status=OutboxStatus.DEAD,
|
|
114
115
|
locked_until=None,
|
|
@@ -118,7 +119,7 @@ async def reclaim_expired(
|
|
|
118
119
|
)
|
|
119
120
|
back = await session.execute(
|
|
120
121
|
update(OutboxMessage)
|
|
121
|
-
.where(*expired)
|
|
122
|
+
.where(*expired)
|
|
122
123
|
.values(status=OutboxStatus.PENDING, locked_until=None, next_attempt_at=now)
|
|
123
124
|
.execution_options(synchronize_session=False)
|
|
124
125
|
)
|
|
@@ -174,7 +175,7 @@ async def claim(
|
|
|
174
175
|
update(OutboxMessage)
|
|
175
176
|
.where(
|
|
176
177
|
OutboxMessage.id.in_(select(claimable.c.id)),
|
|
177
|
-
OutboxMessage.status == OutboxStatus.PENDING,
|
|
178
|
+
col(OutboxMessage.status) == OutboxStatus.PENDING,
|
|
178
179
|
)
|
|
179
180
|
.values(
|
|
180
181
|
status=OutboxStatus.RUNNING,
|
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
9
9
|
from fastapi import HTTPException, status
|
|
10
10
|
from simple_module_db.pagination import PageParams, paginate
|
|
11
11
|
from sqlalchemy import delete, false, func, select, update
|
|
12
|
+
from sqlmodel import col
|
|
12
13
|
|
|
13
14
|
from background_tasks.constants import OutboxStatus
|
|
14
15
|
from background_tasks.contracts.outbox import OutboxItem, OutboxListResponse
|
|
@@ -66,7 +67,7 @@ class OutboxService:
|
|
|
66
67
|
self.db,
|
|
67
68
|
select(OutboxMessage)
|
|
68
69
|
.where(*where)
|
|
69
|
-
.order_by(OutboxMessage.created_at.desc(), OutboxMessage.id),
|
|
70
|
+
.order_by(col(OutboxMessage.created_at).desc(), col(OutboxMessage.id)),
|
|
70
71
|
PageParams(page=page, per_page=per_page),
|
|
71
72
|
)
|
|
72
73
|
return OutboxListResponse(
|
|
@@ -93,7 +94,7 @@ class OutboxService:
|
|
|
93
94
|
row = (
|
|
94
95
|
await self.db.execute(
|
|
95
96
|
select(OutboxMessage)
|
|
96
|
-
.where(OutboxMessage.id == message_id, *self.scope.conditions())
|
|
97
|
+
.where(col(OutboxMessage.id) == message_id, *self.scope.conditions())
|
|
97
98
|
.execution_options(populate_existing=True)
|
|
98
99
|
)
|
|
99
100
|
).scalar_one_or_none()
|
|
@@ -114,8 +115,8 @@ class OutboxService:
|
|
|
114
115
|
result = await self.db.execute(
|
|
115
116
|
update(OutboxMessage)
|
|
116
117
|
.where(
|
|
117
|
-
OutboxMessage.id == message_id,
|
|
118
|
-
OutboxMessage.status == OutboxStatus.DEAD,
|
|
118
|
+
col(OutboxMessage.id) == message_id,
|
|
119
|
+
col(OutboxMessage.status) == OutboxStatus.DEAD,
|
|
119
120
|
*self.scope.conditions(),
|
|
120
121
|
)
|
|
121
122
|
.values(
|
|
@@ -141,7 +142,7 @@ class OutboxService:
|
|
|
141
142
|
result = await self.db.execute(
|
|
142
143
|
delete(OutboxMessage)
|
|
143
144
|
.where(
|
|
144
|
-
OutboxMessage.id == message_id,
|
|
145
|
+
col(OutboxMessage.id) == message_id,
|
|
145
146
|
OutboxMessage.status.in_(DISCARDABLE),
|
|
146
147
|
*self.scope.conditions(),
|
|
147
148
|
)
|
|
@@ -30,6 +30,7 @@ from simple_module_core.events import EventBus
|
|
|
30
30
|
from simple_module_db import init_db
|
|
31
31
|
from simple_module_db.listeners import attach_session_listeners
|
|
32
32
|
from simple_module_db.query_filter import bind_engine_policy
|
|
33
|
+
from simple_module_db.tenant_cleanup import register_tenant_cleanup
|
|
33
34
|
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker
|
|
34
35
|
|
|
35
36
|
from background_tasks import sync_db
|
|
@@ -118,6 +119,7 @@ def build_worker_bus() -> EventBus:
|
|
|
118
119
|
from simple_module_core.discovery import discover_modules, topological_sort
|
|
119
120
|
|
|
120
121
|
bus = EventBus()
|
|
122
|
+
register_tenant_cleanup(bus)
|
|
121
123
|
bus.durable_transport = WorkerOutboxTransport()
|
|
122
124
|
# Lenient on purpose: the worker delivers whatever is installed, and a
|
|
123
125
|
# module set the web process refuses is reported there, not here.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Head, Link, router } from '@inertiajs/react';
|
|
2
2
|
import { keys, useT } from '@simple-module-py/i18n';
|
|
3
|
+
import { ConfirmActionDialog } from '@simple-module-py/ui/components/ConfirmActionDialog';
|
|
3
4
|
import { PageShell } from '@simple-module-py/ui/components/PageShell';
|
|
4
5
|
import { Button } from '@simple-module-py/ui/components/ui/button';
|
|
5
6
|
import { usePermissions } from '@simple-module-py/ui/hooks/use-permissions';
|
|
6
7
|
import { AdminLayout } from '@simple-module-py/ui/layouts/AdminLayout';
|
|
7
8
|
import { cn } from '@simple-module-py/ui/lib/utils';
|
|
8
|
-
import { ArrowLeft } from 'lucide-react';
|
|
9
|
+
import { ArrowLeft, Trash2 } from 'lucide-react';
|
|
9
10
|
import { useState } from 'react';
|
|
10
11
|
import { OutboxTable } from './components/OutboxTable';
|
|
11
12
|
import { VIEW_BASE } from './constants';
|
|
@@ -31,6 +32,7 @@ function Outbox({ messages, counts, pagination, filters }: Props) {
|
|
|
31
32
|
const { t } = useT();
|
|
32
33
|
const { can } = usePermissions();
|
|
33
34
|
const canManage = can('background_tasks.manage');
|
|
35
|
+
const [pendingDiscard, setPendingDiscard] = useState<OutboxMessage | null>(null);
|
|
34
36
|
const [busyId, setBusyId] = useState<string | null>(null);
|
|
35
37
|
|
|
36
38
|
function visit(status: string, page: number) {
|
|
@@ -51,13 +53,6 @@ function Outbox({ messages, counts, pagination, filters }: Props) {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function discard(message: OutboxMessage) {
|
|
55
|
-
const prompt = t(keys.background_tasks.outbox.discard_confirm, {
|
|
56
|
-
handler: message.handler_name,
|
|
57
|
-
});
|
|
58
|
-
if (window.confirm(prompt)) void act(message, discardOutboxMessage);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
56
|
const segments = [
|
|
62
57
|
{ value: '', label: t(keys.background_tasks.outbox.filter_all), count: null },
|
|
63
58
|
...OPEN_OUTBOX_STATUSES.map((s) => ({
|
|
@@ -105,13 +100,30 @@ function Outbox({ messages, counts, pagination, filters }: Props) {
|
|
|
105
100
|
canManage={canManage}
|
|
106
101
|
busyId={busyId}
|
|
107
102
|
onRetry={(m) => void act(m, retryOutboxMessage)}
|
|
108
|
-
onDiscard={
|
|
103
|
+
onDiscard={setPendingDiscard}
|
|
109
104
|
page={pagination.page}
|
|
110
105
|
perPage={pagination.per_page}
|
|
111
106
|
total={pagination.total}
|
|
112
107
|
onPageChange={(page) => visit(filters.status, page)}
|
|
113
108
|
/>
|
|
114
109
|
</PageShell>
|
|
110
|
+
<ConfirmActionDialog
|
|
111
|
+
open={pendingDiscard !== null}
|
|
112
|
+
onOpenChange={(open) => !open && setPendingDiscard(null)}
|
|
113
|
+
icon={Trash2}
|
|
114
|
+
title={t(keys.background_tasks.outbox.discard)}
|
|
115
|
+
description={t(keys.background_tasks.outbox.discard_confirm, {
|
|
116
|
+
handler: pendingDiscard?.handler_name ?? '',
|
|
117
|
+
})}
|
|
118
|
+
confirmLabel={t(keys.background_tasks.outbox.discard)}
|
|
119
|
+
cancelLabel={t(keys.background_tasks.retry_dialog.cancel)}
|
|
120
|
+
busy={busyId !== null}
|
|
121
|
+
onConfirm={() => {
|
|
122
|
+
if (pendingDiscard) {
|
|
123
|
+
void act(pendingDiscard, discardOutboxMessage).finally(() => setPendingDiscard(null));
|
|
124
|
+
}
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
115
127
|
</>
|
|
116
128
|
);
|
|
117
129
|
}
|
|
@@ -16,6 +16,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
16
16
|
from simple_module_core.events import EventBus
|
|
17
17
|
from simple_module_db import RequestSession, all_tenants, tenant_context
|
|
18
18
|
from sqlalchemy import Select, func, select, update
|
|
19
|
+
from sqlmodel import col
|
|
19
20
|
|
|
20
21
|
from background_tasks._signal_support import now_utc
|
|
21
22
|
from background_tasks.after_commit import after_commit
|
|
@@ -271,8 +272,8 @@ def _fail_unsent(task_id: str, exc: Exception) -> None:
|
|
|
271
272
|
session.execute(
|
|
272
273
|
update(TaskExecution)
|
|
273
274
|
.where(
|
|
274
|
-
TaskExecution.celery_task_id == task_id,
|
|
275
|
-
TaskExecution.status == TaskStatus.PENDING,
|
|
275
|
+
col(TaskExecution.celery_task_id) == task_id,
|
|
276
|
+
col(TaskExecution.status) == TaskStatus.PENDING,
|
|
276
277
|
)
|
|
277
278
|
.values(
|
|
278
279
|
status=TaskStatus.FAILED,
|
|
@@ -24,6 +24,7 @@ import time
|
|
|
24
24
|
|
|
25
25
|
from celery import current_task
|
|
26
26
|
from sqlalchemy import update
|
|
27
|
+
from sqlmodel import col
|
|
27
28
|
|
|
28
29
|
from background_tasks._signal_support import now_utc
|
|
29
30
|
from background_tasks.constants import (
|
|
@@ -122,11 +123,11 @@ def heartbeat(
|
|
|
122
123
|
session.execute(
|
|
123
124
|
update(TaskExecution)
|
|
124
125
|
.where(
|
|
125
|
-
TaskExecution.celery_task_id == task_id,
|
|
126
|
+
col(TaskExecution.celery_task_id) == task_id,
|
|
126
127
|
# Only a row still ``running``: a heartbeat must not revive
|
|
127
128
|
# a row the sweep already declared stuck (it may have been
|
|
128
129
|
# retried since) or rewrite a terminal one.
|
|
129
|
-
TaskExecution.status == TaskStatus.RUNNING,
|
|
130
|
+
col(TaskExecution.status) == TaskStatus.RUNNING,
|
|
130
131
|
)
|
|
131
132
|
.values(heartbeat_at=now_utc(), **pending)
|
|
132
133
|
)
|
|
@@ -21,6 +21,7 @@ from typing import Any
|
|
|
21
21
|
|
|
22
22
|
from celery import Task, shared_task
|
|
23
23
|
from sqlalchemy import ColumnElement, and_, delete, or_, update
|
|
24
|
+
from sqlmodel import col
|
|
24
25
|
|
|
25
26
|
from background_tasks.constants import (
|
|
26
27
|
DEMO_ECHO_TASK,
|
|
@@ -119,7 +120,7 @@ def sweep_stuck_tasks(self: Task) -> int:
|
|
|
119
120
|
stmt = (
|
|
120
121
|
update(TaskExecution)
|
|
121
122
|
.where(
|
|
122
|
-
TaskExecution.status == TaskStatus.RUNNING,
|
|
123
|
+
col(TaskExecution.status) == TaskStatus.RUNNING,
|
|
123
124
|
_stale(now, stuck_after, overrides),
|
|
124
125
|
)
|
|
125
126
|
.values(status=TaskStatus.STUCK, finished_at=now)
|
|
@@ -149,7 +150,7 @@ def purge_old_executions() -> int:
|
|
|
149
150
|
count = result.rowcount or 0
|
|
150
151
|
delivered = session.execute(
|
|
151
152
|
delete(OutboxMessage)
|
|
152
|
-
.where(OutboxMessage.status == OutboxStatus.DELIVERED)
|
|
153
|
+
.where(col(OutboxMessage.status) == OutboxStatus.DELIVERED)
|
|
153
154
|
.where(OutboxMessage.delivered_at < cutoff)
|
|
154
155
|
)
|
|
155
156
|
count += delivered.rowcount or 0
|
{simple_module_background_tasks-0.0.38 → simple_module_background_tasks-0.0.39}/pyproject.toml
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "simple_module_background_tasks"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.39"
|
|
4
4
|
description = "Celery + Redis task queue with admin UI for monitoring and retrying failed/stuck tasks"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -21,12 +21,12 @@ classifiers = [
|
|
|
21
21
|
"Typing :: Typed",
|
|
22
22
|
]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"simple_module_core==0.0.
|
|
25
|
-
"simple_module_db==0.0.
|
|
26
|
-
"simple_module_hosting==0.0.
|
|
27
|
-
"simple_module_settings==0.0.
|
|
28
|
-
"celery[redis]>=5.4",
|
|
29
|
-
"redis>=5",
|
|
24
|
+
"simple_module_core==0.0.39",
|
|
25
|
+
"simple_module_db==0.0.39",
|
|
26
|
+
"simple_module_hosting==0.0.39",
|
|
27
|
+
"simple_module_settings==0.0.39",
|
|
28
|
+
"celery[redis]>=5.4,<6",
|
|
29
|
+
"redis>=5,<7",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.entry-points.simple_module]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '@testing-library/jest-dom/vitest';
|
|
2
2
|
import { configureI18n, keys } from '@simple-module-py/i18n';
|
|
3
|
-
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
|
4
4
|
import type { ComponentProps } from 'react';
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
|
|
@@ -134,21 +134,27 @@ describe('background_tasks Outbox page', () => {
|
|
|
134
134
|
|
|
135
135
|
it('asks before discarding and calls nothing when the operator declines', () => {
|
|
136
136
|
const fetchMock = stubFetch(204);
|
|
137
|
-
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false);
|
|
138
137
|
render(<Outbox {...(state.props as unknown as ComponentProps<typeof Outbox>)} />);
|
|
139
138
|
fireEvent.click(screen.getByRole('button', { name: ob.discard }));
|
|
140
139
|
|
|
141
|
-
|
|
140
|
+
const dialog = screen.getByRole('alertdialog');
|
|
141
|
+
fireEvent.click(
|
|
142
|
+
within(dialog).getByRole('button', { name: keys.background_tasks.retry_dialog.cancel }),
|
|
143
|
+
);
|
|
144
|
+
expect(screen.queryByRole('alertdialog')).toBeNull();
|
|
142
145
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
143
146
|
expect(reload).not.toHaveBeenCalled();
|
|
144
147
|
});
|
|
145
148
|
|
|
146
149
|
it('discards with a DELETE once confirmed', async () => {
|
|
147
150
|
const fetchMock = stubFetch(204);
|
|
148
|
-
vi.spyOn(window, 'confirm').mockReturnValue(true);
|
|
149
151
|
render(<Outbox {...(state.props as unknown as ComponentProps<typeof Outbox>)} />);
|
|
150
152
|
fireEvent.click(screen.getByRole('button', { name: ob.discard }));
|
|
151
153
|
|
|
154
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
155
|
+
fireEvent.click(
|
|
156
|
+
within(screen.getByRole('alertdialog')).getByRole('button', { name: ob.discard }),
|
|
157
|
+
);
|
|
152
158
|
await waitFor(() => expect(toast.success).toHaveBeenCalledWith(ob.discarded));
|
|
153
159
|
expect(requestsOf(fetchMock)).toContainEqual({
|
|
154
160
|
url: '/api/background_tasks/admin/outbox/m1',
|
|
@@ -159,9 +165,11 @@ describe('background_tasks Outbox page', () => {
|
|
|
159
165
|
|
|
160
166
|
it('toasts a discard failure', async () => {
|
|
161
167
|
stubFetch(500);
|
|
162
|
-
vi.spyOn(window, 'confirm').mockReturnValue(true);
|
|
163
168
|
render(<Outbox {...(state.props as unknown as ComponentProps<typeof Outbox>)} />);
|
|
164
169
|
fireEvent.click(screen.getByRole('button', { name: ob.discard }));
|
|
170
|
+
fireEvent.click(
|
|
171
|
+
within(screen.getByRole('alertdialog')).getByRole('button', { name: ob.discard }),
|
|
172
|
+
);
|
|
165
173
|
await waitFor(() => expect(toast.error).toHaveBeenCalledWith(ob.discard_failed));
|
|
166
174
|
});
|
|
167
175
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|