kailash 0.9.15__py3-none-any.whl → 0.9.17__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.
- kailash/__init__.py +4 -3
- kailash/middleware/database/base_models.py +7 -1
- kailash/migration/__init__.py +30 -0
- kailash/migration/cli.py +340 -0
- kailash/migration/compatibility_checker.py +662 -0
- kailash/migration/configuration_validator.py +837 -0
- kailash/migration/documentation_generator.py +1828 -0
- kailash/migration/examples/__init__.py +5 -0
- kailash/migration/examples/complete_migration_example.py +692 -0
- kailash/migration/migration_assistant.py +715 -0
- kailash/migration/performance_comparator.py +760 -0
- kailash/migration/regression_detector.py +1141 -0
- kailash/migration/tests/__init__.py +6 -0
- kailash/migration/tests/test_compatibility_checker.py +403 -0
- kailash/migration/tests/test_integration.py +463 -0
- kailash/migration/tests/test_migration_assistant.py +397 -0
- kailash/migration/tests/test_performance_comparator.py +433 -0
- kailash/monitoring/__init__.py +29 -2
- kailash/monitoring/asyncsql_metrics.py +275 -0
- kailash/nodes/data/async_sql.py +1828 -33
- kailash/runtime/local.py +1255 -8
- kailash/runtime/monitoring/__init__.py +1 -0
- kailash/runtime/monitoring/runtime_monitor.py +780 -0
- kailash/runtime/resource_manager.py +3033 -0
- kailash/sdk_exceptions.py +21 -0
- kailash/workflow/cyclic_runner.py +18 -2
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/METADATA +1 -1
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/RECORD +33 -14
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/WHEEL +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/entry_points.txt +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/licenses/NOTICE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,275 @@
|
|
1
|
+
"""
|
2
|
+
Prometheus metrics integration for AsyncSQL lock contention monitoring.
|
3
|
+
|
4
|
+
This module provides easy-to-use Prometheus metrics for monitoring AsyncSQL
|
5
|
+
per-pool locking performance and contention patterns.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import time
|
9
|
+
from typing import Optional, Dict, Any
|
10
|
+
from contextlib import asynccontextmanager
|
11
|
+
|
12
|
+
try:
|
13
|
+
import prometheus_client
|
14
|
+
PROMETHEUS_AVAILABLE = True
|
15
|
+
except ImportError:
|
16
|
+
PROMETHEUS_AVAILABLE = False
|
17
|
+
|
18
|
+
|
19
|
+
class AsyncSQLMetrics:
|
20
|
+
"""Prometheus metrics collector for AsyncSQL lock contention monitoring."""
|
21
|
+
|
22
|
+
def __init__(self, enabled: bool = True, registry: Optional[prometheus_client.CollectorRegistry] = None):
|
23
|
+
"""
|
24
|
+
Initialize AsyncSQL metrics collector.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
enabled: Whether to collect metrics (disabled if prometheus_client not available)
|
28
|
+
registry: Custom Prometheus registry (uses default if None)
|
29
|
+
"""
|
30
|
+
self.enabled = enabled and PROMETHEUS_AVAILABLE
|
31
|
+
self.registry = registry or prometheus_client.REGISTRY
|
32
|
+
|
33
|
+
if not self.enabled:
|
34
|
+
return
|
35
|
+
|
36
|
+
# Lock acquisition counter
|
37
|
+
self.lock_acquisition_counter = prometheus_client.Counter(
|
38
|
+
'asyncsql_lock_acquisitions_total',
|
39
|
+
'Total number of AsyncSQL lock acquisitions',
|
40
|
+
['pool_key', 'status'], # status: success, timeout, error
|
41
|
+
registry=self.registry
|
42
|
+
)
|
43
|
+
|
44
|
+
# Lock wait time histogram
|
45
|
+
self.lock_wait_time_histogram = prometheus_client.Histogram(
|
46
|
+
'asyncsql_lock_wait_seconds',
|
47
|
+
'Time spent waiting for AsyncSQL locks',
|
48
|
+
['pool_key'],
|
49
|
+
buckets=(0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, float('inf')),
|
50
|
+
registry=self.registry
|
51
|
+
)
|
52
|
+
|
53
|
+
# Active locks gauge
|
54
|
+
self.active_locks_gauge = prometheus_client.Gauge(
|
55
|
+
'asyncsql_active_locks',
|
56
|
+
'Number of currently active AsyncSQL locks',
|
57
|
+
['pool_key'],
|
58
|
+
registry=self.registry
|
59
|
+
)
|
60
|
+
|
61
|
+
# Pool operations counter
|
62
|
+
self.pool_operations_counter = prometheus_client.Counter(
|
63
|
+
'asyncsql_pool_operations_total',
|
64
|
+
'Total number of AsyncSQL pool operations',
|
65
|
+
['pool_key', 'operation'], # operation: create, cleanup, acquire, release
|
66
|
+
registry=self.registry
|
67
|
+
)
|
68
|
+
|
69
|
+
# Lock contention summary
|
70
|
+
self.lock_contention_summary = prometheus_client.Summary(
|
71
|
+
'asyncsql_lock_contention_seconds',
|
72
|
+
'Summary of AsyncSQL lock contention patterns',
|
73
|
+
['pool_key'],
|
74
|
+
registry=self.registry
|
75
|
+
)
|
76
|
+
|
77
|
+
def record_lock_acquisition(self, pool_key: str, status: str, wait_time: float = 0.0):
|
78
|
+
"""
|
79
|
+
Record a lock acquisition event.
|
80
|
+
|
81
|
+
Args:
|
82
|
+
pool_key: The pool key for the lock
|
83
|
+
status: 'success', 'timeout', or 'error'
|
84
|
+
wait_time: Time spent waiting for the lock in seconds
|
85
|
+
"""
|
86
|
+
if not self.enabled:
|
87
|
+
return
|
88
|
+
|
89
|
+
self.lock_acquisition_counter.labels(pool_key=pool_key, status=status).inc()
|
90
|
+
|
91
|
+
if wait_time > 0:
|
92
|
+
self.lock_wait_time_histogram.labels(pool_key=pool_key).observe(wait_time)
|
93
|
+
self.lock_contention_summary.labels(pool_key=pool_key).observe(wait_time)
|
94
|
+
|
95
|
+
def set_active_locks(self, pool_key: str, count: int):
|
96
|
+
"""
|
97
|
+
Update the count of active locks for a pool.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
pool_key: The pool key
|
101
|
+
count: Number of active locks
|
102
|
+
"""
|
103
|
+
if not self.enabled:
|
104
|
+
return
|
105
|
+
|
106
|
+
self.active_locks_gauge.labels(pool_key=pool_key).set(count)
|
107
|
+
|
108
|
+
def record_pool_operation(self, pool_key: str, operation: str):
|
109
|
+
"""
|
110
|
+
Record a pool operation event.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
pool_key: The pool key
|
114
|
+
operation: 'create', 'cleanup', 'acquire', 'release'
|
115
|
+
"""
|
116
|
+
if not self.enabled:
|
117
|
+
return
|
118
|
+
|
119
|
+
self.pool_operations_counter.labels(pool_key=pool_key, operation=operation).inc()
|
120
|
+
|
121
|
+
@asynccontextmanager
|
122
|
+
async def timed_lock_acquisition(self, pool_key: str):
|
123
|
+
"""
|
124
|
+
Context manager to time lock acquisition and automatically record metrics.
|
125
|
+
|
126
|
+
Usage:
|
127
|
+
async with metrics.timed_lock_acquisition('my_pool_key'):
|
128
|
+
# Lock acquisition logic here
|
129
|
+
async with some_lock:
|
130
|
+
# Work while holding lock
|
131
|
+
pass
|
132
|
+
"""
|
133
|
+
start_time = time.time()
|
134
|
+
status = 'error'
|
135
|
+
|
136
|
+
try:
|
137
|
+
yield
|
138
|
+
status = 'success'
|
139
|
+
except Exception as e:
|
140
|
+
if 'timeout' in str(e).lower():
|
141
|
+
status = 'timeout'
|
142
|
+
else:
|
143
|
+
status = 'error'
|
144
|
+
raise
|
145
|
+
finally:
|
146
|
+
wait_time = time.time() - start_time
|
147
|
+
self.record_lock_acquisition(pool_key, status, wait_time)
|
148
|
+
|
149
|
+
|
150
|
+
# Global metrics instance (can be overridden)
|
151
|
+
_global_metrics: Optional[AsyncSQLMetrics] = None
|
152
|
+
|
153
|
+
|
154
|
+
def get_global_metrics() -> Optional[AsyncSQLMetrics]:
|
155
|
+
"""Get the global AsyncSQL metrics instance."""
|
156
|
+
global _global_metrics
|
157
|
+
if _global_metrics is None and PROMETHEUS_AVAILABLE:
|
158
|
+
_global_metrics = AsyncSQLMetrics()
|
159
|
+
return _global_metrics
|
160
|
+
|
161
|
+
|
162
|
+
def set_global_metrics(metrics: Optional[AsyncSQLMetrics]):
|
163
|
+
"""Set the global AsyncSQL metrics instance."""
|
164
|
+
global _global_metrics
|
165
|
+
_global_metrics = metrics
|
166
|
+
|
167
|
+
|
168
|
+
def enable_metrics(registry: Optional[prometheus_client.CollectorRegistry] = None) -> AsyncSQLMetrics:
|
169
|
+
"""
|
170
|
+
Enable global AsyncSQL metrics collection.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
registry: Custom Prometheus registry (uses default if None)
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
The configured metrics instance
|
177
|
+
"""
|
178
|
+
metrics = AsyncSQLMetrics(enabled=True, registry=registry)
|
179
|
+
set_global_metrics(metrics)
|
180
|
+
return metrics
|
181
|
+
|
182
|
+
|
183
|
+
def disable_metrics():
|
184
|
+
"""Disable global AsyncSQL metrics collection."""
|
185
|
+
set_global_metrics(None)
|
186
|
+
|
187
|
+
|
188
|
+
# Convenience functions for manual metric recording
|
189
|
+
def record_lock_acquisition(pool_key: str, status: str, wait_time: float = 0.0):
|
190
|
+
"""Record a lock acquisition event using global metrics."""
|
191
|
+
metrics = get_global_metrics()
|
192
|
+
if metrics:
|
193
|
+
metrics.record_lock_acquisition(pool_key, status, wait_time)
|
194
|
+
|
195
|
+
|
196
|
+
def record_pool_operation(pool_key: str, operation: str):
|
197
|
+
"""Record a pool operation event using global metrics."""
|
198
|
+
metrics = get_global_metrics()
|
199
|
+
if metrics:
|
200
|
+
metrics.record_pool_operation(pool_key, operation)
|
201
|
+
|
202
|
+
|
203
|
+
def set_active_locks(pool_key: str, count: int):
|
204
|
+
"""Update active locks count using global metrics."""
|
205
|
+
metrics = get_global_metrics()
|
206
|
+
if metrics:
|
207
|
+
metrics.set_active_locks(pool_key, count)
|
208
|
+
|
209
|
+
|
210
|
+
# Integration example for AsyncSQLDatabaseNode
|
211
|
+
def integrate_with_async_sql():
|
212
|
+
"""
|
213
|
+
Example of how to integrate metrics with AsyncSQLDatabaseNode.
|
214
|
+
|
215
|
+
This would typically be called during AsyncSQL initialization or
|
216
|
+
through a configuration setting.
|
217
|
+
"""
|
218
|
+
if not PROMETHEUS_AVAILABLE:
|
219
|
+
return None
|
220
|
+
|
221
|
+
# Enable metrics
|
222
|
+
metrics = enable_metrics()
|
223
|
+
|
224
|
+
# Example: monkey-patch AsyncSQL methods to include metrics
|
225
|
+
# (This is just an example - actual integration would be cleaner)
|
226
|
+
from kailash.nodes.data.async_sql import AsyncSQLDatabaseNode
|
227
|
+
|
228
|
+
# Store original methods
|
229
|
+
original_get_pool_creation_lock = AsyncSQLDatabaseNode._get_pool_creation_lock
|
230
|
+
original_acquire_lock = AsyncSQLDatabaseNode._acquire_pool_lock_with_timeout
|
231
|
+
|
232
|
+
@classmethod
|
233
|
+
def instrumented_get_pool_creation_lock(cls, pool_key: str):
|
234
|
+
"""Instrumented version that records pool operations."""
|
235
|
+
record_pool_operation(pool_key, 'acquire')
|
236
|
+
return original_get_pool_creation_lock(pool_key)
|
237
|
+
|
238
|
+
@classmethod
|
239
|
+
async def instrumented_acquire_lock(cls, pool_key: str, timeout: float = 5.0):
|
240
|
+
"""Instrumented version that records lock acquisitions."""
|
241
|
+
async with metrics.timed_lock_acquisition(pool_key):
|
242
|
+
async with original_acquire_lock(pool_key, timeout):
|
243
|
+
yield
|
244
|
+
|
245
|
+
# Apply instrumentation
|
246
|
+
AsyncSQLDatabaseNode._get_pool_creation_lock = instrumented_get_pool_creation_lock
|
247
|
+
AsyncSQLDatabaseNode._acquire_pool_lock_with_timeout = instrumented_acquire_lock
|
248
|
+
|
249
|
+
return metrics
|
250
|
+
|
251
|
+
|
252
|
+
if __name__ == "__main__":
|
253
|
+
# Example usage
|
254
|
+
print("AsyncSQL Metrics Module")
|
255
|
+
print(f"Prometheus available: {PROMETHEUS_AVAILABLE}")
|
256
|
+
|
257
|
+
if PROMETHEUS_AVAILABLE:
|
258
|
+
# Enable metrics
|
259
|
+
metrics = enable_metrics()
|
260
|
+
|
261
|
+
# Simulate some metrics
|
262
|
+
metrics.record_lock_acquisition('test_pool_1', 'success', 0.005)
|
263
|
+
metrics.record_lock_acquisition('test_pool_1', 'success', 0.003)
|
264
|
+
metrics.record_lock_acquisition('test_pool_2', 'timeout', 5.0)
|
265
|
+
metrics.set_active_locks('test_pool_1', 2)
|
266
|
+
metrics.record_pool_operation('test_pool_1', 'create')
|
267
|
+
|
268
|
+
print("Metrics recorded successfully")
|
269
|
+
print("Access metrics at: http://localhost:8000/metrics")
|
270
|
+
print("(Start prometheus_client HTTP server to view metrics)")
|
271
|
+
|
272
|
+
# Start metrics server (for testing)
|
273
|
+
# prometheus_client.start_http_server(8000)
|
274
|
+
else:
|
275
|
+
print("Install prometheus_client to enable metrics: pip install prometheus_client")
|