bazaar-compute-node 0.1.28__tar.gz → 0.1.29__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.
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/PKG-INFO +1 -1
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/pyproject.toml +1 -1
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/pyproject.toml.orig +1 -1
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/agent.py +1 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/application.py +56 -2
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/system_service.py +57 -161
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/transport.py +7 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/windows_pipe.py +14 -2
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/channel.py +20 -1
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/database.py +4 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/executor.py +41 -5
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/storage.py +1 -1
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/channel.py +40 -1
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/core/lifecycle.py +100 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/reminder.py +29 -2
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/services.py +24 -12
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/session.py +88 -3
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/timerwheel.py +9 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/launchd.plist +37 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/launchd.sh +11 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/systemd.service +19 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/windows.ps1 +98 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/windows.vbs +11 -0
- bazaar_compute_node-0.1.29/src/bazaar_compute_node/resources/system_service/windows.xml +39 -0
- bazaar_compute_node-0.1.28/src/bazaar_compute_node/core/lifecycle.py +0 -48
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/LICENSE +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/README.md +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/agent_management.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/attachments.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/command.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/config.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/registry.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/resource_dispatch.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/wrapper.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/bcc.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/cli.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/codec.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v19_reminder_system_messages.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v20_remove_freshness_persistence.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v21_remove_handoffs.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/migrations/v22_readable_targets.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/base.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/wecom/channel.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/approval.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/audit.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/channel.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/client.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/command.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/concurrency.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/correlation.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/inbox.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/instruction.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/models/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/models/entities.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/models/reminder.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/models/reminder_owner.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/models/states.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/observability.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/command.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/error_feedback.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/reminder_command.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/orchestration/turn.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/outcomes.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/paths.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/reminder.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/runtime.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/core/storage.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/i18n/__init__.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/i18n/catalog.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/rendering.py +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/resources/developer_instructions.md +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/resources/locales/en.toml +0 -0
- {bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/resources/locales/zh-CN.toml +0 -0
{bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/agent.py
RENAMED
|
@@ -277,6 +277,7 @@ class AgentApplication:
|
|
|
277
277
|
"channel": self.channel.name,
|
|
278
278
|
"runtime": self.runtime.name,
|
|
279
279
|
"channel_health": dict(self.channel.health),
|
|
280
|
+
"orchestrator_health": self.orchestrator.health,
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
async def _cleanup_partial_start(self) -> None:
|
{bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/application.py
RENAMED
|
@@ -10,7 +10,7 @@ from dataclasses import dataclass
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
from ..core.concurrency import SessionLockRegistry
|
|
13
|
-
from ..core.lifecycle import TimeoutBudget
|
|
13
|
+
from ..core.lifecycle import ITaskFailureSource, TimeoutBudget
|
|
14
14
|
from ..core.models import InboundAttachment, Message
|
|
15
15
|
from ..core.observability import IAudit
|
|
16
16
|
from ..core.orchestration import ReminderScheduler
|
|
@@ -245,7 +245,61 @@ class NodeApplication:
|
|
|
245
245
|
loop.add_signal_handler(signum, self._stopped.set)
|
|
246
246
|
except NotImplementedError, RuntimeError:
|
|
247
247
|
pass
|
|
248
|
-
|
|
248
|
+
stop_task = asyncio.create_task(self._stopped.wait(), name="bcn-stop-signal")
|
|
249
|
+
sources: list[tuple[str, ITaskFailureSource]] = [
|
|
250
|
+
("timer_wheel", self.timer_wheel),
|
|
251
|
+
("reminder_scheduler", self.reminder_scheduler),
|
|
252
|
+
("command_server", self.command_server),
|
|
253
|
+
]
|
|
254
|
+
if isinstance(self.storage, ITaskFailureSource):
|
|
255
|
+
sources.append(("storage", self.storage))
|
|
256
|
+
failure_tasks = {
|
|
257
|
+
asyncio.create_task(
|
|
258
|
+
source.wait_failure(),
|
|
259
|
+
name=f"bcn-critical-{name}",
|
|
260
|
+
): name
|
|
261
|
+
for name, source in sources
|
|
262
|
+
}
|
|
263
|
+
tasks = {stop_task, *failure_tasks}
|
|
264
|
+
try:
|
|
265
|
+
done, _ = await asyncio.wait(
|
|
266
|
+
tasks,
|
|
267
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
268
|
+
)
|
|
269
|
+
failure: tuple[str, BaseException] | None = None
|
|
270
|
+
for task in done:
|
|
271
|
+
if task is stop_task:
|
|
272
|
+
continue
|
|
273
|
+
try:
|
|
274
|
+
task.result()
|
|
275
|
+
except asyncio.CancelledError:
|
|
276
|
+
caught: BaseException = RuntimeError(
|
|
277
|
+
f"{failure_tasks[task]} failure monitor stopped unexpectedly"
|
|
278
|
+
)
|
|
279
|
+
except BaseException as error: # noqa: BLE001
|
|
280
|
+
caught = error
|
|
281
|
+
else:
|
|
282
|
+
caught = RuntimeError(
|
|
283
|
+
f"{failure_tasks[task]} failure monitor returned unexpectedly"
|
|
284
|
+
)
|
|
285
|
+
if failure is None:
|
|
286
|
+
failure = failure_tasks[task], caught
|
|
287
|
+
if failure is not None:
|
|
288
|
+
component, error = failure
|
|
289
|
+
self._ready = False
|
|
290
|
+
self._accepting = False
|
|
291
|
+
self._log(
|
|
292
|
+
"bcn.critical.failed",
|
|
293
|
+
component=component,
|
|
294
|
+
error_type=type(error).__name__,
|
|
295
|
+
error=_safe_error(error),
|
|
296
|
+
)
|
|
297
|
+
raise error
|
|
298
|
+
finally:
|
|
299
|
+
for task in tasks:
|
|
300
|
+
if not task.done():
|
|
301
|
+
task.cancel()
|
|
302
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
249
303
|
|
|
250
304
|
async def _publish_inbox_wake(
|
|
251
305
|
self,
|
|
@@ -6,7 +6,6 @@ import getpass
|
|
|
6
6
|
import locale
|
|
7
7
|
import os
|
|
8
8
|
import platform
|
|
9
|
-
import plistlib
|
|
10
9
|
import shlex
|
|
11
10
|
import shutil
|
|
12
11
|
import subprocess
|
|
@@ -15,10 +14,10 @@ import tempfile
|
|
|
15
14
|
import time
|
|
16
15
|
from dataclasses import dataclass
|
|
17
16
|
from pathlib import Path
|
|
18
|
-
from xml.etree import ElementTree
|
|
19
17
|
|
|
20
18
|
from ..core.paths import resolve_data_dir
|
|
21
19
|
from ..i18n import Translator, create_translator
|
|
20
|
+
from ..rendering import TextTemplate
|
|
22
21
|
from .config import ConfigurationError, load_control_configuration, resolve_config_path
|
|
23
22
|
from .transport import LocalCommandClient, local_endpoint_for_path
|
|
24
23
|
|
|
@@ -26,10 +25,16 @@ SYSTEMD_UNIT_NAME = "bcn.service"
|
|
|
26
25
|
LAUNCHD_LABEL = "io.github.yuchanns.bazaar-compute-node"
|
|
27
26
|
WINDOWS_TASK_NAME = r"\BazaarComputeNode"
|
|
28
27
|
MANAGED_MARKER = "Managed by bazaar-compute-node."
|
|
29
|
-
TASK_NAMESPACE = "http://schemas.microsoft.com/windows/2004/02/mit/task"
|
|
30
28
|
WINDOWS_STOP_ATTEMPTS = 100
|
|
31
29
|
WINDOWS_STOP_INTERVAL = 0.05
|
|
32
30
|
|
|
31
|
+
_SYSTEMD_UNIT_TEMPLATE = TextTemplate.from_resource("system_service/systemd.service")
|
|
32
|
+
_LAUNCHD_PLIST_TEMPLATE = TextTemplate.from_resource("system_service/launchd.plist")
|
|
33
|
+
_LAUNCHD_WRAPPER_TEMPLATE = TextTemplate.from_resource("system_service/launchd.sh")
|
|
34
|
+
_WINDOWS_TASK_TEMPLATE = TextTemplate.from_resource("system_service/windows.xml")
|
|
35
|
+
_WINDOWS_LAUNCHER_TEMPLATE = TextTemplate.from_resource("system_service/windows.vbs")
|
|
36
|
+
_WINDOWS_WRAPPER_TEMPLATE = TextTemplate.from_resource("system_service/windows.ps1")
|
|
37
|
+
|
|
33
38
|
|
|
34
39
|
@dataclass(frozen=True, slots=True)
|
|
35
40
|
class SystemServiceContext:
|
|
@@ -166,109 +171,53 @@ def _powershell_literal(path: Path | str | None) -> str:
|
|
|
166
171
|
|
|
167
172
|
|
|
168
173
|
def _render_systemd_unit(context: SystemServiceContext) -> str:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
]
|
|
180
|
-
if context.env_file is not None:
|
|
181
|
-
lines.append(f"EnvironmentFile=-{_systemd_quote(context.env_file)}")
|
|
182
|
-
lines.extend(
|
|
183
|
-
(
|
|
184
|
-
"Restart=on-failure",
|
|
185
|
-
"RestartSec=5",
|
|
186
|
-
"KillSignal=SIGTERM",
|
|
187
|
-
"TimeoutStopSec=15",
|
|
188
|
-
"UMask=0077",
|
|
189
|
-
"StandardOutput=journal",
|
|
190
|
-
"StandardError=journal",
|
|
191
|
-
"",
|
|
192
|
-
"[Install]",
|
|
193
|
-
"WantedBy=default.target",
|
|
194
|
-
"",
|
|
195
|
-
)
|
|
174
|
+
return _SYSTEMD_UNIT_TEMPLATE.render(
|
|
175
|
+
{
|
|
176
|
+
"managed_marker": MANAGED_MARKER,
|
|
177
|
+
"executable": _systemd_quote(_require_executable(context)),
|
|
178
|
+
"config_path": _systemd_quote(context.config_path),
|
|
179
|
+
"data_dir": _systemd_quote(context.data_dir),
|
|
180
|
+
"environment_file": (
|
|
181
|
+
"" if context.env_file is None else _systemd_quote(context.env_file)
|
|
182
|
+
),
|
|
183
|
+
}
|
|
196
184
|
)
|
|
197
|
-
return "\n".join(lines)
|
|
198
185
|
|
|
199
186
|
|
|
200
187
|
def _render_launchd_wrapper() -> str:
|
|
201
|
-
return
|
|
202
|
-
# {MANAGED_MARKER}
|
|
203
|
-
set -eu
|
|
204
|
-
|
|
205
|
-
if [ -n "${{BCN_ENV_FILE:-}}" ] && [ -f "$BCN_ENV_FILE" ]; then
|
|
206
|
-
set -a
|
|
207
|
-
. "$BCN_ENV_FILE"
|
|
208
|
-
set +a
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
|
-
exec "$BCN_EXECUTABLE" run --config "$BCN_CONFIG"
|
|
212
|
-
"""
|
|
188
|
+
return _LAUNCHD_WRAPPER_TEMPLATE.render({"managed_marker": MANAGED_MARKER})
|
|
213
189
|
|
|
214
190
|
|
|
215
191
|
def _render_launchd_plist(
|
|
216
192
|
context: SystemServiceContext,
|
|
217
193
|
wrapper_path: Path,
|
|
218
194
|
) -> bytes:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
"
|
|
226
|
-
"
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"StandardOutPath": str(context.log_path),
|
|
234
|
-
"StandardErrorPath": str(context.log_path),
|
|
235
|
-
}
|
|
236
|
-
return plistlib.dumps(payload, fmt=plistlib.FMT_XML, sort_keys=False)
|
|
195
|
+
return _LAUNCHD_PLIST_TEMPLATE.render(
|
|
196
|
+
{
|
|
197
|
+
"label": LAUNCHD_LABEL,
|
|
198
|
+
"managed_marker": MANAGED_MARKER,
|
|
199
|
+
"wrapper_path": str(wrapper_path),
|
|
200
|
+
"data_dir": str(context.data_dir),
|
|
201
|
+
"config_path": str(context.config_path),
|
|
202
|
+
"environment_file": (
|
|
203
|
+
"" if context.env_file is None else str(context.env_file)
|
|
204
|
+
),
|
|
205
|
+
"executable": str(_require_executable(context)),
|
|
206
|
+
"log_path": str(context.log_path),
|
|
207
|
+
}
|
|
208
|
+
).encode("utf-8")
|
|
237
209
|
|
|
238
210
|
|
|
239
211
|
def _render_windows_wrapper(context: SystemServiceContext) -> str:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
$environmentScript = {environment_script}
|
|
250
|
-
$logPath = {log_path}
|
|
251
|
-
|
|
252
|
-
if ($environmentScript -and (Test-Path -LiteralPath $environmentScript)) {{
|
|
253
|
-
try {{
|
|
254
|
-
. $environmentScript
|
|
255
|
-
}} catch {{
|
|
256
|
-
$_ | Out-String | Add-Content -LiteralPath $logPath -Encoding utf8
|
|
257
|
-
exit 1
|
|
258
|
-
}}
|
|
259
|
-
}}
|
|
260
|
-
|
|
261
|
-
# Native stderr is routed to the service log and must not terminate the wrapper.
|
|
262
|
-
$ErrorActionPreference = 'Continue'
|
|
263
|
-
try {{
|
|
264
|
-
& $executable run --config $configPath *>> $logPath
|
|
265
|
-
$exitCode = $LASTEXITCODE
|
|
266
|
-
}} catch {{
|
|
267
|
-
$_ | Out-String | Add-Content -LiteralPath $logPath -Encoding utf8
|
|
268
|
-
exit 1
|
|
269
|
-
}}
|
|
270
|
-
exit $exitCode
|
|
271
|
-
"""
|
|
212
|
+
return _WINDOWS_WRAPPER_TEMPLATE.render(
|
|
213
|
+
{
|
|
214
|
+
"managed_marker": MANAGED_MARKER,
|
|
215
|
+
"executable": _powershell_literal(_require_executable(context)),
|
|
216
|
+
"config_path": _powershell_literal(context.config_path),
|
|
217
|
+
"environment_script": _powershell_literal(context.env_file),
|
|
218
|
+
"log_path": _powershell_literal(context.log_path),
|
|
219
|
+
}
|
|
220
|
+
)
|
|
272
221
|
|
|
273
222
|
|
|
274
223
|
def _vbs_literal(value: Path | str) -> str:
|
|
@@ -280,80 +229,27 @@ def _render_windows_launcher(wrapper_path: Path) -> str:
|
|
|
280
229
|
"powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden "
|
|
281
230
|
f'-ExecutionPolicy Bypass -File "{wrapper_path}"'
|
|
282
231
|
)
|
|
283
|
-
return
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Set shell = CreateObject("WScript.Shell")
|
|
291
|
-
command = {command}
|
|
292
|
-
exitCode = shell.Run(command, 0, True)
|
|
293
|
-
WScript.Quit exitCode
|
|
294
|
-
"""
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
def _add_task_element(
|
|
298
|
-
parent: ElementTree.Element,
|
|
299
|
-
name: str,
|
|
300
|
-
text: str | None = None,
|
|
301
|
-
) -> ElementTree.Element:
|
|
302
|
-
element = ElementTree.SubElement(parent, f"{{{TASK_NAMESPACE}}}{name}")
|
|
303
|
-
if text is not None:
|
|
304
|
-
element.text = text
|
|
305
|
-
return element
|
|
232
|
+
return _WINDOWS_LAUNCHER_TEMPLATE.render(
|
|
233
|
+
{
|
|
234
|
+
"managed_marker": MANAGED_MARKER,
|
|
235
|
+
"command": command,
|
|
236
|
+
}
|
|
237
|
+
)
|
|
306
238
|
|
|
307
239
|
|
|
308
240
|
def _render_windows_task(
|
|
309
241
|
context: SystemServiceContext,
|
|
310
242
|
launcher_path: Path,
|
|
311
243
|
) -> bytes:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
logon_trigger = _add_task_element(triggers, "LogonTrigger")
|
|
322
|
-
_add_task_element(logon_trigger, "Enabled", "true")
|
|
323
|
-
_add_task_element(logon_trigger, "UserId", context.user)
|
|
324
|
-
principals = _add_task_element(task, "Principals")
|
|
325
|
-
principal = ElementTree.SubElement(
|
|
326
|
-
principals,
|
|
327
|
-
f"{{{TASK_NAMESPACE}}}Principal",
|
|
328
|
-
{"id": "Author"},
|
|
329
|
-
)
|
|
330
|
-
_add_task_element(principal, "UserId", context.user)
|
|
331
|
-
_add_task_element(principal, "LogonType", "InteractiveToken")
|
|
332
|
-
_add_task_element(principal, "RunLevel", "LeastPrivilege")
|
|
333
|
-
settings = _add_task_element(task, "Settings")
|
|
334
|
-
_add_task_element(settings, "MultipleInstancesPolicy", "IgnoreNew")
|
|
335
|
-
_add_task_element(settings, "DisallowStartIfOnBatteries", "false")
|
|
336
|
-
_add_task_element(settings, "StopIfGoingOnBatteries", "false")
|
|
337
|
-
_add_task_element(settings, "AllowHardTerminate", "true")
|
|
338
|
-
_add_task_element(settings, "StartWhenAvailable", "true")
|
|
339
|
-
_add_task_element(settings, "ExecutionTimeLimit", "PT0S")
|
|
340
|
-
restart = _add_task_element(settings, "RestartOnFailure")
|
|
341
|
-
_add_task_element(restart, "Interval", "PT5M")
|
|
342
|
-
_add_task_element(restart, "Count", "3")
|
|
343
|
-
actions = ElementTree.SubElement(
|
|
344
|
-
task,
|
|
345
|
-
f"{{{TASK_NAMESPACE}}}Actions",
|
|
346
|
-
{"Context": "Author"},
|
|
347
|
-
)
|
|
348
|
-
action = _add_task_element(actions, "Exec")
|
|
349
|
-
_add_task_element(action, "Command", "wscript.exe")
|
|
350
|
-
_add_task_element(
|
|
351
|
-
action,
|
|
352
|
-
"Arguments",
|
|
353
|
-
f'//B //Nologo "{launcher_path}"',
|
|
354
|
-
)
|
|
355
|
-
_add_task_element(action, "WorkingDirectory", str(context.data_dir))
|
|
356
|
-
return ElementTree.tostring(task, encoding="utf-16", xml_declaration=True)
|
|
244
|
+
rendered = _WINDOWS_TASK_TEMPLATE.render(
|
|
245
|
+
{
|
|
246
|
+
"user": context.user,
|
|
247
|
+
"managed_marker": MANAGED_MARKER,
|
|
248
|
+
"arguments": f'//B //Nologo "{launcher_path}"',
|
|
249
|
+
"data_dir": str(context.data_dir),
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
return rendered.encode("utf-16")
|
|
357
253
|
|
|
358
254
|
|
|
359
255
|
def _write_managed_file(path: Path, content: str | bytes, *, mode: int) -> None:
|
{bazaar_compute_node-0.1.28 → bazaar_compute_node-0.1.29}/src/bazaar_compute_node/app/transport.py
RENAMED
|
@@ -134,6 +134,13 @@ class LocalCommandServer:
|
|
|
134
134
|
if (path_stat.st_dev, path_stat.st_ino) == identity:
|
|
135
135
|
await asyncio.to_thread(path.unlink)
|
|
136
136
|
|
|
137
|
+
async def wait_failure(self) -> None:
|
|
138
|
+
windows_server = self._windows_server
|
|
139
|
+
if windows_server is not None:
|
|
140
|
+
await windows_server.wait_failure()
|
|
141
|
+
return
|
|
142
|
+
await asyncio.Future()
|
|
143
|
+
|
|
137
144
|
async def _handle_client(
|
|
138
145
|
self,
|
|
139
146
|
reader: asyncio.StreamReader,
|
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
from typing import Any
|
|
13
13
|
from urllib.parse import urlsplit
|
|
14
14
|
|
|
15
|
+
from bazaar_compute_node.core.lifecycle import TaskFailureSignal
|
|
15
16
|
from bazaar_compute_node.core.paths import resolve_data_dir
|
|
16
17
|
|
|
17
18
|
RequestHandler = Callable[[Mapping[str, object]], Awaitable[Mapping[str, object]]]
|
|
@@ -301,6 +302,7 @@ class WindowsNamedPipeServer:
|
|
|
301
302
|
self._stop = threading.Event()
|
|
302
303
|
self._ready = threading.Event()
|
|
303
304
|
self._startup_errors: list[BaseException] = []
|
|
305
|
+
self._failure = TaskFailureSignal()
|
|
304
306
|
self._thread: threading.Thread | None = None
|
|
305
307
|
self._workers: set[threading.Thread] = set()
|
|
306
308
|
self._workers_lock = threading.Lock()
|
|
@@ -317,6 +319,7 @@ class WindowsNamedPipeServer:
|
|
|
317
319
|
self._stop.clear()
|
|
318
320
|
self._ready.clear()
|
|
319
321
|
self._startup_errors.clear()
|
|
322
|
+
self._failure.reset()
|
|
320
323
|
self._thread = threading.Thread(
|
|
321
324
|
target=self._serve,
|
|
322
325
|
name="bcn-windows-named-pipe",
|
|
@@ -332,6 +335,7 @@ class WindowsNamedPipeServer:
|
|
|
332
335
|
raise TimeoutError("Windows named pipe server did not become ready")
|
|
333
336
|
|
|
334
337
|
async def stop(self) -> None:
|
|
338
|
+
self._failure.disable()
|
|
335
339
|
thread = self._thread
|
|
336
340
|
if thread is None:
|
|
337
341
|
_release_mutex(self._mutex)
|
|
@@ -352,8 +356,16 @@ class WindowsNamedPipeServer:
|
|
|
352
356
|
try:
|
|
353
357
|
self._serve_loop()
|
|
354
358
|
except Exception as error: # noqa: BLE001
|
|
355
|
-
self.
|
|
356
|
-
|
|
359
|
+
if not self._ready.is_set():
|
|
360
|
+
self._startup_errors.append(error)
|
|
361
|
+
self._ready.set()
|
|
362
|
+
return
|
|
363
|
+
loop = self._loop
|
|
364
|
+
if loop is not None:
|
|
365
|
+
loop.call_soon_threadsafe(self._failure.fail, error)
|
|
366
|
+
|
|
367
|
+
async def wait_failure(self) -> None:
|
|
368
|
+
await self._failure.wait()
|
|
357
369
|
|
|
358
370
|
def _serve_loop(self) -> None:
|
|
359
371
|
initialized = False
|
|
@@ -242,6 +242,7 @@ class LarkChannel(IChannel):
|
|
|
242
242
|
self._run_typing_dispatcher(),
|
|
243
243
|
name="bcn-lark-typing",
|
|
244
244
|
)
|
|
245
|
+
self._typing_runner.add_done_callback(self._typing_runner_done)
|
|
245
246
|
await transport.start(timeout=_remaining(deadline))
|
|
246
247
|
generation = transport.health.get("connection_generation", 0)
|
|
247
248
|
self._connection_generation = (
|
|
@@ -731,7 +732,9 @@ class LarkChannel(IChannel):
|
|
|
731
732
|
isinstance(raw_name, str)
|
|
732
733
|
and raw_name.strip()
|
|
733
734
|
and "]" not in raw_name
|
|
734
|
-
and not any(
|
|
735
|
+
and not any(
|
|
736
|
+
category(character) in {"Cc", "Zl", "Zp"} for character in raw_name
|
|
737
|
+
)
|
|
735
738
|
):
|
|
736
739
|
name = raw_name
|
|
737
740
|
else:
|
|
@@ -997,6 +1000,22 @@ class LarkChannel(IChannel):
|
|
|
997
1000
|
if state.terminal or self._typing_stopping:
|
|
998
1001
|
self._typing_states.pop(command.session_id, None)
|
|
999
1002
|
|
|
1003
|
+
def _typing_runner_done(self, task: asyncio.Task[None]) -> None:
|
|
1004
|
+
if self._typing_stopping:
|
|
1005
|
+
return
|
|
1006
|
+
error = (
|
|
1007
|
+
RuntimeError("Lark typing dispatcher was canceled unexpectedly")
|
|
1008
|
+
if task.cancelled()
|
|
1009
|
+
else task.exception()
|
|
1010
|
+
or RuntimeError("Lark typing dispatcher stopped unexpectedly")
|
|
1011
|
+
)
|
|
1012
|
+
self._typing_failures += 1
|
|
1013
|
+
_LOGGER.error(
|
|
1014
|
+
"Lark typing dispatcher failed: %s",
|
|
1015
|
+
error,
|
|
1016
|
+
exc_info=(type(error), error, error.__traceback__),
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1000
1019
|
async def _stop_typing(self, *, timeout: float) -> None:
|
|
1001
1020
|
self._typing_stopping = True
|
|
1002
1021
|
runner = self._typing_runner
|
|
@@ -198,6 +198,10 @@ class SqliteDatabase:
|
|
|
198
198
|
self._executor = None
|
|
199
199
|
self._schema_version = None
|
|
200
200
|
|
|
201
|
+
async def wait_failure(self) -> None:
|
|
202
|
+
executor = self._require_executor()
|
|
203
|
+
await executor.wait_failure()
|
|
204
|
+
|
|
201
205
|
async def fetchone(
|
|
202
206
|
self,
|
|
203
207
|
statement: str,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import logging
|
|
4
5
|
from collections import deque
|
|
5
6
|
from collections.abc import Awaitable, Callable, Iterable, Sequence
|
|
6
7
|
from contextlib import AbstractAsyncContextManager
|
|
@@ -11,6 +12,10 @@ from typing import TypeVar
|
|
|
11
12
|
|
|
12
13
|
import aiosqlite
|
|
13
14
|
|
|
15
|
+
from ...core.lifecycle import TaskFailureSignal
|
|
16
|
+
|
|
17
|
+
_LOGGER = logging.getLogger(__name__)
|
|
18
|
+
|
|
14
19
|
T = TypeVar("T")
|
|
15
20
|
|
|
16
21
|
|
|
@@ -222,6 +227,7 @@ class SqliteExecutor:
|
|
|
222
227
|
self._write_queue = asyncio.Queue[_WriteRequest[object] | None]()
|
|
223
228
|
self._writer_task: asyncio.Task[None] | None = None
|
|
224
229
|
self._reader_reaper_task: asyncio.Task[None] | None = None
|
|
230
|
+
self._failure = TaskFailureSignal()
|
|
225
231
|
self._reader_reaper_stop = asyncio.Event()
|
|
226
232
|
self._accepting = False
|
|
227
233
|
self._borrowed_readers = 0
|
|
@@ -238,6 +244,7 @@ class SqliteExecutor:
|
|
|
238
244
|
if self._writer_task is not None:
|
|
239
245
|
return
|
|
240
246
|
self._accepting = True
|
|
247
|
+
self._failure.reset()
|
|
241
248
|
self._reader_reaper_stop.clear()
|
|
242
249
|
self._writer_task = asyncio.create_task(
|
|
243
250
|
self._run_writer(),
|
|
@@ -247,10 +254,17 @@ class SqliteExecutor:
|
|
|
247
254
|
self._reap_idle_readers(),
|
|
248
255
|
name="bcn-sqlite-reader-reaper",
|
|
249
256
|
)
|
|
257
|
+
self._failure.observe(self._writer_task, component="SQLite writer")
|
|
258
|
+
self._reader_reaper_task.add_done_callback(self._reader_reaper_done)
|
|
250
259
|
|
|
251
260
|
async def stop(self) -> None:
|
|
252
261
|
if self._closed:
|
|
253
262
|
return
|
|
263
|
+
writer_task = self._writer_task
|
|
264
|
+
if writer_task is not None and writer_task.done():
|
|
265
|
+
await self.abort()
|
|
266
|
+
return
|
|
267
|
+
self._failure.disable()
|
|
254
268
|
self._accepting = False
|
|
255
269
|
await self._write_queue.join()
|
|
256
270
|
async with self._reader_condition:
|
|
@@ -274,6 +288,7 @@ class SqliteExecutor:
|
|
|
274
288
|
"""Interrupt active work and close all connections after a failed drain."""
|
|
275
289
|
if self._closed:
|
|
276
290
|
return
|
|
291
|
+
self._failure.disable()
|
|
277
292
|
self._accepting = False
|
|
278
293
|
async with self._reader_condition:
|
|
279
294
|
self._reader_condition.notify_all()
|
|
@@ -309,10 +324,7 @@ class SqliteExecutor:
|
|
|
309
324
|
writer_task = self._writer_task
|
|
310
325
|
if writer_task is not None:
|
|
311
326
|
writer_task.cancel()
|
|
312
|
-
|
|
313
|
-
await writer_task
|
|
314
|
-
except asyncio.CancelledError:
|
|
315
|
-
pass
|
|
327
|
+
await asyncio.gather(writer_task, return_exceptions=True)
|
|
316
328
|
self._writer_task = None
|
|
317
329
|
|
|
318
330
|
await self._stop_reader_reaper()
|
|
@@ -520,7 +532,15 @@ class SqliteExecutor:
|
|
|
520
532
|
to_close.append(reader.connection)
|
|
521
533
|
excess -= 1
|
|
522
534
|
for connection in to_close:
|
|
523
|
-
|
|
535
|
+
try:
|
|
536
|
+
await connection.close()
|
|
537
|
+
except asyncio.CancelledError:
|
|
538
|
+
raise
|
|
539
|
+
except Exception:
|
|
540
|
+
_LOGGER.exception("SQLite idle reader close failed")
|
|
541
|
+
|
|
542
|
+
async def wait_failure(self) -> None:
|
|
543
|
+
await self._failure.wait()
|
|
524
544
|
|
|
525
545
|
async def _stop_reader_reaper(self) -> None:
|
|
526
546
|
task = self._reader_reaper_task
|
|
@@ -530,6 +550,22 @@ class SqliteExecutor:
|
|
|
530
550
|
await task
|
|
531
551
|
self._reader_reaper_task = None
|
|
532
552
|
|
|
553
|
+
def _reader_reaper_done(self, task: asyncio.Task[None]) -> None:
|
|
554
|
+
if not self._accepting:
|
|
555
|
+
return
|
|
556
|
+
self._reader_reaper_task = None
|
|
557
|
+
error = (
|
|
558
|
+
RuntimeError("SQLite reader reaper was canceled unexpectedly")
|
|
559
|
+
if task.cancelled()
|
|
560
|
+
else task.exception()
|
|
561
|
+
or RuntimeError("SQLite reader reaper stopped unexpectedly")
|
|
562
|
+
)
|
|
563
|
+
_LOGGER.error(
|
|
564
|
+
"SQLite reader reaper failed: %s",
|
|
565
|
+
error,
|
|
566
|
+
exc_info=(type(error), error, error.__traceback__),
|
|
567
|
+
)
|
|
568
|
+
|
|
533
569
|
def _require_accepting(self) -> None:
|
|
534
570
|
if not self._accepting:
|
|
535
571
|
raise SqliteExecutorClosedError("SQLite executor is not accepting work")
|