kailash 0.8.3__py3-none-any.whl → 0.8.5__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 +1 -7
- kailash/cli/__init__.py +11 -1
- kailash/cli/validation_audit.py +570 -0
- kailash/core/actors/supervisor.py +1 -1
- kailash/core/resilience/circuit_breaker.py +71 -1
- kailash/core/resilience/health_monitor.py +172 -0
- kailash/edge/compliance.py +33 -0
- kailash/edge/consistency.py +609 -0
- kailash/edge/coordination/__init__.py +30 -0
- kailash/edge/coordination/global_ordering.py +355 -0
- kailash/edge/coordination/leader_election.py +217 -0
- kailash/edge/coordination/partition_detector.py +296 -0
- kailash/edge/coordination/raft.py +485 -0
- kailash/edge/discovery.py +63 -1
- kailash/edge/migration/__init__.py +19 -0
- kailash/edge/migration/edge_migrator.py +832 -0
- kailash/edge/monitoring/__init__.py +21 -0
- kailash/edge/monitoring/edge_monitor.py +736 -0
- kailash/edge/prediction/__init__.py +10 -0
- kailash/edge/prediction/predictive_warmer.py +591 -0
- kailash/edge/resource/__init__.py +102 -0
- kailash/edge/resource/cloud_integration.py +796 -0
- kailash/edge/resource/cost_optimizer.py +949 -0
- kailash/edge/resource/docker_integration.py +919 -0
- kailash/edge/resource/kubernetes_integration.py +893 -0
- kailash/edge/resource/platform_integration.py +913 -0
- kailash/edge/resource/predictive_scaler.py +959 -0
- kailash/edge/resource/resource_analyzer.py +824 -0
- kailash/edge/resource/resource_pools.py +610 -0
- kailash/integrations/dataflow_edge.py +261 -0
- kailash/mcp_server/registry_integration.py +1 -1
- kailash/monitoring/__init__.py +18 -0
- kailash/monitoring/alerts.py +646 -0
- kailash/monitoring/metrics.py +677 -0
- kailash/nodes/__init__.py +2 -0
- kailash/nodes/ai/__init__.py +17 -0
- kailash/nodes/ai/a2a.py +1914 -43
- kailash/nodes/ai/a2a_backup.py +1807 -0
- kailash/nodes/ai/hybrid_search.py +972 -0
- kailash/nodes/ai/semantic_memory.py +558 -0
- kailash/nodes/ai/streaming_analytics.py +947 -0
- kailash/nodes/base.py +545 -0
- kailash/nodes/edge/__init__.py +36 -0
- kailash/nodes/edge/base.py +240 -0
- kailash/nodes/edge/cloud_node.py +710 -0
- kailash/nodes/edge/coordination.py +239 -0
- kailash/nodes/edge/docker_node.py +825 -0
- kailash/nodes/edge/edge_data.py +582 -0
- kailash/nodes/edge/edge_migration_node.py +392 -0
- kailash/nodes/edge/edge_monitoring_node.py +421 -0
- kailash/nodes/edge/edge_state.py +673 -0
- kailash/nodes/edge/edge_warming_node.py +393 -0
- kailash/nodes/edge/kubernetes_node.py +652 -0
- kailash/nodes/edge/platform_node.py +766 -0
- kailash/nodes/edge/resource_analyzer_node.py +378 -0
- kailash/nodes/edge/resource_optimizer_node.py +501 -0
- kailash/nodes/edge/resource_scaler_node.py +397 -0
- kailash/nodes/ports.py +676 -0
- kailash/runtime/local.py +344 -1
- kailash/runtime/validation/__init__.py +20 -0
- kailash/runtime/validation/connection_context.py +119 -0
- kailash/runtime/validation/enhanced_error_formatter.py +202 -0
- kailash/runtime/validation/error_categorizer.py +164 -0
- kailash/runtime/validation/metrics.py +380 -0
- kailash/runtime/validation/performance.py +615 -0
- kailash/runtime/validation/suggestion_engine.py +212 -0
- kailash/testing/fixtures.py +2 -2
- kailash/workflow/builder.py +234 -8
- kailash/workflow/contracts.py +418 -0
- kailash/workflow/edge_infrastructure.py +369 -0
- kailash/workflow/migration.py +3 -3
- kailash/workflow/type_inference.py +669 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/METADATA +44 -27
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/RECORD +78 -28
- kailash/nexus/__init__.py +0 -21
- kailash/nexus/cli/__init__.py +0 -5
- kailash/nexus/cli/__main__.py +0 -6
- kailash/nexus/cli/main.py +0 -176
- kailash/nexus/factory.py +0 -413
- kailash/nexus/gateway.py +0 -545
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/WHEEL +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/entry_points.txt +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,615 @@
|
|
1
|
+
"""
|
2
|
+
Performance optimization for connection validation.
|
3
|
+
|
4
|
+
Provides advanced caching, lazy evaluation, batch validation,
|
5
|
+
and performance benchmarking for the validation system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import logging
|
10
|
+
import time
|
11
|
+
import weakref
|
12
|
+
from collections import OrderedDict, defaultdict
|
13
|
+
from dataclasses import dataclass, field
|
14
|
+
from datetime import UTC, datetime, timedelta
|
15
|
+
from threading import RLock
|
16
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
17
|
+
|
18
|
+
from kailash.workflow.type_inference import (
|
19
|
+
ConnectionInferenceResult,
|
20
|
+
TypeCompatibilityResult,
|
21
|
+
)
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class PerformanceMetrics:
|
28
|
+
"""Performance metrics for validation operations."""
|
29
|
+
|
30
|
+
total_validations: int = 0
|
31
|
+
total_time_ms: float = 0.0
|
32
|
+
cache_hits: int = 0
|
33
|
+
cache_misses: int = 0
|
34
|
+
avg_validation_time_ms: float = 0.0
|
35
|
+
max_validation_time_ms: float = 0.0
|
36
|
+
min_validation_time_ms: float = float("inf")
|
37
|
+
|
38
|
+
# Cache performance
|
39
|
+
cache_hit_ratio: float = 0.0
|
40
|
+
cache_size: int = 0
|
41
|
+
cache_memory_kb: float = 0.0
|
42
|
+
|
43
|
+
# Bottlenecks
|
44
|
+
slow_validations: List[Tuple[str, float]] = field(default_factory=list)
|
45
|
+
expensive_connections: List[Tuple[str, str, float]] = field(default_factory=list)
|
46
|
+
|
47
|
+
def update_validation_time(self, time_ms: float, operation: str = "unknown"):
|
48
|
+
"""Update validation timing statistics."""
|
49
|
+
self.total_validations += 1
|
50
|
+
self.total_time_ms += time_ms
|
51
|
+
self.avg_validation_time_ms = self.total_time_ms / self.total_validations
|
52
|
+
|
53
|
+
if time_ms > self.max_validation_time_ms:
|
54
|
+
self.max_validation_time_ms = time_ms
|
55
|
+
|
56
|
+
if time_ms < self.min_validation_time_ms:
|
57
|
+
self.min_validation_time_ms = time_ms
|
58
|
+
|
59
|
+
# Track slow validations (>50ms)
|
60
|
+
if time_ms > 50.0:
|
61
|
+
self.slow_validations.append((operation, time_ms))
|
62
|
+
# Keep only recent slow validations
|
63
|
+
if len(self.slow_validations) > 100:
|
64
|
+
self.slow_validations = self.slow_validations[-100:]
|
65
|
+
|
66
|
+
def update_cache_stats(self, hits: int, misses: int, size: int, memory_kb: float):
|
67
|
+
"""Update cache performance statistics."""
|
68
|
+
self.cache_hits = hits
|
69
|
+
self.cache_misses = misses
|
70
|
+
self.cache_size = size
|
71
|
+
self.cache_memory_kb = memory_kb
|
72
|
+
|
73
|
+
total_requests = hits + misses
|
74
|
+
self.cache_hit_ratio = hits / total_requests if total_requests > 0 else 0.0
|
75
|
+
|
76
|
+
|
77
|
+
class LRUCache:
|
78
|
+
"""Thread-safe LRU cache with memory management."""
|
79
|
+
|
80
|
+
def __init__(self, max_size: int = 1000, ttl_seconds: int = 3600):
|
81
|
+
"""Initialize LRU cache.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
max_size: Maximum number of items to cache
|
85
|
+
ttl_seconds: Time-to-live for cache entries
|
86
|
+
"""
|
87
|
+
self.max_size = max_size
|
88
|
+
self.ttl_seconds = ttl_seconds
|
89
|
+
self._cache: OrderedDict = OrderedDict()
|
90
|
+
self._timestamps: Dict[Any, datetime] = {}
|
91
|
+
self._lock = RLock()
|
92
|
+
self._hits = 0
|
93
|
+
self._misses = 0
|
94
|
+
|
95
|
+
def get(self, key: Any) -> Optional[Any]:
|
96
|
+
"""Get item from cache."""
|
97
|
+
with self._lock:
|
98
|
+
# Check if key exists and is not expired
|
99
|
+
if key in self._cache:
|
100
|
+
timestamp = self._timestamps[key]
|
101
|
+
if datetime.now(UTC) - timestamp < timedelta(seconds=self.ttl_seconds):
|
102
|
+
# Move to end (most recently used)
|
103
|
+
self._cache.move_to_end(key)
|
104
|
+
self._hits += 1
|
105
|
+
return self._cache[key]
|
106
|
+
else:
|
107
|
+
# Expired, remove
|
108
|
+
del self._cache[key]
|
109
|
+
del self._timestamps[key]
|
110
|
+
|
111
|
+
self._misses += 1
|
112
|
+
return None
|
113
|
+
|
114
|
+
def put(self, key: Any, value: Any) -> None:
|
115
|
+
"""Put item in cache."""
|
116
|
+
with self._lock:
|
117
|
+
# Update existing item
|
118
|
+
if key in self._cache:
|
119
|
+
self._cache[key] = value
|
120
|
+
self._cache.move_to_end(key)
|
121
|
+
self._timestamps[key] = datetime.now(UTC)
|
122
|
+
return
|
123
|
+
|
124
|
+
# Add new item
|
125
|
+
self._cache[key] = value
|
126
|
+
self._timestamps[key] = datetime.now(UTC)
|
127
|
+
|
128
|
+
# Remove oldest if over capacity
|
129
|
+
if len(self._cache) > self.max_size:
|
130
|
+
oldest_key = next(iter(self._cache))
|
131
|
+
del self._cache[oldest_key]
|
132
|
+
del self._timestamps[oldest_key]
|
133
|
+
|
134
|
+
def clear(self) -> None:
|
135
|
+
"""Clear all cache entries."""
|
136
|
+
with self._lock:
|
137
|
+
self._cache.clear()
|
138
|
+
self._timestamps.clear()
|
139
|
+
self._hits = 0
|
140
|
+
self._misses = 0
|
141
|
+
|
142
|
+
def get_stats(self) -> Dict[str, Any]:
|
143
|
+
"""Get cache statistics."""
|
144
|
+
with self._lock:
|
145
|
+
total_requests = self._hits + self._misses
|
146
|
+
return {
|
147
|
+
"size": len(self._cache),
|
148
|
+
"max_size": self.max_size,
|
149
|
+
"hits": self._hits,
|
150
|
+
"misses": self._misses,
|
151
|
+
"hit_ratio": self._hits / total_requests if total_requests > 0 else 0.0,
|
152
|
+
"memory_estimate_kb": len(self._cache) * 0.1, # Rough estimate
|
153
|
+
}
|
154
|
+
|
155
|
+
|
156
|
+
class ValidationCache:
|
157
|
+
"""High-performance validation result cache."""
|
158
|
+
|
159
|
+
def __init__(self, max_size: int = 5000, ttl_seconds: int = 3600):
|
160
|
+
"""Initialize validation cache.
|
161
|
+
|
162
|
+
Args:
|
163
|
+
max_size: Maximum cache entries
|
164
|
+
ttl_seconds: Time-to-live for entries
|
165
|
+
"""
|
166
|
+
self._compatibility_cache = LRUCache(max_size, ttl_seconds)
|
167
|
+
self._connection_cache = LRUCache(max_size, ttl_seconds)
|
168
|
+
self._schema_cache = LRUCache(
|
169
|
+
max_size // 2, ttl_seconds * 2
|
170
|
+
) # Schemas change less often
|
171
|
+
|
172
|
+
def get_compatibility_result(
|
173
|
+
self, source_type: type, target_type: type, allow_coercion: bool
|
174
|
+
) -> Optional[TypeCompatibilityResult]:
|
175
|
+
"""Get cached compatibility result."""
|
176
|
+
key = (source_type, target_type, allow_coercion)
|
177
|
+
return self._compatibility_cache.get(key)
|
178
|
+
|
179
|
+
def cache_compatibility_result(
|
180
|
+
self,
|
181
|
+
source_type: type,
|
182
|
+
target_type: type,
|
183
|
+
allow_coercion: bool,
|
184
|
+
result: TypeCompatibilityResult,
|
185
|
+
) -> None:
|
186
|
+
"""Cache compatibility result."""
|
187
|
+
key = (source_type, target_type, allow_coercion)
|
188
|
+
self._compatibility_cache.put(key, result)
|
189
|
+
|
190
|
+
def get_connection_result(
|
191
|
+
self, source_node: str, target_node: str, mapping: Dict[str, str]
|
192
|
+
) -> Optional[ConnectionInferenceResult]:
|
193
|
+
"""Get cached connection validation result."""
|
194
|
+
# Create hashable key from mapping
|
195
|
+
mapping_key = tuple(sorted(mapping.items())) if mapping else ()
|
196
|
+
key = (source_node, target_node, mapping_key)
|
197
|
+
return self._connection_cache.get(key)
|
198
|
+
|
199
|
+
def cache_connection_result(
|
200
|
+
self,
|
201
|
+
source_node: str,
|
202
|
+
target_node: str,
|
203
|
+
mapping: Dict[str, str],
|
204
|
+
result: ConnectionInferenceResult,
|
205
|
+
) -> None:
|
206
|
+
"""Cache connection validation result."""
|
207
|
+
mapping_key = tuple(sorted(mapping.items())) if mapping else ()
|
208
|
+
key = (source_node, target_node, mapping_key)
|
209
|
+
self._connection_cache.put(key, result)
|
210
|
+
|
211
|
+
def get_schema_validation(self, schema_hash: str, data_hash: str) -> Optional[bool]:
|
212
|
+
"""Get cached schema validation result."""
|
213
|
+
key = (schema_hash, data_hash)
|
214
|
+
return self._schema_cache.get(key)
|
215
|
+
|
216
|
+
def cache_schema_validation(
|
217
|
+
self, schema_hash: str, data_hash: str, is_valid: bool
|
218
|
+
) -> None:
|
219
|
+
"""Cache schema validation result."""
|
220
|
+
key = (schema_hash, data_hash)
|
221
|
+
self._schema_cache.put(key, is_valid)
|
222
|
+
|
223
|
+
def clear_all(self) -> None:
|
224
|
+
"""Clear all caches."""
|
225
|
+
self._compatibility_cache.clear()
|
226
|
+
self._connection_cache.clear()
|
227
|
+
self._schema_cache.clear()
|
228
|
+
|
229
|
+
def get_stats(self) -> Dict[str, Any]:
|
230
|
+
"""Get cache performance statistics."""
|
231
|
+
compatibility_stats = self._compatibility_cache.get_stats()
|
232
|
+
connection_stats = self._connection_cache.get_stats()
|
233
|
+
schema_stats = self._schema_cache.get_stats()
|
234
|
+
|
235
|
+
return {
|
236
|
+
"compatibility_cache": compatibility_stats,
|
237
|
+
"connection_cache": connection_stats,
|
238
|
+
"schema_cache": schema_stats,
|
239
|
+
"total_size": compatibility_stats["size"]
|
240
|
+
+ connection_stats["size"]
|
241
|
+
+ schema_stats["size"],
|
242
|
+
"total_memory_kb": (
|
243
|
+
compatibility_stats["memory_estimate_kb"]
|
244
|
+
+ connection_stats["memory_estimate_kb"]
|
245
|
+
+ schema_stats["memory_estimate_kb"]
|
246
|
+
),
|
247
|
+
}
|
248
|
+
|
249
|
+
|
250
|
+
class LazyValidator:
|
251
|
+
"""Lazy evaluation for validation operations."""
|
252
|
+
|
253
|
+
def __init__(self, cache: ValidationCache):
|
254
|
+
"""Initialize lazy validator.
|
255
|
+
|
256
|
+
Args:
|
257
|
+
cache: Validation cache instance
|
258
|
+
"""
|
259
|
+
self.cache = cache
|
260
|
+
self._pending_validations: Dict[str, Any] = {}
|
261
|
+
self._validation_futures: Dict[str, asyncio.Future] = {}
|
262
|
+
|
263
|
+
def defer_validation(
|
264
|
+
self, validation_id: str, validation_func, *args, **kwargs
|
265
|
+
) -> str:
|
266
|
+
"""Defer validation until actually needed.
|
267
|
+
|
268
|
+
Args:
|
269
|
+
validation_id: Unique identifier for validation
|
270
|
+
validation_func: Function to call for validation
|
271
|
+
*args, **kwargs: Arguments for validation function
|
272
|
+
|
273
|
+
Returns:
|
274
|
+
Validation ID for later retrieval
|
275
|
+
"""
|
276
|
+
self._pending_validations[validation_id] = {
|
277
|
+
"func": validation_func,
|
278
|
+
"args": args,
|
279
|
+
"kwargs": kwargs,
|
280
|
+
"created_at": time.time(),
|
281
|
+
}
|
282
|
+
return validation_id
|
283
|
+
|
284
|
+
def get_validation_result(self, validation_id: str) -> Any:
|
285
|
+
"""Get validation result, executing if needed.
|
286
|
+
|
287
|
+
Args:
|
288
|
+
validation_id: Validation identifier
|
289
|
+
|
290
|
+
Returns:
|
291
|
+
Validation result
|
292
|
+
"""
|
293
|
+
if validation_id not in self._pending_validations:
|
294
|
+
raise ValueError(f"Unknown validation ID: {validation_id}")
|
295
|
+
|
296
|
+
validation = self._pending_validations[validation_id]
|
297
|
+
|
298
|
+
# Execute validation
|
299
|
+
start_time = time.time()
|
300
|
+
try:
|
301
|
+
result = validation["func"](*validation["args"], **validation["kwargs"])
|
302
|
+
execution_time = (time.time() - start_time) * 1000
|
303
|
+
|
304
|
+
logger.debug(
|
305
|
+
f"Lazy validation {validation_id} completed in {execution_time:.2f}ms"
|
306
|
+
)
|
307
|
+
return result
|
308
|
+
finally:
|
309
|
+
# Clean up
|
310
|
+
del self._pending_validations[validation_id]
|
311
|
+
|
312
|
+
def cleanup_expired(self, max_age_seconds: int = 300) -> int:
|
313
|
+
"""Clean up expired pending validations.
|
314
|
+
|
315
|
+
Args:
|
316
|
+
max_age_seconds: Maximum age for pending validations
|
317
|
+
|
318
|
+
Returns:
|
319
|
+
Number of cleaned up validations
|
320
|
+
"""
|
321
|
+
current_time = time.time()
|
322
|
+
expired_ids = [
|
323
|
+
validation_id
|
324
|
+
for validation_id, validation in self._pending_validations.items()
|
325
|
+
if current_time - validation["created_at"] > max_age_seconds
|
326
|
+
]
|
327
|
+
|
328
|
+
for validation_id in expired_ids:
|
329
|
+
del self._pending_validations[validation_id]
|
330
|
+
|
331
|
+
return len(expired_ids)
|
332
|
+
|
333
|
+
|
334
|
+
class BatchValidator:
|
335
|
+
"""Batch validation for improved performance."""
|
336
|
+
|
337
|
+
def __init__(self, cache: ValidationCache, batch_size: int = 50):
|
338
|
+
"""Initialize batch validator.
|
339
|
+
|
340
|
+
Args:
|
341
|
+
cache: Validation cache instance
|
342
|
+
batch_size: Number of validations to batch together
|
343
|
+
"""
|
344
|
+
self.cache = cache
|
345
|
+
self.batch_size = batch_size
|
346
|
+
self._batch_queue: List[Dict[str, Any]] = []
|
347
|
+
self._batch_results: Dict[str, Any] = {}
|
348
|
+
|
349
|
+
def add_validation(
|
350
|
+
self, validation_id: str, validation_type: str, **params
|
351
|
+
) -> None:
|
352
|
+
"""Add validation to batch queue.
|
353
|
+
|
354
|
+
Args:
|
355
|
+
validation_id: Unique identifier
|
356
|
+
validation_type: Type of validation (compatibility, connection, schema)
|
357
|
+
**params: Validation parameters
|
358
|
+
"""
|
359
|
+
self._batch_queue.append(
|
360
|
+
{
|
361
|
+
"id": validation_id,
|
362
|
+
"type": validation_type,
|
363
|
+
"params": params,
|
364
|
+
"added_at": time.time(),
|
365
|
+
}
|
366
|
+
)
|
367
|
+
|
368
|
+
# Process batch if full
|
369
|
+
if len(self._batch_queue) >= self.batch_size:
|
370
|
+
self.process_batch()
|
371
|
+
|
372
|
+
def process_batch(self) -> Dict[str, Any]:
|
373
|
+
"""Process all queued validations as a batch.
|
374
|
+
|
375
|
+
Returns:
|
376
|
+
Dictionary of validation results by ID
|
377
|
+
"""
|
378
|
+
if not self._batch_queue:
|
379
|
+
return {}
|
380
|
+
|
381
|
+
start_time = time.time()
|
382
|
+
batch = self._batch_queue.copy()
|
383
|
+
self._batch_queue.clear()
|
384
|
+
|
385
|
+
# Group by validation type for optimized processing
|
386
|
+
by_type = defaultdict(list)
|
387
|
+
for validation in batch:
|
388
|
+
by_type[validation["type"]].append(validation)
|
389
|
+
|
390
|
+
results = {}
|
391
|
+
|
392
|
+
# Process each type in batch
|
393
|
+
for validation_type, validations in by_type.items():
|
394
|
+
if validation_type == "compatibility":
|
395
|
+
results.update(self._process_compatibility_batch(validations))
|
396
|
+
elif validation_type == "connection":
|
397
|
+
results.update(self._process_connection_batch(validations))
|
398
|
+
elif validation_type == "schema":
|
399
|
+
results.update(self._process_schema_batch(validations))
|
400
|
+
|
401
|
+
processing_time = (time.time() - start_time) * 1000
|
402
|
+
logger.debug(
|
403
|
+
f"Processed batch of {len(batch)} validations in {processing_time:.2f}ms"
|
404
|
+
)
|
405
|
+
|
406
|
+
self._batch_results.update(results)
|
407
|
+
return results
|
408
|
+
|
409
|
+
def _process_compatibility_batch(
|
410
|
+
self, validations: List[Dict[str, Any]]
|
411
|
+
) -> Dict[str, Any]:
|
412
|
+
"""Process compatibility validations in batch."""
|
413
|
+
results = {}
|
414
|
+
|
415
|
+
# Check cache first for all validations
|
416
|
+
for validation in validations:
|
417
|
+
params = validation["params"]
|
418
|
+
cached_result = self.cache.get_compatibility_result(
|
419
|
+
params["source_type"], params["target_type"], params["allow_coercion"]
|
420
|
+
)
|
421
|
+
|
422
|
+
if cached_result:
|
423
|
+
results[validation["id"]] = cached_result
|
424
|
+
else:
|
425
|
+
# Would need actual validation logic here
|
426
|
+
# For now, indicate cache miss
|
427
|
+
results[validation["id"]] = None
|
428
|
+
|
429
|
+
return results
|
430
|
+
|
431
|
+
def _process_connection_batch(
|
432
|
+
self, validations: List[Dict[str, Any]]
|
433
|
+
) -> Dict[str, Any]:
|
434
|
+
"""Process connection validations in batch."""
|
435
|
+
results = {}
|
436
|
+
|
437
|
+
for validation in validations:
|
438
|
+
params = validation["params"]
|
439
|
+
cached_result = self.cache.get_connection_result(
|
440
|
+
params["source_node"], params["target_node"], params["mapping"]
|
441
|
+
)
|
442
|
+
|
443
|
+
results[validation["id"]] = cached_result
|
444
|
+
|
445
|
+
return results
|
446
|
+
|
447
|
+
def _process_schema_batch(
|
448
|
+
self, validations: List[Dict[str, Any]]
|
449
|
+
) -> Dict[str, Any]:
|
450
|
+
"""Process schema validations in batch."""
|
451
|
+
results = {}
|
452
|
+
|
453
|
+
for validation in validations:
|
454
|
+
params = validation["params"]
|
455
|
+
cached_result = self.cache.get_schema_validation(
|
456
|
+
params["schema_hash"], params["data_hash"]
|
457
|
+
)
|
458
|
+
|
459
|
+
results[validation["id"]] = cached_result
|
460
|
+
|
461
|
+
return results
|
462
|
+
|
463
|
+
def get_result(self, validation_id: str) -> Optional[Any]:
|
464
|
+
"""Get result for a specific validation."""
|
465
|
+
return self._batch_results.get(validation_id)
|
466
|
+
|
467
|
+
def flush(self) -> Dict[str, Any]:
|
468
|
+
"""Force process any remaining validations in queue."""
|
469
|
+
return self.process_batch()
|
470
|
+
|
471
|
+
|
472
|
+
class PerformanceOptimizer:
|
473
|
+
"""Main performance optimization coordinator."""
|
474
|
+
|
475
|
+
def __init__(self, cache_size: int = 5000, batch_size: int = 50):
|
476
|
+
"""Initialize performance optimizer.
|
477
|
+
|
478
|
+
Args:
|
479
|
+
cache_size: Size of validation cache
|
480
|
+
batch_size: Batch size for validation operations
|
481
|
+
"""
|
482
|
+
self.cache = ValidationCache(cache_size)
|
483
|
+
self.lazy_validator = LazyValidator(self.cache)
|
484
|
+
self.batch_validator = BatchValidator(self.cache, batch_size)
|
485
|
+
self.metrics = PerformanceMetrics()
|
486
|
+
|
487
|
+
# Optimization settings
|
488
|
+
self.enable_caching = True
|
489
|
+
self.enable_lazy_evaluation = True
|
490
|
+
self.enable_batch_processing = True
|
491
|
+
self.cache_cleanup_interval = 300 # 5 minutes
|
492
|
+
|
493
|
+
def optimize_validation_pipeline(
|
494
|
+
self, workflow_data: Dict[str, Any]
|
495
|
+
) -> Dict[str, Any]:
|
496
|
+
"""Optimize validation pipeline for a workflow.
|
497
|
+
|
498
|
+
Args:
|
499
|
+
workflow_data: Workflow configuration
|
500
|
+
|
501
|
+
Returns:
|
502
|
+
Optimization recommendations
|
503
|
+
"""
|
504
|
+
recommendations = {
|
505
|
+
"cache_configuration": {},
|
506
|
+
"batch_settings": {},
|
507
|
+
"lazy_evaluation": {},
|
508
|
+
"performance_warnings": [],
|
509
|
+
}
|
510
|
+
|
511
|
+
# Analyze workflow complexity
|
512
|
+
node_count = len(workflow_data.get("nodes", []))
|
513
|
+
connection_count = len(workflow_data.get("connections", []))
|
514
|
+
|
515
|
+
# Cache size recommendations
|
516
|
+
if connection_count > 100:
|
517
|
+
recommendations["cache_configuration"]["size"] = min(
|
518
|
+
10000, connection_count * 10
|
519
|
+
)
|
520
|
+
recommendations["cache_configuration"][
|
521
|
+
"ttl"
|
522
|
+
] = 7200 # 2 hours for complex workflows
|
523
|
+
else:
|
524
|
+
recommendations["cache_configuration"]["size"] = 1000
|
525
|
+
recommendations["cache_configuration"]["ttl"] = 3600 # 1 hour
|
526
|
+
|
527
|
+
# Batch size recommendations
|
528
|
+
if connection_count > 50:
|
529
|
+
recommendations["batch_settings"]["size"] = min(100, connection_count // 2)
|
530
|
+
recommendations["batch_settings"]["enable"] = True
|
531
|
+
else:
|
532
|
+
recommendations["batch_settings"]["size"] = 10
|
533
|
+
recommendations["batch_settings"]["enable"] = False
|
534
|
+
|
535
|
+
# Lazy evaluation recommendations
|
536
|
+
recommendations["lazy_evaluation"]["enable"] = connection_count > 20
|
537
|
+
|
538
|
+
# Performance warnings
|
539
|
+
if connection_count > 200:
|
540
|
+
recommendations["performance_warnings"].append(
|
541
|
+
"Large number of connections detected. Consider workflow splitting."
|
542
|
+
)
|
543
|
+
|
544
|
+
if node_count > 50:
|
545
|
+
recommendations["performance_warnings"].append(
|
546
|
+
"Complex workflow detected. Enable all performance optimizations."
|
547
|
+
)
|
548
|
+
|
549
|
+
return recommendations
|
550
|
+
|
551
|
+
def get_performance_report(self) -> Dict[str, Any]:
|
552
|
+
"""Get comprehensive performance report."""
|
553
|
+
cache_stats = self.cache.get_stats()
|
554
|
+
|
555
|
+
return {
|
556
|
+
"validation_metrics": {
|
557
|
+
"total_validations": self.metrics.total_validations,
|
558
|
+
"avg_time_ms": self.metrics.avg_validation_time_ms,
|
559
|
+
"max_time_ms": self.metrics.max_validation_time_ms,
|
560
|
+
"min_time_ms": (
|
561
|
+
self.metrics.min_validation_time_ms
|
562
|
+
if self.metrics.min_validation_time_ms != float("inf")
|
563
|
+
else 0
|
564
|
+
),
|
565
|
+
},
|
566
|
+
"cache_performance": {
|
567
|
+
"hit_ratio": self.metrics.cache_hit_ratio,
|
568
|
+
"total_size": cache_stats["total_size"],
|
569
|
+
"memory_usage_kb": cache_stats["total_memory_kb"],
|
570
|
+
},
|
571
|
+
"bottlenecks": {
|
572
|
+
"slow_validations": self.metrics.slow_validations[-10:], # Last 10
|
573
|
+
"expensive_connections": self.metrics.expensive_connections[-10:],
|
574
|
+
},
|
575
|
+
"optimization_status": {
|
576
|
+
"caching_enabled": self.enable_caching,
|
577
|
+
"lazy_evaluation_enabled": self.enable_lazy_evaluation,
|
578
|
+
"batch_processing_enabled": self.enable_batch_processing,
|
579
|
+
},
|
580
|
+
}
|
581
|
+
|
582
|
+
def cleanup_resources(self) -> Dict[str, int]:
|
583
|
+
"""Clean up resources and return statistics."""
|
584
|
+
cleanup_stats = {
|
585
|
+
"expired_lazy_validations": self.lazy_validator.cleanup_expired(),
|
586
|
+
"cache_entries_before": self.cache.get_stats()["total_size"],
|
587
|
+
}
|
588
|
+
|
589
|
+
# Optional: Clear old cache entries (could be more sophisticated)
|
590
|
+
# For now, we rely on TTL in the cache implementation
|
591
|
+
|
592
|
+
cleanup_stats["cache_entries_after"] = self.cache.get_stats()["total_size"]
|
593
|
+
cleanup_stats["cache_entries_cleaned"] = (
|
594
|
+
cleanup_stats["cache_entries_before"] - cleanup_stats["cache_entries_after"]
|
595
|
+
)
|
596
|
+
|
597
|
+
return cleanup_stats
|
598
|
+
|
599
|
+
|
600
|
+
# Global performance optimizer instance
|
601
|
+
_global_optimizer = None
|
602
|
+
|
603
|
+
|
604
|
+
def get_performance_optimizer() -> PerformanceOptimizer:
|
605
|
+
"""Get global performance optimizer instance."""
|
606
|
+
global _global_optimizer
|
607
|
+
if _global_optimizer is None:
|
608
|
+
_global_optimizer = PerformanceOptimizer()
|
609
|
+
return _global_optimizer
|
610
|
+
|
611
|
+
|
612
|
+
def reset_performance_optimizer() -> None:
|
613
|
+
"""Reset global performance optimizer (for testing)."""
|
614
|
+
global _global_optimizer
|
615
|
+
_global_optimizer = None
|