unrealon 1.0.9__py3-none-any.whl → 1.1.1__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.1.dist-info/METADATA +722 -0
- unrealon-1.1.1.dist-info/RECORD +82 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info}/WHEEL +1 -1
- unrealon-1.1.1.dist-info/entry_points.txt +9 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.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,763 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Resource Pool - Layer 4 Concurrency Service
|
|
3
|
-
|
|
4
|
-
Enterprise-grade resource pooling system with intelligent lifecycle management,
|
|
5
|
-
health monitoring, and automatic optimization. Provides efficient resource
|
|
6
|
-
utilization with connection pooling, thread management, and memory optimization.
|
|
7
|
-
|
|
8
|
-
Features:
|
|
9
|
-
- Multi-type resource pooling (connections, threads, memory, files)
|
|
10
|
-
- Dynamic pool sizing with auto-scaling capabilities
|
|
11
|
-
- Health monitoring and automatic resource validation
|
|
12
|
-
- Resource lifecycle management with cleanup
|
|
13
|
-
- Pool exhaustion handling with queuing and overflow
|
|
14
|
-
- Performance optimization with usage analytics
|
|
15
|
-
- Leak detection and automatic resource recovery
|
|
16
|
-
- Pool warmup and preemptive resource allocation
|
|
17
|
-
- Resource tagging and categorization
|
|
18
|
-
- Integration with monitoring and alerting systems
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
import asyncio
|
|
22
|
-
import logging
|
|
23
|
-
import threading
|
|
24
|
-
import time
|
|
25
|
-
import weakref
|
|
26
|
-
from typing import Dict, List, Optional, Any, Callable, Set, Union, TypeVar, Generic
|
|
27
|
-
from datetime import datetime, timezone, timedelta
|
|
28
|
-
from collections import defaultdict, deque
|
|
29
|
-
from dataclasses import dataclass, field
|
|
30
|
-
from contextlib import asynccontextmanager, contextmanager
|
|
31
|
-
from enum import Enum
|
|
32
|
-
import uuid
|
|
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
|
-
ResourceType,
|
|
42
|
-
ResourceStatus,
|
|
43
|
-
ConcurrencyEventType,
|
|
44
|
-
ResourcePool as ResourcePoolDTO,
|
|
45
|
-
ConcurrencyMetrics,
|
|
46
|
-
)
|
|
47
|
-
from unrealon_sdk.src.dto.resource_pooling import (
|
|
48
|
-
ResourceLifecycleState,
|
|
49
|
-
PoolScalingStrategy,
|
|
50
|
-
ResourceMetadata,
|
|
51
|
-
PoolConfig,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
# Development logging
|
|
55
|
-
from typing import TYPE_CHECKING
|
|
56
|
-
|
|
57
|
-
if TYPE_CHECKING:
|
|
58
|
-
from unrealon_sdk.src.enterprise.logging import DevelopmentLogger
|
|
59
|
-
|
|
60
|
-
logger = logging.getLogger(__name__)
|
|
61
|
-
|
|
62
|
-
T = TypeVar("T")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class ManagedResource(Generic[T]):
|
|
66
|
-
"""Wrapper for managed resources with metadata."""
|
|
67
|
-
|
|
68
|
-
def __init__(
|
|
69
|
-
self,
|
|
70
|
-
resource: T,
|
|
71
|
-
metadata: ResourceMetadata,
|
|
72
|
-
validator: Optional[Callable[[T], bool]] = None,
|
|
73
|
-
cleanup_func: Optional[Callable[[T], None]] = None,
|
|
74
|
-
):
|
|
75
|
-
self.resource = resource
|
|
76
|
-
self.metadata = metadata
|
|
77
|
-
self._validator = validator
|
|
78
|
-
self._cleanup_func = cleanup_func
|
|
79
|
-
self._in_use = False
|
|
80
|
-
self._acquired_at: Optional[datetime] = None
|
|
81
|
-
self._acquired_by: Optional[str] = None
|
|
82
|
-
|
|
83
|
-
def acquire(self, client_id: str) -> T:
|
|
84
|
-
"""Acquire resource for use."""
|
|
85
|
-
if self._in_use:
|
|
86
|
-
raise RuntimeError(f"Resource {self.metadata.resource_id} is already in use")
|
|
87
|
-
|
|
88
|
-
self._in_use = True
|
|
89
|
-
self._acquired_at = datetime.now(timezone.utc)
|
|
90
|
-
self._acquired_by = client_id
|
|
91
|
-
self.metadata.last_used = self._acquired_at
|
|
92
|
-
self.metadata.usage_count += 1
|
|
93
|
-
self.metadata.lifecycle_state = ResourceLifecycleState.IN_USE
|
|
94
|
-
|
|
95
|
-
return self.resource
|
|
96
|
-
|
|
97
|
-
def release(self) -> None:
|
|
98
|
-
"""Release resource back to pool."""
|
|
99
|
-
self._in_use = False
|
|
100
|
-
self._acquired_at = None
|
|
101
|
-
self._acquired_by = None
|
|
102
|
-
self.metadata.lifecycle_state = ResourceLifecycleState.IDLE
|
|
103
|
-
|
|
104
|
-
def is_valid(self) -> bool:
|
|
105
|
-
"""Check if resource is valid."""
|
|
106
|
-
if self._validator:
|
|
107
|
-
try:
|
|
108
|
-
return self._validator(self.resource)
|
|
109
|
-
except Exception as e:
|
|
110
|
-
self.metadata.error_count += 1
|
|
111
|
-
self.metadata.last_error = str(e)
|
|
112
|
-
return False
|
|
113
|
-
return True
|
|
114
|
-
|
|
115
|
-
def cleanup(self) -> None:
|
|
116
|
-
"""Cleanup resource."""
|
|
117
|
-
if self._cleanup_func:
|
|
118
|
-
try:
|
|
119
|
-
self._cleanup_func(self.resource)
|
|
120
|
-
except Exception as e:
|
|
121
|
-
logger.error(f"Error cleaning up resource {self.metadata.resource_id}: {e}")
|
|
122
|
-
|
|
123
|
-
self.metadata.lifecycle_state = ResourceLifecycleState.DESTROYED
|
|
124
|
-
|
|
125
|
-
@property
|
|
126
|
-
def is_in_use(self) -> bool:
|
|
127
|
-
"""Check if resource is currently in use."""
|
|
128
|
-
return self._in_use
|
|
129
|
-
|
|
130
|
-
@property
|
|
131
|
-
def acquired_duration(self) -> Optional[timedelta]:
|
|
132
|
-
"""Get how long resource has been acquired."""
|
|
133
|
-
if self._acquired_at:
|
|
134
|
-
return datetime.now(timezone.utc) - self._acquired_at
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class ResourcePoolManager:
|
|
139
|
-
"""
|
|
140
|
-
Enterprise-grade resource pool manager.
|
|
141
|
-
|
|
142
|
-
Manages multiple resource pools with intelligent lifecycle management,
|
|
143
|
-
health monitoring, and performance optimization.
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
def __init__(
|
|
147
|
-
self,
|
|
148
|
-
config: AdapterConfig,
|
|
149
|
-
dev_logger: Optional["DevelopmentLogger"] = None,
|
|
150
|
-
):
|
|
151
|
-
"""Initialize resource pool manager."""
|
|
152
|
-
self.config = config
|
|
153
|
-
self.dev_logger = dev_logger
|
|
154
|
-
|
|
155
|
-
# Pool management
|
|
156
|
-
self._pools: Dict[str, "ResourcePoolInstance"] = {}
|
|
157
|
-
self._pool_configs: Dict[str, PoolConfig] = {}
|
|
158
|
-
|
|
159
|
-
# Resource factories
|
|
160
|
-
self._resource_factories: Dict[str, Callable[[], Any]] = {}
|
|
161
|
-
self._resource_validators: Dict[str, Callable[[Any], bool]] = {}
|
|
162
|
-
self._resource_cleanup_funcs: Dict[str, Callable[[Any], None]] = {}
|
|
163
|
-
|
|
164
|
-
# Global metrics
|
|
165
|
-
self._global_metrics = ConcurrencyMetrics()
|
|
166
|
-
|
|
167
|
-
# Background tasks
|
|
168
|
-
self._monitor_task: Optional[asyncio.Task[None]] = None
|
|
169
|
-
self._cleanup_task: Optional[asyncio.Task[None]] = None
|
|
170
|
-
self._scaling_task: Optional[asyncio.Task[None]] = None
|
|
171
|
-
self._shutdown = False
|
|
172
|
-
|
|
173
|
-
# Thread safety
|
|
174
|
-
self._lock = threading.RLock()
|
|
175
|
-
|
|
176
|
-
self._log_info("Resource pool manager initialized")
|
|
177
|
-
|
|
178
|
-
async def start(self) -> None:
|
|
179
|
-
"""Start resource pool manager."""
|
|
180
|
-
# Start background tasks
|
|
181
|
-
if self._monitor_task is None:
|
|
182
|
-
self._monitor_task = asyncio.create_task(self._monitoring_loop())
|
|
183
|
-
|
|
184
|
-
if self._cleanup_task is None:
|
|
185
|
-
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
|
186
|
-
|
|
187
|
-
if self._scaling_task is None:
|
|
188
|
-
self._scaling_task = asyncio.create_task(self._scaling_loop())
|
|
189
|
-
|
|
190
|
-
self._log_info("Resource pool manager started")
|
|
191
|
-
|
|
192
|
-
async def stop(self) -> None:
|
|
193
|
-
"""Stop resource pool manager and cleanup all pools."""
|
|
194
|
-
self._shutdown = True
|
|
195
|
-
|
|
196
|
-
# Cancel background tasks
|
|
197
|
-
for task in [self._monitor_task, self._cleanup_task, self._scaling_task]:
|
|
198
|
-
if task:
|
|
199
|
-
task.cancel()
|
|
200
|
-
try:
|
|
201
|
-
await task
|
|
202
|
-
except asyncio.CancelledError:
|
|
203
|
-
pass
|
|
204
|
-
|
|
205
|
-
# Shutdown all pools
|
|
206
|
-
for pool_name, pool in list(self._pools.items()):
|
|
207
|
-
await pool.shutdown()
|
|
208
|
-
|
|
209
|
-
self._log_info("Resource pool manager stopped")
|
|
210
|
-
|
|
211
|
-
def create_pool(
|
|
212
|
-
self,
|
|
213
|
-
pool_config: PoolConfig,
|
|
214
|
-
resource_factory: Callable[[], T],
|
|
215
|
-
validator: Optional[Callable[[T], bool]] = None,
|
|
216
|
-
cleanup_func: Optional[Callable[[T], None]] = None,
|
|
217
|
-
) -> None:
|
|
218
|
-
"""Create new resource pool."""
|
|
219
|
-
|
|
220
|
-
with self._lock:
|
|
221
|
-
if pool_config.pool_name in self._pools:
|
|
222
|
-
raise ValueError(f"Pool '{pool_config.pool_name}' already exists")
|
|
223
|
-
|
|
224
|
-
# Store configuration and factories
|
|
225
|
-
self._pool_configs[pool_config.pool_name] = pool_config
|
|
226
|
-
self._resource_factories[pool_config.pool_name] = resource_factory
|
|
227
|
-
if validator:
|
|
228
|
-
self._resource_validators[pool_config.pool_name] = validator
|
|
229
|
-
if cleanup_func:
|
|
230
|
-
self._resource_cleanup_funcs[pool_config.pool_name] = cleanup_func
|
|
231
|
-
|
|
232
|
-
# Create pool instance
|
|
233
|
-
pool = ResourcePoolInstance(
|
|
234
|
-
config=pool_config,
|
|
235
|
-
resource_factory=resource_factory,
|
|
236
|
-
validator=validator,
|
|
237
|
-
cleanup_func=cleanup_func,
|
|
238
|
-
dev_logger=self.dev_logger,
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
self._pools[pool_config.pool_name] = pool
|
|
242
|
-
|
|
243
|
-
# Initialize pool
|
|
244
|
-
asyncio.create_task(pool.initialize())
|
|
245
|
-
|
|
246
|
-
self._log_info(
|
|
247
|
-
f"Created resource pool '{pool_config.pool_name}' for {pool_config.resource_type.value}"
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
@asynccontextmanager
|
|
251
|
-
async def acquire_resource(
|
|
252
|
-
self,
|
|
253
|
-
pool_name: str,
|
|
254
|
-
timeout_seconds: Optional[float] = None,
|
|
255
|
-
client_id: Optional[str] = None,
|
|
256
|
-
):
|
|
257
|
-
"""Acquire resource from pool with context manager."""
|
|
258
|
-
|
|
259
|
-
if pool_name not in self._pools:
|
|
260
|
-
raise ValueError(f"Pool '{pool_name}' does not exist")
|
|
261
|
-
|
|
262
|
-
pool = self._pools[pool_name]
|
|
263
|
-
client_id = client_id or generate_correlation_id()
|
|
264
|
-
|
|
265
|
-
# Acquire resource
|
|
266
|
-
managed_resource = await pool.acquire_resource(client_id, timeout_seconds)
|
|
267
|
-
|
|
268
|
-
try:
|
|
269
|
-
yield managed_resource.resource
|
|
270
|
-
finally:
|
|
271
|
-
# Always return resource to pool
|
|
272
|
-
await pool.return_resource(managed_resource)
|
|
273
|
-
|
|
274
|
-
async def get_pool_status(self, pool_name: str) -> Optional[ResourcePoolDTO]:
|
|
275
|
-
"""Get pool status information."""
|
|
276
|
-
if pool_name not in self._pools:
|
|
277
|
-
return None
|
|
278
|
-
|
|
279
|
-
pool = self._pools[pool_name]
|
|
280
|
-
return await pool.get_status()
|
|
281
|
-
|
|
282
|
-
async def get_all_pools_status(self) -> List[ResourcePoolDTO]:
|
|
283
|
-
"""Get status for all pools."""
|
|
284
|
-
statuses = []
|
|
285
|
-
for pool in self._pools.values():
|
|
286
|
-
status = await pool.get_status()
|
|
287
|
-
statuses.append(status)
|
|
288
|
-
return statuses
|
|
289
|
-
|
|
290
|
-
def get_global_metrics(self) -> ConcurrencyMetrics:
|
|
291
|
-
"""Get global resource pool metrics."""
|
|
292
|
-
return self._global_metrics.model_copy()
|
|
293
|
-
|
|
294
|
-
async def _monitoring_loop(self) -> None:
|
|
295
|
-
"""Background monitoring loop."""
|
|
296
|
-
while not self._shutdown:
|
|
297
|
-
try:
|
|
298
|
-
await asyncio.sleep(30) # Monitor every 30 seconds
|
|
299
|
-
await self._collect_global_metrics()
|
|
300
|
-
await self._check_pool_health()
|
|
301
|
-
except asyncio.CancelledError:
|
|
302
|
-
break
|
|
303
|
-
except Exception as e:
|
|
304
|
-
logger.error(f"Error in resource pool monitoring: {e}")
|
|
305
|
-
|
|
306
|
-
async def _cleanup_loop(self) -> None:
|
|
307
|
-
"""Background cleanup loop."""
|
|
308
|
-
while not self._shutdown:
|
|
309
|
-
try:
|
|
310
|
-
await asyncio.sleep(60) # Cleanup every minute
|
|
311
|
-
await self._cleanup_pools()
|
|
312
|
-
except asyncio.CancelledError:
|
|
313
|
-
break
|
|
314
|
-
except Exception as e:
|
|
315
|
-
logger.error(f"Error in resource pool cleanup: {e}")
|
|
316
|
-
|
|
317
|
-
async def _scaling_loop(self) -> None:
|
|
318
|
-
"""Background scaling loop."""
|
|
319
|
-
while not self._shutdown:
|
|
320
|
-
try:
|
|
321
|
-
await asyncio.sleep(45) # Scale every 45 seconds
|
|
322
|
-
await self._auto_scale_pools()
|
|
323
|
-
except asyncio.CancelledError:
|
|
324
|
-
break
|
|
325
|
-
except Exception as e:
|
|
326
|
-
logger.error(f"Error in resource pool scaling: {e}")
|
|
327
|
-
|
|
328
|
-
async def _collect_global_metrics(self) -> None:
|
|
329
|
-
"""Collect global metrics from all pools."""
|
|
330
|
-
total_resources = 0
|
|
331
|
-
available_resources = 0
|
|
332
|
-
|
|
333
|
-
for pool in self._pools.values():
|
|
334
|
-
status = await pool.get_status()
|
|
335
|
-
total_resources += status.current_size
|
|
336
|
-
available_resources += status.available_resources
|
|
337
|
-
|
|
338
|
-
# Update global metrics
|
|
339
|
-
self._global_metrics.total_resources = total_resources
|
|
340
|
-
self._global_metrics.available_resources = available_resources
|
|
341
|
-
|
|
342
|
-
if total_resources > 0:
|
|
343
|
-
self._global_metrics.resource_utilization_percent = (
|
|
344
|
-
(total_resources - available_resources) / total_resources * 100
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
async def _check_pool_health(self) -> None:
|
|
348
|
-
"""Check health of all pools."""
|
|
349
|
-
for pool_name, pool in self._pools.items():
|
|
350
|
-
try:
|
|
351
|
-
await pool.health_check()
|
|
352
|
-
except Exception as e:
|
|
353
|
-
logger.error(f"Health check failed for pool '{pool_name}': {e}")
|
|
354
|
-
|
|
355
|
-
async def _cleanup_pools(self) -> None:
|
|
356
|
-
"""Cleanup all pools."""
|
|
357
|
-
for pool in self._pools.values():
|
|
358
|
-
await pool.cleanup()
|
|
359
|
-
|
|
360
|
-
async def _auto_scale_pools(self) -> None:
|
|
361
|
-
"""Auto-scale all pools based on their configurations."""
|
|
362
|
-
for pool in self._pools.values():
|
|
363
|
-
await pool.auto_scale()
|
|
364
|
-
|
|
365
|
-
def _log_info(self, message: str, **kwargs: Any) -> None:
|
|
366
|
-
"""Log info message."""
|
|
367
|
-
if self.dev_logger:
|
|
368
|
-
self.dev_logger.log_info(
|
|
369
|
-
SDKEventType.PERFORMANCE_OPTIMIZATION_APPLIED, message, **kwargs
|
|
370
|
-
)
|
|
371
|
-
else:
|
|
372
|
-
logger.info(message)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
class ResourcePoolInstance(Generic[T]):
|
|
376
|
-
"""Individual resource pool instance."""
|
|
377
|
-
|
|
378
|
-
def __init__(
|
|
379
|
-
self,
|
|
380
|
-
config: PoolConfig,
|
|
381
|
-
resource_factory: Callable[[], T],
|
|
382
|
-
validator: Optional[Callable[[T], bool]] = None,
|
|
383
|
-
cleanup_func: Optional[Callable[[T], None]] = None,
|
|
384
|
-
dev_logger: Optional["DevelopmentLogger"] = None,
|
|
385
|
-
):
|
|
386
|
-
self.config = config
|
|
387
|
-
self._resource_factory = resource_factory
|
|
388
|
-
self._validator = validator
|
|
389
|
-
self._cleanup_func = cleanup_func
|
|
390
|
-
self.dev_logger = dev_logger
|
|
391
|
-
|
|
392
|
-
# Resource storage
|
|
393
|
-
self._resources: Dict[str, ManagedResource[T]] = {}
|
|
394
|
-
self._available: deque[str] = deque()
|
|
395
|
-
self._in_use: Set[str] = set()
|
|
396
|
-
|
|
397
|
-
# Acquisition queue
|
|
398
|
-
self._acquisition_queue: deque[asyncio.Future] = deque()
|
|
399
|
-
|
|
400
|
-
# Metrics
|
|
401
|
-
self._metrics = ResourcePoolDTO(
|
|
402
|
-
pool_id=config.pool_name,
|
|
403
|
-
pool_name=config.pool_name,
|
|
404
|
-
resource_type=config.resource_type,
|
|
405
|
-
min_size=config.min_size,
|
|
406
|
-
max_size=config.max_size,
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
# Performance tracking
|
|
410
|
-
self._acquisition_times: deque[float] = deque(maxlen=100)
|
|
411
|
-
self._last_scale_time: Optional[datetime] = None
|
|
412
|
-
|
|
413
|
-
# Thread safety
|
|
414
|
-
self._lock = asyncio.Lock()
|
|
415
|
-
|
|
416
|
-
# Initialization state
|
|
417
|
-
self._initialized = False
|
|
418
|
-
|
|
419
|
-
async def initialize(self) -> None:
|
|
420
|
-
"""Initialize pool with initial resources."""
|
|
421
|
-
async with self._lock:
|
|
422
|
-
if self._initialized:
|
|
423
|
-
return
|
|
424
|
-
|
|
425
|
-
# Create initial resources
|
|
426
|
-
for i in range(self.config.initial_size):
|
|
427
|
-
try:
|
|
428
|
-
resource_id = await self._create_resource()
|
|
429
|
-
if resource_id:
|
|
430
|
-
self._available.append(resource_id)
|
|
431
|
-
except Exception as e:
|
|
432
|
-
logger.error(f"Failed to create initial resource {i}: {e}")
|
|
433
|
-
|
|
434
|
-
# Warmup if enabled
|
|
435
|
-
if self.config.enable_warmup:
|
|
436
|
-
await self._warmup_pool()
|
|
437
|
-
|
|
438
|
-
self._initialized = True
|
|
439
|
-
self._update_metrics()
|
|
440
|
-
|
|
441
|
-
async def acquire_resource(
|
|
442
|
-
self,
|
|
443
|
-
client_id: str,
|
|
444
|
-
timeout_seconds: Optional[float] = None,
|
|
445
|
-
) -> ManagedResource[T]:
|
|
446
|
-
"""Acquire resource from pool."""
|
|
447
|
-
|
|
448
|
-
timeout_seconds = timeout_seconds or self.config.acquisition_timeout_seconds
|
|
449
|
-
start_time = time.time()
|
|
450
|
-
|
|
451
|
-
try:
|
|
452
|
-
# Try to get available resource
|
|
453
|
-
async with asyncio.wait_for(self._lock, timeout=timeout_seconds):
|
|
454
|
-
|
|
455
|
-
# Check for available resources
|
|
456
|
-
while self._available:
|
|
457
|
-
resource_id = self._available.popleft()
|
|
458
|
-
managed_resource = self._resources[resource_id]
|
|
459
|
-
|
|
460
|
-
# Validate resource if configured
|
|
461
|
-
if self.config.validation_on_acquire and not managed_resource.is_valid():
|
|
462
|
-
await self._destroy_resource(resource_id)
|
|
463
|
-
continue
|
|
464
|
-
|
|
465
|
-
# Acquire resource
|
|
466
|
-
managed_resource.acquire(client_id)
|
|
467
|
-
self._in_use.add(resource_id)
|
|
468
|
-
|
|
469
|
-
# Track acquisition time
|
|
470
|
-
acquisition_time = (time.time() - start_time) * 1000
|
|
471
|
-
self._acquisition_times.append(acquisition_time)
|
|
472
|
-
|
|
473
|
-
self._update_metrics()
|
|
474
|
-
return managed_resource
|
|
475
|
-
|
|
476
|
-
# No available resources, try to create new one
|
|
477
|
-
if len(self._resources) < self.config.max_size:
|
|
478
|
-
resource_id = await self._create_resource()
|
|
479
|
-
if resource_id:
|
|
480
|
-
managed_resource = self._resources[resource_id]
|
|
481
|
-
managed_resource.acquire(client_id)
|
|
482
|
-
self._in_use.add(resource_id)
|
|
483
|
-
|
|
484
|
-
acquisition_time = (time.time() - start_time) * 1000
|
|
485
|
-
self._acquisition_times.append(acquisition_time)
|
|
486
|
-
|
|
487
|
-
self._update_metrics()
|
|
488
|
-
return managed_resource
|
|
489
|
-
|
|
490
|
-
# Pool exhausted, add to queue
|
|
491
|
-
future = asyncio.Future()
|
|
492
|
-
self._acquisition_queue.append(future)
|
|
493
|
-
|
|
494
|
-
# Wait for resource to become available
|
|
495
|
-
try:
|
|
496
|
-
managed_resource = await asyncio.wait_for(future, timeout=timeout_seconds)
|
|
497
|
-
managed_resource.acquire(client_id)
|
|
498
|
-
|
|
499
|
-
acquisition_time = (time.time() - start_time) * 1000
|
|
500
|
-
self._acquisition_times.append(acquisition_time)
|
|
501
|
-
|
|
502
|
-
return managed_resource
|
|
503
|
-
|
|
504
|
-
except asyncio.TimeoutError:
|
|
505
|
-
# Remove from queue if still there
|
|
506
|
-
try:
|
|
507
|
-
self._acquisition_queue.remove(future)
|
|
508
|
-
except ValueError:
|
|
509
|
-
pass
|
|
510
|
-
raise
|
|
511
|
-
|
|
512
|
-
except asyncio.TimeoutError:
|
|
513
|
-
self._metrics.failed_acquisitions += 1
|
|
514
|
-
raise RuntimeError(
|
|
515
|
-
f"Failed to acquire resource from pool '{self.config.pool_name}' within {timeout_seconds}s"
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
async def return_resource(self, managed_resource: ManagedResource[T]) -> None:
|
|
519
|
-
"""Return resource to pool."""
|
|
520
|
-
|
|
521
|
-
async with self._lock:
|
|
522
|
-
resource_id = managed_resource.metadata.resource_id
|
|
523
|
-
|
|
524
|
-
if resource_id not in self._resources:
|
|
525
|
-
return # Resource was already destroyed
|
|
526
|
-
|
|
527
|
-
# Validate resource if configured
|
|
528
|
-
if self.config.validation_on_return and not managed_resource.is_valid():
|
|
529
|
-
await self._destroy_resource(resource_id)
|
|
530
|
-
return
|
|
531
|
-
|
|
532
|
-
# Release resource
|
|
533
|
-
managed_resource.release()
|
|
534
|
-
self._in_use.discard(resource_id)
|
|
535
|
-
|
|
536
|
-
# Check if someone is waiting
|
|
537
|
-
if self._acquisition_queue:
|
|
538
|
-
future = self._acquisition_queue.popleft()
|
|
539
|
-
if not future.cancelled():
|
|
540
|
-
future.set_result(managed_resource)
|
|
541
|
-
return
|
|
542
|
-
|
|
543
|
-
# Return to available pool
|
|
544
|
-
self._available.append(resource_id)
|
|
545
|
-
self._update_metrics()
|
|
546
|
-
|
|
547
|
-
async def _create_resource(self) -> Optional[str]:
|
|
548
|
-
"""Create new resource."""
|
|
549
|
-
try:
|
|
550
|
-
# Create resource using factory
|
|
551
|
-
resource = self._resource_factory()
|
|
552
|
-
resource_id = str(uuid.uuid4())
|
|
553
|
-
|
|
554
|
-
# Create metadata
|
|
555
|
-
metadata = ResourceMetadata(
|
|
556
|
-
resource_id=resource_id,
|
|
557
|
-
resource_type=self.config.resource_type,
|
|
558
|
-
created_at=datetime.now(timezone.utc),
|
|
559
|
-
last_used=datetime.now(timezone.utc),
|
|
560
|
-
lifecycle_state=ResourceLifecycleState.READY,
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
# Create managed resource
|
|
564
|
-
managed_resource = ManagedResource(
|
|
565
|
-
resource=resource,
|
|
566
|
-
metadata=metadata,
|
|
567
|
-
validator=self._validator,
|
|
568
|
-
cleanup_func=self._cleanup_func,
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
self._resources[resource_id] = managed_resource
|
|
572
|
-
return resource_id
|
|
573
|
-
|
|
574
|
-
except Exception as e:
|
|
575
|
-
logger.error(f"Failed to create resource: {e}")
|
|
576
|
-
return None
|
|
577
|
-
|
|
578
|
-
async def _destroy_resource(self, resource_id: str) -> None:
|
|
579
|
-
"""Destroy resource and clean up."""
|
|
580
|
-
if resource_id in self._resources:
|
|
581
|
-
managed_resource = self._resources[resource_id]
|
|
582
|
-
|
|
583
|
-
# Clean up resource
|
|
584
|
-
try:
|
|
585
|
-
managed_resource.cleanup()
|
|
586
|
-
except Exception as e:
|
|
587
|
-
logger.error(f"Error during resource cleanup: {e}")
|
|
588
|
-
|
|
589
|
-
# Remove from all collections
|
|
590
|
-
del self._resources[resource_id]
|
|
591
|
-
self._in_use.discard(resource_id)
|
|
592
|
-
|
|
593
|
-
# Remove from available queue if present
|
|
594
|
-
try:
|
|
595
|
-
self._available.remove(resource_id)
|
|
596
|
-
except ValueError:
|
|
597
|
-
pass
|
|
598
|
-
|
|
599
|
-
async def _warmup_pool(self) -> None:
|
|
600
|
-
"""Warm up pool by pre-creating resources."""
|
|
601
|
-
warmup_count = min(self.config.warmup_size, self.config.max_size - len(self._resources))
|
|
602
|
-
|
|
603
|
-
for _ in range(warmup_count):
|
|
604
|
-
resource_id = await self._create_resource()
|
|
605
|
-
if resource_id:
|
|
606
|
-
self._available.append(resource_id)
|
|
607
|
-
|
|
608
|
-
async def health_check(self) -> None:
|
|
609
|
-
"""Perform health check on pool resources."""
|
|
610
|
-
if not self.config.enable_health_checks:
|
|
611
|
-
return
|
|
612
|
-
|
|
613
|
-
unhealthy_resources = []
|
|
614
|
-
|
|
615
|
-
async with self._lock:
|
|
616
|
-
for resource_id, managed_resource in self._resources.items():
|
|
617
|
-
if resource_id not in self._in_use: # Only check idle resources
|
|
618
|
-
if not managed_resource.is_valid():
|
|
619
|
-
unhealthy_resources.append(resource_id)
|
|
620
|
-
|
|
621
|
-
# Remove unhealthy resources
|
|
622
|
-
for resource_id in unhealthy_resources:
|
|
623
|
-
await self._destroy_resource(resource_id)
|
|
624
|
-
|
|
625
|
-
if unhealthy_resources:
|
|
626
|
-
logger.info(
|
|
627
|
-
f"Removed {len(unhealthy_resources)} unhealthy resources from pool '{self.config.pool_name}'"
|
|
628
|
-
)
|
|
629
|
-
|
|
630
|
-
async def auto_scale(self) -> None:
|
|
631
|
-
"""Auto-scale pool based on configuration."""
|
|
632
|
-
if self.config.scaling_strategy == PoolScalingStrategy.FIXED:
|
|
633
|
-
return
|
|
634
|
-
|
|
635
|
-
async with self._lock:
|
|
636
|
-
current_size = len(self._resources)
|
|
637
|
-
available_count = len(self._available)
|
|
638
|
-
in_use_count = len(self._in_use)
|
|
639
|
-
|
|
640
|
-
utilization = in_use_count / current_size if current_size > 0 else 0.0
|
|
641
|
-
|
|
642
|
-
# Check if scaling is needed
|
|
643
|
-
should_scale_up = (
|
|
644
|
-
utilization > self.config.scale_up_threshold
|
|
645
|
-
and current_size < self.config.max_size
|
|
646
|
-
and available_count == 0
|
|
647
|
-
)
|
|
648
|
-
|
|
649
|
-
should_scale_down = (
|
|
650
|
-
utilization < self.config.scale_down_threshold
|
|
651
|
-
and current_size > self.config.min_size
|
|
652
|
-
and available_count > (current_size * 0.5) # More than 50% are idle
|
|
653
|
-
)
|
|
654
|
-
|
|
655
|
-
# Scale up
|
|
656
|
-
if should_scale_up:
|
|
657
|
-
new_size = min(int(current_size * self.config.scale_factor), self.config.max_size)
|
|
658
|
-
resources_to_add = new_size - current_size
|
|
659
|
-
|
|
660
|
-
for _ in range(resources_to_add):
|
|
661
|
-
resource_id = await self._create_resource()
|
|
662
|
-
if resource_id:
|
|
663
|
-
self._available.append(resource_id)
|
|
664
|
-
|
|
665
|
-
self._last_scale_time = datetime.now(timezone.utc)
|
|
666
|
-
logger.info(
|
|
667
|
-
f"Scaled up pool '{self.config.pool_name}' from {current_size} to {len(self._resources)}"
|
|
668
|
-
)
|
|
669
|
-
|
|
670
|
-
# Scale down
|
|
671
|
-
elif should_scale_down:
|
|
672
|
-
new_size = max(int(current_size / self.config.scale_factor), self.config.min_size)
|
|
673
|
-
resources_to_remove = current_size - new_size
|
|
674
|
-
|
|
675
|
-
# Remove only idle resources
|
|
676
|
-
removed_count = 0
|
|
677
|
-
while removed_count < resources_to_remove and self._available:
|
|
678
|
-
resource_id = self._available.popleft()
|
|
679
|
-
await self._destroy_resource(resource_id)
|
|
680
|
-
removed_count += 1
|
|
681
|
-
|
|
682
|
-
if removed_count > 0:
|
|
683
|
-
self._last_scale_time = datetime.now(timezone.utc)
|
|
684
|
-
logger.info(
|
|
685
|
-
f"Scaled down pool '{self.config.pool_name}' by {removed_count} resources"
|
|
686
|
-
)
|
|
687
|
-
|
|
688
|
-
async def cleanup(self) -> None:
|
|
689
|
-
"""Cleanup idle and expired resources."""
|
|
690
|
-
expired_resources = []
|
|
691
|
-
|
|
692
|
-
async with self._lock:
|
|
693
|
-
current_time = datetime.now(timezone.utc)
|
|
694
|
-
|
|
695
|
-
for resource_id, managed_resource in self._resources.items():
|
|
696
|
-
if resource_id not in self._in_use: # Only cleanup idle resources
|
|
697
|
-
metadata = managed_resource.metadata
|
|
698
|
-
|
|
699
|
-
# Check idle timeout
|
|
700
|
-
idle_time = (current_time - metadata.last_used).total_seconds()
|
|
701
|
-
if idle_time > self.config.idle_timeout_seconds:
|
|
702
|
-
expired_resources.append(resource_id)
|
|
703
|
-
|
|
704
|
-
# Check max lifetime
|
|
705
|
-
elif (
|
|
706
|
-
self.config.max_lifetime_seconds
|
|
707
|
-
and (current_time - metadata.created_at).total_seconds()
|
|
708
|
-
> self.config.max_lifetime_seconds
|
|
709
|
-
):
|
|
710
|
-
expired_resources.append(resource_id)
|
|
711
|
-
|
|
712
|
-
# Remove expired resources
|
|
713
|
-
for resource_id in expired_resources:
|
|
714
|
-
await self._destroy_resource(resource_id)
|
|
715
|
-
|
|
716
|
-
if expired_resources:
|
|
717
|
-
logger.info(
|
|
718
|
-
f"Cleaned up {len(expired_resources)} expired resources from pool '{self.config.pool_name}'"
|
|
719
|
-
)
|
|
720
|
-
|
|
721
|
-
async def get_status(self) -> ResourcePoolDTO:
|
|
722
|
-
"""Get current pool status."""
|
|
723
|
-
self._update_metrics()
|
|
724
|
-
return self._metrics.model_copy()
|
|
725
|
-
|
|
726
|
-
async def shutdown(self) -> None:
|
|
727
|
-
"""Shutdown pool and cleanup all resources."""
|
|
728
|
-
async with self._lock:
|
|
729
|
-
# Cancel all waiting acquisitions
|
|
730
|
-
while self._acquisition_queue:
|
|
731
|
-
future = self._acquisition_queue.popleft()
|
|
732
|
-
if not future.cancelled():
|
|
733
|
-
future.cancel()
|
|
734
|
-
|
|
735
|
-
# Destroy all resources
|
|
736
|
-
for resource_id in list(self._resources.keys()):
|
|
737
|
-
await self._destroy_resource(resource_id)
|
|
738
|
-
|
|
739
|
-
self._available.clear()
|
|
740
|
-
self._in_use.clear()
|
|
741
|
-
|
|
742
|
-
def _update_metrics(self) -> None:
|
|
743
|
-
"""Update pool metrics."""
|
|
744
|
-
self._metrics.current_size = len(self._resources)
|
|
745
|
-
self._metrics.available_resources = len(self._available)
|
|
746
|
-
self._metrics.in_use_resources = len(self._in_use)
|
|
747
|
-
self._metrics.total_requests += 1
|
|
748
|
-
|
|
749
|
-
if self._acquisition_times:
|
|
750
|
-
self._metrics.avg_acquisition_time_ms = sum(self._acquisition_times) / len(
|
|
751
|
-
self._acquisition_times
|
|
752
|
-
)
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
__all__ = [
|
|
756
|
-
# Main classes
|
|
757
|
-
"ResourcePoolManager",
|
|
758
|
-
"ResourcePoolInstance",
|
|
759
|
-
"ManagedResource",
|
|
760
|
-
# Note: Resource pooling models are available via DTO imports:
|
|
761
|
-
# from unrealon_sdk.src.dto.resource_pooling import ...
|
|
762
|
-
# from unrealon_sdk.src.dto.concurrency import ...
|
|
763
|
-
]
|