unrealon 1.0.9__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unrealon/__init__.py +23 -21
- unrealon-1.1.0.dist-info/METADATA +164 -0
- unrealon-1.1.0.dist-info/RECORD +82 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info}/WHEEL +1 -1
- unrealon-1.1.0.dist-info/entry_points.txt +9 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info/licenses}/LICENSE +1 -1
- unrealon_bridge/__init__.py +114 -0
- unrealon_bridge/cli.py +316 -0
- unrealon_bridge/client/__init__.py +93 -0
- unrealon_bridge/client/base.py +78 -0
- unrealon_bridge/client/commands.py +89 -0
- unrealon_bridge/client/connection.py +90 -0
- unrealon_bridge/client/events.py +65 -0
- unrealon_bridge/client/health.py +38 -0
- unrealon_bridge/client/html_parser.py +146 -0
- unrealon_bridge/client/logging.py +139 -0
- unrealon_bridge/client/proxy.py +70 -0
- unrealon_bridge/client/scheduler.py +450 -0
- unrealon_bridge/client/session.py +70 -0
- unrealon_bridge/configs/__init__.py +14 -0
- unrealon_bridge/configs/bridge_config.py +212 -0
- unrealon_bridge/configs/bridge_config.yaml +39 -0
- unrealon_bridge/models/__init__.py +138 -0
- unrealon_bridge/models/base.py +28 -0
- unrealon_bridge/models/command.py +41 -0
- unrealon_bridge/models/events.py +40 -0
- unrealon_bridge/models/html_parser.py +79 -0
- unrealon_bridge/models/logging.py +55 -0
- unrealon_bridge/models/parser.py +63 -0
- unrealon_bridge/models/proxy.py +41 -0
- unrealon_bridge/models/requests.py +95 -0
- unrealon_bridge/models/responses.py +88 -0
- unrealon_bridge/models/scheduler.py +592 -0
- unrealon_bridge/models/session.py +28 -0
- unrealon_bridge/server/__init__.py +91 -0
- unrealon_bridge/server/base.py +171 -0
- unrealon_bridge/server/handlers/__init__.py +23 -0
- unrealon_bridge/server/handlers/command.py +110 -0
- unrealon_bridge/server/handlers/html_parser.py +139 -0
- unrealon_bridge/server/handlers/logging.py +95 -0
- unrealon_bridge/server/handlers/parser.py +95 -0
- unrealon_bridge/server/handlers/proxy.py +75 -0
- unrealon_bridge/server/handlers/scheduler.py +545 -0
- unrealon_bridge/server/handlers/session.py +66 -0
- unrealon_browser/__init__.py +61 -18
- unrealon_browser/{src/cli → cli}/browser_cli.py +6 -13
- unrealon_browser/{src/cli → cli}/cookies_cli.py +5 -1
- unrealon_browser/{src/core → core}/browser_manager.py +2 -2
- unrealon_browser/{src/managers → managers}/captcha.py +1 -1
- unrealon_browser/{src/managers → managers}/cookies.py +1 -1
- unrealon_browser/managers/logger_bridge.py +231 -0
- unrealon_browser/{src/managers → managers}/profile.py +1 -1
- unrealon_driver/__init__.py +73 -19
- unrealon_driver/browser/__init__.py +8 -0
- unrealon_driver/browser/config.py +74 -0
- unrealon_driver/browser/manager.py +416 -0
- unrealon_driver/exceptions.py +28 -0
- unrealon_driver/parser/__init__.py +55 -0
- unrealon_driver/parser/cli_manager.py +141 -0
- unrealon_driver/parser/daemon_manager.py +227 -0
- unrealon_driver/parser/managers/__init__.py +46 -0
- unrealon_driver/parser/managers/browser.py +51 -0
- unrealon_driver/parser/managers/config.py +281 -0
- unrealon_driver/parser/managers/error.py +412 -0
- unrealon_driver/parser/managers/html.py +732 -0
- unrealon_driver/parser/managers/logging.py +609 -0
- unrealon_driver/parser/managers/result.py +321 -0
- unrealon_driver/parser/parser_manager.py +628 -0
- unrealon/sdk_config.py +0 -88
- unrealon-1.0.9.dist-info/METADATA +0 -810
- unrealon-1.0.9.dist-info/RECORD +0 -246
- unrealon_browser/pyproject.toml +0 -182
- unrealon_browser/src/__init__.py +0 -62
- unrealon_browser/src/managers/logger_bridge.py +0 -395
- unrealon_driver/README.md +0 -204
- unrealon_driver/pyproject.toml +0 -187
- unrealon_driver/src/__init__.py +0 -90
- unrealon_driver/src/cli/__init__.py +0 -10
- unrealon_driver/src/cli/main.py +0 -66
- unrealon_driver/src/cli/simple.py +0 -510
- unrealon_driver/src/config/__init__.py +0 -11
- unrealon_driver/src/config/auto_config.py +0 -478
- unrealon_driver/src/core/__init__.py +0 -18
- unrealon_driver/src/core/exceptions.py +0 -289
- unrealon_driver/src/core/parser.py +0 -638
- unrealon_driver/src/dto/__init__.py +0 -66
- unrealon_driver/src/dto/cli.py +0 -119
- unrealon_driver/src/dto/config.py +0 -18
- unrealon_driver/src/dto/events.py +0 -237
- unrealon_driver/src/dto/execution.py +0 -313
- unrealon_driver/src/dto/services.py +0 -311
- unrealon_driver/src/execution/__init__.py +0 -23
- unrealon_driver/src/execution/daemon_mode.py +0 -317
- unrealon_driver/src/execution/interactive_mode.py +0 -88
- unrealon_driver/src/execution/modes.py +0 -45
- unrealon_driver/src/execution/scheduled_mode.py +0 -209
- unrealon_driver/src/execution/test_mode.py +0 -250
- unrealon_driver/src/logging/__init__.py +0 -24
- unrealon_driver/src/logging/driver_logger.py +0 -512
- unrealon_driver/src/services/__init__.py +0 -24
- unrealon_driver/src/services/browser_service.py +0 -726
- unrealon_driver/src/services/llm/__init__.py +0 -15
- unrealon_driver/src/services/llm/browser_llm_service.py +0 -363
- unrealon_driver/src/services/llm/llm.py +0 -195
- unrealon_driver/src/services/logger_service.py +0 -232
- unrealon_driver/src/services/metrics_service.py +0 -185
- unrealon_driver/src/services/scheduler_service.py +0 -489
- unrealon_driver/src/services/websocket_service.py +0 -362
- unrealon_driver/src/utils/__init__.py +0 -16
- unrealon_driver/src/utils/service_factory.py +0 -317
- unrealon_driver/src/utils/time_formatter.py +0 -338
- unrealon_llm/README.md +0 -44
- unrealon_llm/__init__.py +0 -26
- unrealon_llm/pyproject.toml +0 -154
- unrealon_llm/src/__init__.py +0 -228
- unrealon_llm/src/cli/__init__.py +0 -0
- unrealon_llm/src/core/__init__.py +0 -11
- unrealon_llm/src/core/smart_client.py +0 -438
- unrealon_llm/src/dto/__init__.py +0 -155
- unrealon_llm/src/dto/models/__init__.py +0 -0
- unrealon_llm/src/dto/models/config.py +0 -343
- unrealon_llm/src/dto/models/core.py +0 -328
- unrealon_llm/src/dto/models/enums.py +0 -123
- unrealon_llm/src/dto/models/html_analysis.py +0 -345
- unrealon_llm/src/dto/models/statistics.py +0 -473
- unrealon_llm/src/dto/models/translation.py +0 -383
- unrealon_llm/src/dto/models/type_conversion.py +0 -462
- unrealon_llm/src/dto/schemas/__init__.py +0 -0
- unrealon_llm/src/exceptions.py +0 -392
- unrealon_llm/src/llm_config/__init__.py +0 -20
- unrealon_llm/src/llm_config/logging_config.py +0 -178
- unrealon_llm/src/llm_logging/__init__.py +0 -42
- unrealon_llm/src/llm_logging/llm_events.py +0 -107
- unrealon_llm/src/llm_logging/llm_logger.py +0 -466
- unrealon_llm/src/managers/__init__.py +0 -15
- unrealon_llm/src/managers/cache_manager.py +0 -67
- unrealon_llm/src/managers/cost_manager.py +0 -107
- unrealon_llm/src/managers/request_manager.py +0 -298
- unrealon_llm/src/modules/__init__.py +0 -0
- unrealon_llm/src/modules/html_processor/__init__.py +0 -25
- unrealon_llm/src/modules/html_processor/base_processor.py +0 -415
- unrealon_llm/src/modules/html_processor/details_processor.py +0 -85
- unrealon_llm/src/modules/html_processor/listing_processor.py +0 -91
- unrealon_llm/src/modules/html_processor/models/__init__.py +0 -20
- unrealon_llm/src/modules/html_processor/models/processing_models.py +0 -40
- unrealon_llm/src/modules/html_processor/models/universal_model.py +0 -56
- unrealon_llm/src/modules/html_processor/processor.py +0 -102
- unrealon_llm/src/modules/llm/__init__.py +0 -0
- unrealon_llm/src/modules/translator/__init__.py +0 -0
- unrealon_llm/src/provider.py +0 -116
- unrealon_llm/src/utils/__init__.py +0 -95
- unrealon_llm/src/utils/common.py +0 -64
- unrealon_llm/src/utils/data_extractor.py +0 -188
- unrealon_llm/src/utils/html_cleaner.py +0 -767
- unrealon_llm/src/utils/language_detector.py +0 -308
- unrealon_llm/src/utils/models_cache.py +0 -592
- unrealon_llm/src/utils/smart_counter.py +0 -229
- unrealon_llm/src/utils/token_counter.py +0 -189
- unrealon_sdk/README.md +0 -25
- unrealon_sdk/__init__.py +0 -30
- unrealon_sdk/pyproject.toml +0 -231
- unrealon_sdk/src/__init__.py +0 -150
- unrealon_sdk/src/cli/__init__.py +0 -12
- unrealon_sdk/src/cli/commands/__init__.py +0 -22
- unrealon_sdk/src/cli/commands/benchmark.py +0 -42
- unrealon_sdk/src/cli/commands/diagnostics.py +0 -573
- unrealon_sdk/src/cli/commands/health.py +0 -46
- unrealon_sdk/src/cli/commands/integration.py +0 -498
- unrealon_sdk/src/cli/commands/reports.py +0 -43
- unrealon_sdk/src/cli/commands/security.py +0 -36
- unrealon_sdk/src/cli/commands/server.py +0 -483
- unrealon_sdk/src/cli/commands/servers.py +0 -56
- unrealon_sdk/src/cli/commands/tests.py +0 -55
- unrealon_sdk/src/cli/main.py +0 -126
- unrealon_sdk/src/cli/utils/reporter.py +0 -519
- unrealon_sdk/src/clients/openapi.yaml +0 -3347
- unrealon_sdk/src/clients/python_http/__init__.py +0 -3
- unrealon_sdk/src/clients/python_http/api_config.py +0 -228
- unrealon_sdk/src/clients/python_http/models/BaseModel.py +0 -12
- unrealon_sdk/src/clients/python_http/models/BroadcastDeliveryStats.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastMessage.py +0 -17
- unrealon_sdk/src/clients/python_http/models/BroadcastMessageRequest.py +0 -35
- unrealon_sdk/src/clients/python_http/models/BroadcastPriority.py +0 -10
- unrealon_sdk/src/clients/python_http/models/BroadcastResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/BroadcastResultResponse.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastTarget.py +0 -11
- unrealon_sdk/src/clients/python_http/models/ConnectionStats.py +0 -27
- unrealon_sdk/src/clients/python_http/models/ConnectionsResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/DeveloperMessageResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ErrorResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/HTTPValidationError.py +0 -16
- unrealon_sdk/src/clients/python_http/models/HealthResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/HealthStatus.py +0 -33
- unrealon_sdk/src/clients/python_http/models/LogLevel.py +0 -10
- unrealon_sdk/src/clients/python_http/models/LoggingRequest.py +0 -27
- unrealon_sdk/src/clients/python_http/models/LoggingResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/MaintenanceMode.py +0 -9
- unrealon_sdk/src/clients/python_http/models/MaintenanceModeRequest.py +0 -33
- unrealon_sdk/src/clients/python_http/models/MaintenanceStatusResponse.py +0 -39
- unrealon_sdk/src/clients/python_http/models/ParserCommandRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserMessageResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationRequest.py +0 -28
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserType.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyBlockRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyEndpointResponse.py +0 -20
- unrealon_sdk/src/clients/python_http/models/ProxyListResponse.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyProvider.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyPurchaseRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ProxyResponse.py +0 -47
- unrealon_sdk/src/clients/python_http/models/ProxyRotationRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ProxyStatus.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyUsageRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyUsageStatsResponse.py +0 -26
- unrealon_sdk/src/clients/python_http/models/ServiceRegistrationDto.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ServiceStatsResponse.py +0 -31
- unrealon_sdk/src/clients/python_http/models/SessionStartRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/SuccessResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/SystemNotificationResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ValidationError.py +0 -18
- unrealon_sdk/src/clients/python_http/models/ValidationErrorResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/WebSocketMetrics.py +0 -21
- unrealon_sdk/src/clients/python_http/models/__init__.py +0 -44
- unrealon_sdk/src/clients/python_http/services/None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/SocketLogging_service.py +0 -187
- unrealon_sdk/src/clients/python_http/services/SystemHealth_service.py +0 -119
- unrealon_sdk/src/clients/python_http/services/WebSocketAPI_service.py +0 -198
- unrealon_sdk/src/clients/python_http/services/__init__.py +0 -0
- unrealon_sdk/src/clients/python_http/services/admin_service.py +0 -125
- unrealon_sdk/src/clients/python_http/services/async_None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/async_ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/async_ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/async_SocketLogging_service.py +0 -189
- unrealon_sdk/src/clients/python_http/services/async_SystemHealth_service.py +0 -123
- unrealon_sdk/src/clients/python_http/services/async_WebSocketAPI_service.py +0 -200
- unrealon_sdk/src/clients/python_http/services/async_admin_service.py +0 -125
- unrealon_sdk/src/clients/python_websocket/__init__.py +0 -28
- unrealon_sdk/src/clients/python_websocket/client.py +0 -490
- unrealon_sdk/src/clients/python_websocket/events.py +0 -732
- unrealon_sdk/src/clients/python_websocket/example.py +0 -136
- unrealon_sdk/src/clients/python_websocket/types.py +0 -871
- unrealon_sdk/src/core/__init__.py +0 -64
- unrealon_sdk/src/core/client.py +0 -556
- unrealon_sdk/src/core/config.py +0 -465
- unrealon_sdk/src/core/exceptions.py +0 -239
- unrealon_sdk/src/core/metadata.py +0 -191
- unrealon_sdk/src/core/models.py +0 -142
- unrealon_sdk/src/core/types.py +0 -68
- unrealon_sdk/src/dto/__init__.py +0 -268
- unrealon_sdk/src/dto/authentication.py +0 -108
- unrealon_sdk/src/dto/cache.py +0 -208
- unrealon_sdk/src/dto/common.py +0 -19
- unrealon_sdk/src/dto/concurrency.py +0 -393
- unrealon_sdk/src/dto/events.py +0 -108
- unrealon_sdk/src/dto/health.py +0 -339
- unrealon_sdk/src/dto/load_balancing.py +0 -336
- unrealon_sdk/src/dto/logging.py +0 -230
- unrealon_sdk/src/dto/performance.py +0 -165
- unrealon_sdk/src/dto/rate_limiting.py +0 -295
- unrealon_sdk/src/dto/resource_pooling.py +0 -128
- unrealon_sdk/src/dto/structured_logging.py +0 -112
- unrealon_sdk/src/dto/task_scheduling.py +0 -121
- unrealon_sdk/src/dto/websocket.py +0 -55
- unrealon_sdk/src/enterprise/__init__.py +0 -59
- unrealon_sdk/src/enterprise/authentication.py +0 -401
- unrealon_sdk/src/enterprise/cache_manager.py +0 -578
- unrealon_sdk/src/enterprise/error_recovery.py +0 -494
- unrealon_sdk/src/enterprise/event_system.py +0 -549
- unrealon_sdk/src/enterprise/health_monitor.py +0 -747
- unrealon_sdk/src/enterprise/load_balancer.py +0 -964
- unrealon_sdk/src/enterprise/logging/__init__.py +0 -68
- unrealon_sdk/src/enterprise/logging/cleanup.py +0 -156
- unrealon_sdk/src/enterprise/logging/development.py +0 -744
- unrealon_sdk/src/enterprise/logging/service.py +0 -410
- unrealon_sdk/src/enterprise/multithreading_manager.py +0 -853
- unrealon_sdk/src/enterprise/performance_monitor.py +0 -539
- unrealon_sdk/src/enterprise/proxy_manager.py +0 -696
- unrealon_sdk/src/enterprise/rate_limiter.py +0 -652
- unrealon_sdk/src/enterprise/resource_pool.py +0 -763
- unrealon_sdk/src/enterprise/task_scheduler.py +0 -709
- unrealon_sdk/src/internal/__init__.py +0 -10
- unrealon_sdk/src/internal/command_router.py +0 -497
- unrealon_sdk/src/internal/connection_manager.py +0 -397
- unrealon_sdk/src/internal/http_client.py +0 -446
- unrealon_sdk/src/internal/websocket_client.py +0 -420
- unrealon_sdk/src/provider.py +0 -471
- unrealon_sdk/src/utils.py +0 -234
- /unrealon_browser/{src/cli → cli}/__init__.py +0 -0
- /unrealon_browser/{src/cli → cli}/interactive_mode.py +0 -0
- /unrealon_browser/{src/cli → cli}/main.py +0 -0
- /unrealon_browser/{src/core → core}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/config.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/core.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/dataclasses.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/detection.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/enums.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/statistics.py +0 -0
- /unrealon_browser/{src/managers → managers}/__init__.py +0 -0
- /unrealon_browser/{src/managers → managers}/stealth.py +0 -0
|
@@ -1,709 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Task Scheduler - Layer 4 Concurrency Service
|
|
3
|
-
|
|
4
|
-
Advanced task scheduling system with priority-based execution, dependency management,
|
|
5
|
-
and intelligent scheduling algorithms. Provides enterprise-grade task orchestration
|
|
6
|
-
with automatic retry, timeout handling, and performance optimization.
|
|
7
|
-
|
|
8
|
-
Features:
|
|
9
|
-
- Priority-based task queuing with multiple priority levels
|
|
10
|
-
- Dependency management with cycle detection
|
|
11
|
-
- Scheduled execution with cron-like scheduling
|
|
12
|
-
- Automatic retry with intelligent backoff strategies
|
|
13
|
-
- Timeout handling and resource management
|
|
14
|
-
- Progress tracking and result aggregation
|
|
15
|
-
- Cancellation support with cleanup
|
|
16
|
-
- Performance-based task optimization
|
|
17
|
-
- Dead letter queue for failed tasks
|
|
18
|
-
- Task result caching and persistence
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
import asyncio
|
|
22
|
-
import logging
|
|
23
|
-
import time
|
|
24
|
-
import heapq
|
|
25
|
-
import threading
|
|
26
|
-
from typing import Dict, List, Optional, Any, Callable, Set, Union, Tuple
|
|
27
|
-
from datetime import datetime, timezone, timedelta
|
|
28
|
-
from collections import defaultdict, deque
|
|
29
|
-
from dataclasses import dataclass, field
|
|
30
|
-
from enum import Enum
|
|
31
|
-
import uuid
|
|
32
|
-
import weakref
|
|
33
|
-
|
|
34
|
-
# Core SDK components
|
|
35
|
-
from unrealon_sdk.src.core.config import AdapterConfig
|
|
36
|
-
from unrealon_sdk.src.utils import generate_correlation_id
|
|
37
|
-
|
|
38
|
-
# DTO models
|
|
39
|
-
from unrealon_sdk.src.dto.logging import SDKEventType, SDKSeverity
|
|
40
|
-
from unrealon_sdk.src.dto.concurrency import (
|
|
41
|
-
TaskPriority,
|
|
42
|
-
TaskStatus,
|
|
43
|
-
ConcurrencyEventType,
|
|
44
|
-
Task,
|
|
45
|
-
ConcurrencyMetrics,
|
|
46
|
-
)
|
|
47
|
-
from unrealon_sdk.src.dto.task_scheduling import (
|
|
48
|
-
ScheduleType,
|
|
49
|
-
TaskSchedulerStrategy,
|
|
50
|
-
ScheduleInfo,
|
|
51
|
-
TaskDependency,
|
|
52
|
-
TaskProgress,
|
|
53
|
-
TaskSchedulerConfig,
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
# Development logging
|
|
57
|
-
from typing import TYPE_CHECKING
|
|
58
|
-
|
|
59
|
-
if TYPE_CHECKING:
|
|
60
|
-
from unrealon_sdk.src.enterprise.logging import DevelopmentLogger
|
|
61
|
-
|
|
62
|
-
logger = logging.getLogger(__name__)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class TaskScheduler:
|
|
66
|
-
"""
|
|
67
|
-
Enterprise-grade task scheduler.
|
|
68
|
-
|
|
69
|
-
Provides advanced task scheduling with priority management,
|
|
70
|
-
dependency resolution, and comprehensive execution monitoring.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
def __init__(
|
|
74
|
-
self,
|
|
75
|
-
config: AdapterConfig,
|
|
76
|
-
scheduler_config: Optional[TaskSchedulerConfig] = None,
|
|
77
|
-
dev_logger: Optional["DevelopmentLogger"] = None,
|
|
78
|
-
):
|
|
79
|
-
"""Initialize task scheduler."""
|
|
80
|
-
self.config = config
|
|
81
|
-
self.scheduler_config = scheduler_config or TaskSchedulerConfig()
|
|
82
|
-
self.dev_logger = dev_logger
|
|
83
|
-
|
|
84
|
-
# Task storage
|
|
85
|
-
self._tasks: Dict[str, Task] = {}
|
|
86
|
-
self._scheduled_tasks: List[Tuple[float, str]] = [] # (execution_time, task_id)
|
|
87
|
-
self._running_tasks: Dict[str, asyncio.Task] = {}
|
|
88
|
-
self._completed_tasks: Dict[str, Task] = {}
|
|
89
|
-
self._failed_tasks: Dict[str, Task] = {}
|
|
90
|
-
|
|
91
|
-
# Dependency management
|
|
92
|
-
self._dependencies: Dict[str, List[TaskDependency]] = defaultdict(list)
|
|
93
|
-
self._waiting_tasks: Dict[str, Set[str]] = defaultdict(set) # task_id -> depends_on_ids
|
|
94
|
-
self._dependent_tasks: Dict[str, Set[str]] = defaultdict(set) # task_id -> dependent_ids
|
|
95
|
-
|
|
96
|
-
# Scheduling
|
|
97
|
-
self._schedule_info: Dict[str, ScheduleInfo] = {}
|
|
98
|
-
self._recurring_tasks: Set[str] = set()
|
|
99
|
-
|
|
100
|
-
# Progress tracking
|
|
101
|
-
self._task_progress: Dict[str, TaskProgress] = {}
|
|
102
|
-
|
|
103
|
-
# Dead letter queue
|
|
104
|
-
self._dead_letter_queue: deque[Task] = deque(
|
|
105
|
-
maxlen=self.scheduler_config.dead_letter_max_size
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# Result caching
|
|
109
|
-
self._result_cache: Dict[str, Any] = {}
|
|
110
|
-
self._cache_timestamps: Dict[str, datetime] = {}
|
|
111
|
-
|
|
112
|
-
# Performance metrics
|
|
113
|
-
self._metrics = ConcurrencyMetrics()
|
|
114
|
-
self._task_performance: Dict[str, List[float]] = defaultdict(list)
|
|
115
|
-
|
|
116
|
-
# Background tasks
|
|
117
|
-
self._scheduler_task: Optional[asyncio.Task[None]] = None
|
|
118
|
-
self._cleanup_task: Optional[asyncio.Task[None]] = None
|
|
119
|
-
self._metrics_task: Optional[asyncio.Task[None]] = None
|
|
120
|
-
self._shutdown = False
|
|
121
|
-
|
|
122
|
-
# Thread safety
|
|
123
|
-
self._lock = threading.RLock()
|
|
124
|
-
|
|
125
|
-
# Task execution callbacks
|
|
126
|
-
self._on_task_start: Optional[Callable[[Task], None]] = None
|
|
127
|
-
self._on_task_complete: Optional[Callable[[Task, Any], None]] = None
|
|
128
|
-
self._on_task_error: Optional[Callable[[Task, Exception], None]] = None
|
|
129
|
-
|
|
130
|
-
self._log_info("Task scheduler initialized")
|
|
131
|
-
|
|
132
|
-
async def start(self) -> None:
|
|
133
|
-
"""Start task scheduler."""
|
|
134
|
-
if self._scheduler_task is None:
|
|
135
|
-
self._scheduler_task = asyncio.create_task(self._scheduler_loop())
|
|
136
|
-
|
|
137
|
-
if self._cleanup_task is None:
|
|
138
|
-
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
|
139
|
-
|
|
140
|
-
if self._metrics_task is None:
|
|
141
|
-
self._metrics_task = asyncio.create_task(self._metrics_loop())
|
|
142
|
-
|
|
143
|
-
self._log_info("Task scheduler started")
|
|
144
|
-
|
|
145
|
-
async def stop(self) -> None:
|
|
146
|
-
"""Stop task scheduler."""
|
|
147
|
-
self._shutdown = True
|
|
148
|
-
|
|
149
|
-
# Cancel running tasks
|
|
150
|
-
for task_id, task in list(self._running_tasks.items()):
|
|
151
|
-
task.cancel()
|
|
152
|
-
try:
|
|
153
|
-
await task
|
|
154
|
-
except asyncio.CancelledError:
|
|
155
|
-
pass
|
|
156
|
-
|
|
157
|
-
# Cancel background tasks
|
|
158
|
-
for task in [self._scheduler_task, self._cleanup_task, self._metrics_task]:
|
|
159
|
-
if task:
|
|
160
|
-
task.cancel()
|
|
161
|
-
try:
|
|
162
|
-
await task
|
|
163
|
-
except asyncio.CancelledError:
|
|
164
|
-
pass
|
|
165
|
-
|
|
166
|
-
self._log_info("Task scheduler stopped")
|
|
167
|
-
|
|
168
|
-
def schedule_task(
|
|
169
|
-
self,
|
|
170
|
-
task_func: Callable,
|
|
171
|
-
*args,
|
|
172
|
-
task_name: Optional[str] = None,
|
|
173
|
-
priority: TaskPriority = TaskPriority.NORMAL,
|
|
174
|
-
schedule_type: ScheduleType = ScheduleType.IMMEDIATE,
|
|
175
|
-
scheduled_time: Optional[datetime] = None,
|
|
176
|
-
delay_seconds: Optional[float] = None,
|
|
177
|
-
dependencies: Optional[List[str]] = None,
|
|
178
|
-
timeout_seconds: Optional[float] = None,
|
|
179
|
-
max_retries: Optional[int] = None,
|
|
180
|
-
context: Optional[Dict[str, Any]] = None,
|
|
181
|
-
**kwargs,
|
|
182
|
-
) -> str:
|
|
183
|
-
"""Schedule task for execution."""
|
|
184
|
-
|
|
185
|
-
# Create task
|
|
186
|
-
task = Task(
|
|
187
|
-
task_name=task_name or f"{task_func.__name__}",
|
|
188
|
-
task_type=f"{task_func.__module__}.{task_func.__name__}",
|
|
189
|
-
priority=priority,
|
|
190
|
-
dependencies=dependencies or [],
|
|
191
|
-
timeout_seconds=timeout_seconds or self.scheduler_config.default_timeout_seconds,
|
|
192
|
-
max_retries=max_retries or self.scheduler_config.max_retries,
|
|
193
|
-
context=context or {},
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
# Store function and arguments
|
|
197
|
-
task.context.update(
|
|
198
|
-
{
|
|
199
|
-
"func": task_func,
|
|
200
|
-
"args": args,
|
|
201
|
-
"kwargs": kwargs,
|
|
202
|
-
}
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
# Create schedule info
|
|
206
|
-
schedule_info = ScheduleInfo(schedule_type=schedule_type)
|
|
207
|
-
|
|
208
|
-
if schedule_type == ScheduleType.DELAYED and delay_seconds:
|
|
209
|
-
schedule_info.delay_seconds = delay_seconds
|
|
210
|
-
schedule_info.next_execution = datetime.now(timezone.utc) + timedelta(
|
|
211
|
-
seconds=delay_seconds
|
|
212
|
-
)
|
|
213
|
-
elif schedule_type == ScheduleType.SCHEDULED and scheduled_time:
|
|
214
|
-
schedule_info.scheduled_time = scheduled_time
|
|
215
|
-
schedule_info.next_execution = scheduled_time
|
|
216
|
-
else:
|
|
217
|
-
schedule_info.next_execution = datetime.now(timezone.utc)
|
|
218
|
-
|
|
219
|
-
with self._lock:
|
|
220
|
-
# Store task and schedule info
|
|
221
|
-
self._tasks[task.task_id] = task
|
|
222
|
-
self._schedule_info[task.task_id] = schedule_info
|
|
223
|
-
|
|
224
|
-
# Handle dependencies
|
|
225
|
-
if task.dependencies:
|
|
226
|
-
self._setup_dependencies(task)
|
|
227
|
-
|
|
228
|
-
# Add to schedule if not waiting for dependencies
|
|
229
|
-
if not self._waiting_tasks.get(task.task_id):
|
|
230
|
-
self._add_to_schedule(task.task_id, schedule_info.next_execution)
|
|
231
|
-
|
|
232
|
-
# Initialize progress tracking
|
|
233
|
-
self._task_progress[task.task_id] = TaskProgress(task_id=task.task_id)
|
|
234
|
-
|
|
235
|
-
self._log_task_event(ConcurrencyEventType.TASK_QUEUED, task)
|
|
236
|
-
return task.task_id
|
|
237
|
-
|
|
238
|
-
def schedule_recurring_task(
|
|
239
|
-
self,
|
|
240
|
-
task_func: Callable,
|
|
241
|
-
interval: timedelta,
|
|
242
|
-
*args,
|
|
243
|
-
task_name: Optional[str] = None,
|
|
244
|
-
priority: TaskPriority = TaskPriority.NORMAL,
|
|
245
|
-
max_executions: Optional[int] = None,
|
|
246
|
-
start_time: Optional[datetime] = None,
|
|
247
|
-
**kwargs,
|
|
248
|
-
) -> str:
|
|
249
|
-
"""Schedule recurring task."""
|
|
250
|
-
|
|
251
|
-
task_id = self.schedule_task(
|
|
252
|
-
task_func,
|
|
253
|
-
*args,
|
|
254
|
-
task_name=task_name,
|
|
255
|
-
priority=priority,
|
|
256
|
-
schedule_type=ScheduleType.RECURRING,
|
|
257
|
-
scheduled_time=start_time or datetime.now(timezone.utc),
|
|
258
|
-
**kwargs,
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
# Update schedule info for recurring
|
|
262
|
-
schedule_info = self._schedule_info[task_id]
|
|
263
|
-
schedule_info.repeat_interval = interval
|
|
264
|
-
schedule_info.max_executions = max_executions
|
|
265
|
-
|
|
266
|
-
self._recurring_tasks.add(task_id)
|
|
267
|
-
return task_id
|
|
268
|
-
|
|
269
|
-
def _setup_dependencies(self, task: Task) -> None:
|
|
270
|
-
"""Setup task dependencies."""
|
|
271
|
-
for dep_id in task.dependencies:
|
|
272
|
-
dependency = TaskDependency(
|
|
273
|
-
task_id=task.task_id,
|
|
274
|
-
depends_on=dep_id,
|
|
275
|
-
)
|
|
276
|
-
self._dependencies[task.task_id].append(dependency)
|
|
277
|
-
self._waiting_tasks[task.task_id].add(dep_id)
|
|
278
|
-
self._dependent_tasks[dep_id].add(task.task_id)
|
|
279
|
-
|
|
280
|
-
def _add_to_schedule(self, task_id: str, execution_time: datetime) -> None:
|
|
281
|
-
"""Add task to schedule."""
|
|
282
|
-
timestamp = execution_time.timestamp()
|
|
283
|
-
heapq.heappush(self._scheduled_tasks, (timestamp, task_id))
|
|
284
|
-
|
|
285
|
-
async def _scheduler_loop(self) -> None:
|
|
286
|
-
"""Main scheduler loop."""
|
|
287
|
-
while not self._shutdown:
|
|
288
|
-
try:
|
|
289
|
-
current_time = time.time()
|
|
290
|
-
tasks_to_execute = []
|
|
291
|
-
|
|
292
|
-
# Find tasks ready for execution
|
|
293
|
-
with self._lock:
|
|
294
|
-
while self._scheduled_tasks and self._scheduled_tasks[0][0] <= current_time:
|
|
295
|
-
_, task_id = heapq.heappop(self._scheduled_tasks)
|
|
296
|
-
if task_id in self._tasks:
|
|
297
|
-
tasks_to_execute.append(task_id)
|
|
298
|
-
|
|
299
|
-
# Execute ready tasks
|
|
300
|
-
for task_id in tasks_to_execute:
|
|
301
|
-
if len(self._running_tasks) < self.scheduler_config.max_concurrent_tasks:
|
|
302
|
-
await self._execute_task(task_id)
|
|
303
|
-
else:
|
|
304
|
-
# Re-schedule for later if at capacity
|
|
305
|
-
execution_time = datetime.fromtimestamp(current_time + 1, tz=timezone.utc)
|
|
306
|
-
self._add_to_schedule(task_id, execution_time)
|
|
307
|
-
|
|
308
|
-
# Sleep based on resolution
|
|
309
|
-
await asyncio.sleep(self.scheduler_config.schedule_resolution_seconds)
|
|
310
|
-
|
|
311
|
-
except asyncio.CancelledError:
|
|
312
|
-
break
|
|
313
|
-
except Exception as e:
|
|
314
|
-
logger.error(f"Error in scheduler loop: {e}")
|
|
315
|
-
await asyncio.sleep(1)
|
|
316
|
-
|
|
317
|
-
async def _execute_task(self, task_id: str) -> None:
|
|
318
|
-
"""Execute task."""
|
|
319
|
-
if task_id not in self._tasks:
|
|
320
|
-
return
|
|
321
|
-
|
|
322
|
-
task = self._tasks[task_id]
|
|
323
|
-
|
|
324
|
-
# Update task status
|
|
325
|
-
task.status = TaskStatus.RUNNING
|
|
326
|
-
task.started_at = datetime.now(timezone.utc)
|
|
327
|
-
|
|
328
|
-
# Update progress
|
|
329
|
-
progress = self._task_progress.get(task_id)
|
|
330
|
-
if progress:
|
|
331
|
-
progress.started_at = task.started_at
|
|
332
|
-
progress.status_message = "Executing..."
|
|
333
|
-
|
|
334
|
-
# Create execution task
|
|
335
|
-
execution_task = asyncio.create_task(self._run_task_with_timeout(task))
|
|
336
|
-
self._running_tasks[task_id] = execution_task
|
|
337
|
-
|
|
338
|
-
self._log_task_event(ConcurrencyEventType.TASK_STARTED, task)
|
|
339
|
-
|
|
340
|
-
# Call start callback
|
|
341
|
-
if self._on_task_start:
|
|
342
|
-
try:
|
|
343
|
-
self._on_task_start(task)
|
|
344
|
-
except Exception as e:
|
|
345
|
-
logger.error(f"Error in task start callback: {e}")
|
|
346
|
-
|
|
347
|
-
async def _run_task_with_timeout(self, task: Task) -> None:
|
|
348
|
-
"""Run task with timeout handling."""
|
|
349
|
-
try:
|
|
350
|
-
# Extract function and arguments
|
|
351
|
-
func = task.context["func"]
|
|
352
|
-
args = task.context.get("args", ())
|
|
353
|
-
kwargs = task.context.get("kwargs", {})
|
|
354
|
-
|
|
355
|
-
# Execute with timeout
|
|
356
|
-
if task.timeout_seconds:
|
|
357
|
-
result = await asyncio.wait_for(
|
|
358
|
-
self._execute_task_function(func, args, kwargs), timeout=task.timeout_seconds
|
|
359
|
-
)
|
|
360
|
-
else:
|
|
361
|
-
result = await self._execute_task_function(func, args, kwargs)
|
|
362
|
-
|
|
363
|
-
# Task completed successfully
|
|
364
|
-
await self._handle_task_completion(task, result)
|
|
365
|
-
|
|
366
|
-
except asyncio.TimeoutError:
|
|
367
|
-
task.status = TaskStatus.TIMEOUT
|
|
368
|
-
task.error_message = f"Task timed out after {task.timeout_seconds} seconds"
|
|
369
|
-
await self._handle_task_failure(task, asyncio.TimeoutError("Task timeout"))
|
|
370
|
-
|
|
371
|
-
except Exception as e:
|
|
372
|
-
task.status = TaskStatus.FAILED
|
|
373
|
-
task.error_message = str(e)
|
|
374
|
-
await self._handle_task_failure(task, e)
|
|
375
|
-
|
|
376
|
-
finally:
|
|
377
|
-
# Clean up
|
|
378
|
-
self._running_tasks.pop(task.task_id, None)
|
|
379
|
-
task.completed_at = datetime.now(timezone.utc)
|
|
380
|
-
|
|
381
|
-
if task.started_at:
|
|
382
|
-
duration = (task.completed_at - task.started_at).total_seconds() * 1000
|
|
383
|
-
task.actual_duration_ms = duration
|
|
384
|
-
|
|
385
|
-
# Track performance
|
|
386
|
-
task_type = task.task_type
|
|
387
|
-
self._task_performance[task_type].append(duration)
|
|
388
|
-
|
|
389
|
-
# Keep only recent performance data
|
|
390
|
-
if len(self._task_performance[task_type]) > 100:
|
|
391
|
-
self._task_performance[task_type] = self._task_performance[task_type][-50:]
|
|
392
|
-
|
|
393
|
-
async def _execute_task_function(self, func: Callable, args: tuple, kwargs: dict) -> Any:
|
|
394
|
-
"""Execute task function (async or sync)."""
|
|
395
|
-
if asyncio.iscoroutinefunction(func):
|
|
396
|
-
return await func(*args, **kwargs)
|
|
397
|
-
else:
|
|
398
|
-
# Run sync function in thread pool
|
|
399
|
-
loop = asyncio.get_event_loop()
|
|
400
|
-
return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
|
|
401
|
-
|
|
402
|
-
async def _handle_task_completion(self, task: Task, result: Any) -> None:
|
|
403
|
-
"""Handle successful task completion."""
|
|
404
|
-
task.status = TaskStatus.COMPLETED
|
|
405
|
-
task.result = result
|
|
406
|
-
|
|
407
|
-
# Update progress
|
|
408
|
-
progress = self._task_progress.get(task.task_id)
|
|
409
|
-
if progress:
|
|
410
|
-
progress.progress_percent = 100.0
|
|
411
|
-
progress.status_message = "Completed"
|
|
412
|
-
progress.last_update = datetime.now(timezone.utc)
|
|
413
|
-
|
|
414
|
-
# Cache result if enabled
|
|
415
|
-
if self.scheduler_config.enable_result_caching:
|
|
416
|
-
cache_key = f"{task.task_type}:{hash(str(task.context))}"
|
|
417
|
-
self._result_cache[cache_key] = result
|
|
418
|
-
self._cache_timestamps[cache_key] = datetime.now(timezone.utc)
|
|
419
|
-
|
|
420
|
-
# Move to completed tasks
|
|
421
|
-
with self._lock:
|
|
422
|
-
self._completed_tasks[task.task_id] = task
|
|
423
|
-
|
|
424
|
-
# Handle dependent tasks
|
|
425
|
-
for dependent_id in self._dependent_tasks.get(task.task_id, set()):
|
|
426
|
-
if dependent_id in self._waiting_tasks:
|
|
427
|
-
self._waiting_tasks[dependent_id].discard(task.task_id)
|
|
428
|
-
|
|
429
|
-
# If all dependencies met, schedule for execution
|
|
430
|
-
if not self._waiting_tasks[dependent_id]:
|
|
431
|
-
del self._waiting_tasks[dependent_id]
|
|
432
|
-
dependent_task = self._tasks.get(dependent_id)
|
|
433
|
-
if dependent_task:
|
|
434
|
-
schedule_info = self._schedule_info.get(dependent_id)
|
|
435
|
-
if schedule_info:
|
|
436
|
-
self._add_to_schedule(dependent_id, schedule_info.next_execution)
|
|
437
|
-
|
|
438
|
-
# Handle recurring tasks
|
|
439
|
-
if task.task_id in self._recurring_tasks:
|
|
440
|
-
await self._reschedule_recurring_task(task.task_id)
|
|
441
|
-
|
|
442
|
-
self._log_task_event(ConcurrencyEventType.TASK_COMPLETED, task)
|
|
443
|
-
|
|
444
|
-
# Call completion callback
|
|
445
|
-
if self._on_task_complete:
|
|
446
|
-
try:
|
|
447
|
-
self._on_task_complete(task, result)
|
|
448
|
-
except Exception as e:
|
|
449
|
-
logger.error(f"Error in task completion callback: {e}")
|
|
450
|
-
|
|
451
|
-
async def _handle_task_failure(self, task: Task, exception: Exception) -> None:
|
|
452
|
-
"""Handle task failure."""
|
|
453
|
-
|
|
454
|
-
# Check if retry is possible
|
|
455
|
-
if task.retry_count < task.max_retries:
|
|
456
|
-
task.retry_count += 1
|
|
457
|
-
task.status = TaskStatus.RETRYING
|
|
458
|
-
|
|
459
|
-
# Calculate retry delay with exponential backoff
|
|
460
|
-
delay = self.scheduler_config.retry_backoff_factor ** (task.retry_count - 1)
|
|
461
|
-
retry_time = datetime.now(timezone.utc) + timedelta(seconds=delay)
|
|
462
|
-
|
|
463
|
-
# Re-schedule task
|
|
464
|
-
self._add_to_schedule(task.task_id, retry_time)
|
|
465
|
-
|
|
466
|
-
self._log_info(
|
|
467
|
-
f"Retrying task {task.task_id} (attempt {task.retry_count}/{task.max_retries}) in {delay}s"
|
|
468
|
-
)
|
|
469
|
-
return
|
|
470
|
-
|
|
471
|
-
# Task permanently failed
|
|
472
|
-
with self._lock:
|
|
473
|
-
self._failed_tasks[task.task_id] = task
|
|
474
|
-
|
|
475
|
-
# Add to dead letter queue if enabled
|
|
476
|
-
if self.scheduler_config.enable_dead_letter_queue:
|
|
477
|
-
self._dead_letter_queue.append(task)
|
|
478
|
-
|
|
479
|
-
self._log_task_event(ConcurrencyEventType.TASK_FAILED, task)
|
|
480
|
-
|
|
481
|
-
# Call error callback
|
|
482
|
-
if self._on_task_error:
|
|
483
|
-
try:
|
|
484
|
-
self._on_task_error(task, exception)
|
|
485
|
-
except Exception as e:
|
|
486
|
-
logger.error(f"Error in task error callback: {e}")
|
|
487
|
-
|
|
488
|
-
async def _reschedule_recurring_task(self, task_id: str) -> None:
|
|
489
|
-
"""Reschedule recurring task for next execution."""
|
|
490
|
-
schedule_info = self._schedule_info.get(task_id)
|
|
491
|
-
if not schedule_info or not schedule_info.repeat_interval:
|
|
492
|
-
return
|
|
493
|
-
|
|
494
|
-
# Check execution count limit
|
|
495
|
-
schedule_info.execution_count += 1
|
|
496
|
-
if (
|
|
497
|
-
schedule_info.max_executions
|
|
498
|
-
and schedule_info.execution_count >= schedule_info.max_executions
|
|
499
|
-
):
|
|
500
|
-
self._recurring_tasks.discard(task_id)
|
|
501
|
-
return
|
|
502
|
-
|
|
503
|
-
# Calculate next execution time
|
|
504
|
-
schedule_info.last_execution = datetime.now(timezone.utc)
|
|
505
|
-
schedule_info.next_execution = schedule_info.last_execution + schedule_info.repeat_interval
|
|
506
|
-
|
|
507
|
-
# Create new task instance for next execution
|
|
508
|
-
original_task = self._tasks[task_id]
|
|
509
|
-
new_task = Task(
|
|
510
|
-
task_name=original_task.task_name,
|
|
511
|
-
task_type=original_task.task_type,
|
|
512
|
-
priority=original_task.priority,
|
|
513
|
-
timeout_seconds=original_task.timeout_seconds,
|
|
514
|
-
max_retries=original_task.max_retries,
|
|
515
|
-
context=original_task.context.copy(),
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
# Store new task
|
|
519
|
-
self._tasks[new_task.task_id] = new_task
|
|
520
|
-
self._schedule_info[new_task.task_id] = schedule_info
|
|
521
|
-
self._task_progress[new_task.task_id] = TaskProgress(task_id=new_task.task_id)
|
|
522
|
-
|
|
523
|
-
# Schedule for next execution
|
|
524
|
-
self._add_to_schedule(new_task.task_id, schedule_info.next_execution)
|
|
525
|
-
|
|
526
|
-
async def _cleanup_loop(self) -> None:
|
|
527
|
-
"""Background cleanup loop."""
|
|
528
|
-
while not self._shutdown:
|
|
529
|
-
try:
|
|
530
|
-
await asyncio.sleep(300) # Cleanup every 5 minutes
|
|
531
|
-
await self._cleanup_old_tasks()
|
|
532
|
-
await self._cleanup_cache()
|
|
533
|
-
except asyncio.CancelledError:
|
|
534
|
-
break
|
|
535
|
-
except Exception as e:
|
|
536
|
-
logger.error(f"Error in cleanup loop: {e}")
|
|
537
|
-
|
|
538
|
-
async def _cleanup_old_tasks(self) -> None:
|
|
539
|
-
"""Clean up old completed/failed tasks."""
|
|
540
|
-
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=24)
|
|
541
|
-
|
|
542
|
-
with self._lock:
|
|
543
|
-
# Clean up completed tasks
|
|
544
|
-
old_completed = [
|
|
545
|
-
task_id
|
|
546
|
-
for task_id, task in self._completed_tasks.items()
|
|
547
|
-
if task.completed_at and task.completed_at < cutoff_time
|
|
548
|
-
]
|
|
549
|
-
for task_id in old_completed:
|
|
550
|
-
del self._completed_tasks[task_id]
|
|
551
|
-
self._task_progress.pop(task_id, None)
|
|
552
|
-
|
|
553
|
-
# Clean up failed tasks
|
|
554
|
-
old_failed = [
|
|
555
|
-
task_id
|
|
556
|
-
for task_id, task in self._failed_tasks.items()
|
|
557
|
-
if task.completed_at and task.completed_at < cutoff_time
|
|
558
|
-
]
|
|
559
|
-
for task_id in old_failed:
|
|
560
|
-
del self._failed_tasks[task_id]
|
|
561
|
-
self._task_progress.pop(task_id, None)
|
|
562
|
-
|
|
563
|
-
if old_completed or old_failed:
|
|
564
|
-
self._log_info(
|
|
565
|
-
f"Cleaned up {len(old_completed)} completed and {len(old_failed)} failed tasks"
|
|
566
|
-
)
|
|
567
|
-
|
|
568
|
-
async def _cleanup_cache(self) -> None:
|
|
569
|
-
"""Clean up expired cache entries."""
|
|
570
|
-
if not self.scheduler_config.enable_result_caching:
|
|
571
|
-
return
|
|
572
|
-
|
|
573
|
-
cutoff_time = datetime.now(timezone.utc) - timedelta(
|
|
574
|
-
seconds=self.scheduler_config.cache_ttl_seconds
|
|
575
|
-
)
|
|
576
|
-
|
|
577
|
-
expired_keys = [
|
|
578
|
-
key for key, timestamp in self._cache_timestamps.items() if timestamp < cutoff_time
|
|
579
|
-
]
|
|
580
|
-
|
|
581
|
-
for key in expired_keys:
|
|
582
|
-
self._result_cache.pop(key, None)
|
|
583
|
-
self._cache_timestamps.pop(key, None)
|
|
584
|
-
|
|
585
|
-
if expired_keys:
|
|
586
|
-
self._log_info(f"Cleaned up {len(expired_keys)} expired cache entries")
|
|
587
|
-
|
|
588
|
-
async def _metrics_loop(self) -> None:
|
|
589
|
-
"""Background metrics collection loop."""
|
|
590
|
-
while not self._shutdown:
|
|
591
|
-
try:
|
|
592
|
-
await asyncio.sleep(60) # Collect every minute
|
|
593
|
-
await self._collect_metrics()
|
|
594
|
-
except asyncio.CancelledError:
|
|
595
|
-
break
|
|
596
|
-
except Exception as e:
|
|
597
|
-
logger.error(f"Error in metrics loop: {e}")
|
|
598
|
-
|
|
599
|
-
async def _collect_metrics(self) -> None:
|
|
600
|
-
"""Collect scheduler metrics."""
|
|
601
|
-
with self._lock:
|
|
602
|
-
self._metrics.total_tasks = len(self._tasks)
|
|
603
|
-
self._metrics.running_tasks = len(self._running_tasks)
|
|
604
|
-
self._metrics.pending_tasks = len(self._scheduled_tasks)
|
|
605
|
-
self._metrics.completed_tasks = len(self._completed_tasks)
|
|
606
|
-
self._metrics.failed_tasks = len(self._failed_tasks)
|
|
607
|
-
|
|
608
|
-
# Calculate average task duration
|
|
609
|
-
all_durations = []
|
|
610
|
-
for durations in self._task_performance.values():
|
|
611
|
-
all_durations.extend(durations)
|
|
612
|
-
|
|
613
|
-
if all_durations:
|
|
614
|
-
self._metrics.avg_task_duration_ms = sum(all_durations) / len(all_durations)
|
|
615
|
-
|
|
616
|
-
def cancel_task(self, task_id: str) -> bool:
|
|
617
|
-
"""Cancel task execution."""
|
|
618
|
-
with self._lock:
|
|
619
|
-
# Cancel if running
|
|
620
|
-
if task_id in self._running_tasks:
|
|
621
|
-
execution_task = self._running_tasks[task_id]
|
|
622
|
-
execution_task.cancel()
|
|
623
|
-
|
|
624
|
-
# Update task status
|
|
625
|
-
if task_id in self._tasks:
|
|
626
|
-
self._tasks[task_id].status = TaskStatus.CANCELLED
|
|
627
|
-
|
|
628
|
-
return True
|
|
629
|
-
|
|
630
|
-
# Remove from schedule if pending
|
|
631
|
-
elif task_id in self._tasks:
|
|
632
|
-
self._tasks[task_id].status = TaskStatus.CANCELLED
|
|
633
|
-
|
|
634
|
-
# Remove from scheduled tasks
|
|
635
|
-
self._scheduled_tasks = [
|
|
636
|
-
(timestamp, tid) for timestamp, tid in self._scheduled_tasks if tid != task_id
|
|
637
|
-
]
|
|
638
|
-
heapq.heapify(self._scheduled_tasks)
|
|
639
|
-
|
|
640
|
-
return True
|
|
641
|
-
|
|
642
|
-
return False
|
|
643
|
-
|
|
644
|
-
def get_task_status(self, task_id: str) -> Optional[Task]:
|
|
645
|
-
"""Get task status."""
|
|
646
|
-
return (
|
|
647
|
-
self._tasks.get(task_id)
|
|
648
|
-
or self._completed_tasks.get(task_id)
|
|
649
|
-
or self._failed_tasks.get(task_id)
|
|
650
|
-
)
|
|
651
|
-
|
|
652
|
-
def get_task_progress(self, task_id: str) -> Optional[TaskProgress]:
|
|
653
|
-
"""Get task progress."""
|
|
654
|
-
return self._task_progress.get(task_id)
|
|
655
|
-
|
|
656
|
-
def get_metrics(self) -> ConcurrencyMetrics:
|
|
657
|
-
"""Get scheduler metrics."""
|
|
658
|
-
return self._metrics.model_copy()
|
|
659
|
-
|
|
660
|
-
def get_dead_letter_queue(self) -> List[Task]:
|
|
661
|
-
"""Get dead letter queue contents."""
|
|
662
|
-
return list(self._dead_letter_queue)
|
|
663
|
-
|
|
664
|
-
def set_callbacks(
|
|
665
|
-
self,
|
|
666
|
-
on_start: Optional[Callable[[Task], None]] = None,
|
|
667
|
-
on_complete: Optional[Callable[[Task, Any], None]] = None,
|
|
668
|
-
on_error: Optional[Callable[[Task, Exception], None]] = None,
|
|
669
|
-
) -> None:
|
|
670
|
-
"""Set task execution callbacks."""
|
|
671
|
-
self._on_task_start = on_start
|
|
672
|
-
self._on_task_complete = on_complete
|
|
673
|
-
self._on_task_error = on_error
|
|
674
|
-
|
|
675
|
-
def _log_task_event(self, event_type: ConcurrencyEventType, task: Task) -> None:
|
|
676
|
-
"""Log task event."""
|
|
677
|
-
message = f"Task {event_type.value}: {task.task_name} ({task.task_id})"
|
|
678
|
-
|
|
679
|
-
if self.dev_logger:
|
|
680
|
-
self.dev_logger.log_debug(
|
|
681
|
-
SDKEventType.DEBUG_CHECKPOINT,
|
|
682
|
-
message,
|
|
683
|
-
details={
|
|
684
|
-
"event_type": event_type.value,
|
|
685
|
-
"task_id": task.task_id,
|
|
686
|
-
"task_status": task.status.value,
|
|
687
|
-
"priority": task.priority.value,
|
|
688
|
-
},
|
|
689
|
-
)
|
|
690
|
-
else:
|
|
691
|
-
logger.debug(message)
|
|
692
|
-
|
|
693
|
-
def _log_info(self, message: str, **kwargs: Any) -> None:
|
|
694
|
-
"""Log info message."""
|
|
695
|
-
if self.dev_logger:
|
|
696
|
-
self.dev_logger.log_info(
|
|
697
|
-
SDKEventType.PERFORMANCE_OPTIMIZATION_APPLIED, message, **kwargs
|
|
698
|
-
)
|
|
699
|
-
else:
|
|
700
|
-
logger.info(message)
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
__all__ = [
|
|
704
|
-
# Main business logic class
|
|
705
|
-
"TaskScheduler",
|
|
706
|
-
# Note: Task scheduling models are available via DTO imports:
|
|
707
|
-
# from unrealon_sdk.src.dto.task_scheduling import ...
|
|
708
|
-
# from unrealon_sdk.src.dto.concurrency import ...
|
|
709
|
-
]
|