exonware-xwnode 0.0.1.12__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.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,487 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/strategies/performance_monitor.py
|
3
|
+
"""
|
4
|
+
Strategy Performance Monitor
|
5
|
+
|
6
|
+
Tracks strategy usage, performance metrics, and provides optimization recommendations.
|
7
|
+
This enables data-driven strategy selection and performance tuning.
|
8
|
+
|
9
|
+
Company: eXonware.com
|
10
|
+
Author: Eng. Muhammad AlShehri
|
11
|
+
Email: connect@exonware.com
|
12
|
+
Version: 0.0.1.12
|
13
|
+
Generation Date: 07-Sep-2025
|
14
|
+
"""
|
15
|
+
|
16
|
+
import time
|
17
|
+
import threading
|
18
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
19
|
+
from dataclasses import dataclass, field
|
20
|
+
from collections import defaultdict, deque
|
21
|
+
from enum import Enum
|
22
|
+
from exonware.xwsystem import get_logger
|
23
|
+
|
24
|
+
logger = get_logger(__name__)
|
25
|
+
|
26
|
+
from ..types import NodeMode, EdgeMode
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
class OperationType(Enum):
|
31
|
+
"""Types of operations to monitor."""
|
32
|
+
GET = "get"
|
33
|
+
PUT = "put"
|
34
|
+
DELETE = "delete"
|
35
|
+
ITERATE = "iterate"
|
36
|
+
SEARCH = "search"
|
37
|
+
MIGRATE = "migrate"
|
38
|
+
CREATE = "create"
|
39
|
+
|
40
|
+
|
41
|
+
@dataclass
|
42
|
+
class OperationMetrics:
|
43
|
+
"""Metrics for a specific operation."""
|
44
|
+
operation: OperationType
|
45
|
+
count: int = 0
|
46
|
+
total_time: float = 0.0
|
47
|
+
min_time: float = float('inf')
|
48
|
+
max_time: float = 0.0
|
49
|
+
avg_time: float = 0.0
|
50
|
+
error_count: int = 0
|
51
|
+
memory_usage: float = 0.0
|
52
|
+
|
53
|
+
def add_measurement(self, duration: float, memory_usage: float = 0.0, error: bool = False):
|
54
|
+
"""Add a new measurement."""
|
55
|
+
self.count += 1
|
56
|
+
self.total_time += duration
|
57
|
+
self.min_time = min(self.min_time, duration)
|
58
|
+
self.max_time = max(self.max_time, duration)
|
59
|
+
self.avg_time = self.total_time / self.count
|
60
|
+
self.memory_usage += memory_usage
|
61
|
+
|
62
|
+
if error:
|
63
|
+
self.error_count += 1
|
64
|
+
|
65
|
+
|
66
|
+
@dataclass
|
67
|
+
class StrategyProfile:
|
68
|
+
"""Performance profile for a strategy."""
|
69
|
+
strategy_name: str
|
70
|
+
mode: Union[NodeMode, EdgeMode]
|
71
|
+
total_operations: int = 0
|
72
|
+
total_time: float = 0.0
|
73
|
+
operations: Dict[OperationType, OperationMetrics] = field(default_factory=dict)
|
74
|
+
memory_usage: float = 0.0
|
75
|
+
error_rate: float = 0.0
|
76
|
+
last_used: float = 0.0
|
77
|
+
creation_time: float = field(default_factory=time.time)
|
78
|
+
|
79
|
+
def get_operation_metrics(self, operation: OperationType) -> OperationMetrics:
|
80
|
+
"""Get or create operation metrics."""
|
81
|
+
if operation not in self.operations:
|
82
|
+
self.operations[operation] = OperationMetrics(operation)
|
83
|
+
return self.operations[operation]
|
84
|
+
|
85
|
+
def update_error_rate(self):
|
86
|
+
"""Update error rate calculation."""
|
87
|
+
total_errors = sum(op.error_count for op in self.operations.values())
|
88
|
+
self.error_rate = total_errors / max(self.total_operations, 1)
|
89
|
+
|
90
|
+
|
91
|
+
@dataclass
|
92
|
+
class PerformanceRecommendation:
|
93
|
+
"""Performance optimization recommendation."""
|
94
|
+
strategy_name: str
|
95
|
+
recommendation_type: str
|
96
|
+
confidence: float
|
97
|
+
reasoning: str
|
98
|
+
estimated_improvement: float
|
99
|
+
alternative_strategy: Optional[str] = None
|
100
|
+
|
101
|
+
|
102
|
+
class StrategyPerformanceMonitor:
|
103
|
+
"""
|
104
|
+
Monitors strategy performance and provides optimization recommendations.
|
105
|
+
"""
|
106
|
+
|
107
|
+
def __init__(self, history_size: int = 1000):
|
108
|
+
"""
|
109
|
+
Initialize performance monitor.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
history_size: Maximum number of operations to keep in history
|
113
|
+
"""
|
114
|
+
self._history_size = history_size
|
115
|
+
self._profiles: Dict[str, StrategyProfile] = {}
|
116
|
+
self._operation_history: deque = deque(maxlen=history_size)
|
117
|
+
self._lock = threading.RLock()
|
118
|
+
self._stats = {
|
119
|
+
'total_operations': 0,
|
120
|
+
'total_strategies': 0,
|
121
|
+
'monitoring_start_time': time.time(),
|
122
|
+
'recommendations_given': 0
|
123
|
+
}
|
124
|
+
|
125
|
+
def record_operation(
|
126
|
+
self,
|
127
|
+
strategy_id: str,
|
128
|
+
operation: OperationType,
|
129
|
+
duration: float,
|
130
|
+
memory_usage: float = 0.0,
|
131
|
+
error: bool = False,
|
132
|
+
**metadata: Any
|
133
|
+
) -> None:
|
134
|
+
"""
|
135
|
+
Record an operation for performance monitoring.
|
136
|
+
|
137
|
+
Args:
|
138
|
+
strategy_id: Unique identifier for the strategy
|
139
|
+
operation: Type of operation performed
|
140
|
+
duration: Operation duration in seconds
|
141
|
+
memory_usage: Memory usage in bytes
|
142
|
+
error: Whether the operation resulted in an error
|
143
|
+
**metadata: Additional metadata
|
144
|
+
"""
|
145
|
+
with self._lock:
|
146
|
+
# Get or create strategy profile
|
147
|
+
if strategy_id not in self._profiles:
|
148
|
+
self._profiles[strategy_id] = StrategyProfile(
|
149
|
+
strategy_name=strategy_id,
|
150
|
+
mode=self._extract_mode_from_id(strategy_id)
|
151
|
+
)
|
152
|
+
self._stats['total_strategies'] += 1
|
153
|
+
|
154
|
+
profile = self._profiles[strategy_id]
|
155
|
+
|
156
|
+
# Update profile
|
157
|
+
profile.total_operations += 1
|
158
|
+
profile.total_time += duration
|
159
|
+
profile.memory_usage += memory_usage
|
160
|
+
profile.last_used = time.time()
|
161
|
+
|
162
|
+
# Update operation metrics
|
163
|
+
op_metrics = profile.get_operation_metrics(operation)
|
164
|
+
op_metrics.add_measurement(duration, memory_usage, error)
|
165
|
+
|
166
|
+
# Update error rate
|
167
|
+
profile.update_error_rate()
|
168
|
+
|
169
|
+
# Add to history
|
170
|
+
self._operation_history.append({
|
171
|
+
'timestamp': time.time(),
|
172
|
+
'strategy_id': strategy_id,
|
173
|
+
'operation': operation.value,
|
174
|
+
'duration': duration,
|
175
|
+
'memory_usage': memory_usage,
|
176
|
+
'error': error,
|
177
|
+
'metadata': metadata
|
178
|
+
})
|
179
|
+
|
180
|
+
self._stats['total_operations'] += 1
|
181
|
+
|
182
|
+
logger.debug(f"๐ Recorded {operation.value} operation for {strategy_id}: {duration:.3f}s")
|
183
|
+
|
184
|
+
def get_strategy_profile(self, strategy_id: str) -> Optional[StrategyProfile]:
|
185
|
+
"""
|
186
|
+
Get performance profile for a strategy.
|
187
|
+
|
188
|
+
Args:
|
189
|
+
strategy_id: Strategy identifier
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
Strategy profile or None if not found
|
193
|
+
"""
|
194
|
+
with self._lock:
|
195
|
+
return self._profiles.get(strategy_id)
|
196
|
+
|
197
|
+
def get_top_performing_strategies(self, limit: int = 5) -> List[Tuple[str, StrategyProfile]]:
|
198
|
+
"""
|
199
|
+
Get top performing strategies by average operation time.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
limit: Maximum number of strategies to return
|
203
|
+
|
204
|
+
Returns:
|
205
|
+
List of (strategy_id, profile) tuples sorted by performance
|
206
|
+
"""
|
207
|
+
with self._lock:
|
208
|
+
strategies = []
|
209
|
+
for strategy_id, profile in self._profiles.items():
|
210
|
+
if profile.total_operations > 0:
|
211
|
+
avg_time = profile.total_time / profile.total_operations
|
212
|
+
strategies.append((strategy_id, profile, avg_time))
|
213
|
+
|
214
|
+
# Sort by average time (lower is better)
|
215
|
+
strategies.sort(key=lambda x: x[2])
|
216
|
+
|
217
|
+
return [(sid, prof) for sid, prof, _ in strategies[:limit]]
|
218
|
+
|
219
|
+
def get_underperforming_strategies(self, threshold: float = 0.1) -> List[Tuple[str, StrategyProfile]]:
|
220
|
+
"""
|
221
|
+
Get strategies that are underperforming.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
threshold: Error rate threshold for underperformance
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
List of underperforming strategies
|
228
|
+
"""
|
229
|
+
with self._lock:
|
230
|
+
underperforming = []
|
231
|
+
for strategy_id, profile in self._profiles.items():
|
232
|
+
if profile.error_rate > threshold or profile.total_operations == 0:
|
233
|
+
underperforming.append((strategy_id, profile))
|
234
|
+
|
235
|
+
return underperforming
|
236
|
+
|
237
|
+
def generate_recommendations(self, strategy_id: str) -> List[PerformanceRecommendation]:
|
238
|
+
"""
|
239
|
+
Generate performance recommendations for a strategy.
|
240
|
+
|
241
|
+
Args:
|
242
|
+
strategy_id: Strategy identifier
|
243
|
+
|
244
|
+
Returns:
|
245
|
+
List of performance recommendations
|
246
|
+
"""
|
247
|
+
profile = self.get_strategy_profile(strategy_id)
|
248
|
+
if not profile or profile.total_operations < 10:
|
249
|
+
return []
|
250
|
+
|
251
|
+
recommendations = []
|
252
|
+
|
253
|
+
# Check for high error rate
|
254
|
+
if profile.error_rate > 0.05: # 5% error rate
|
255
|
+
recommendations.append(PerformanceRecommendation(
|
256
|
+
strategy_name=strategy_id,
|
257
|
+
recommendation_type="error_rate",
|
258
|
+
confidence=0.8,
|
259
|
+
reasoning=f"High error rate ({profile.error_rate:.1%}) detected",
|
260
|
+
estimated_improvement=0.2,
|
261
|
+
alternative_strategy=self._suggest_alternative_strategy(profile.mode)
|
262
|
+
))
|
263
|
+
|
264
|
+
# Check for slow operations
|
265
|
+
slow_operations = []
|
266
|
+
for op_type, metrics in profile.operations.items():
|
267
|
+
if metrics.avg_time > 0.01: # 10ms threshold
|
268
|
+
slow_operations.append((op_type, metrics.avg_time))
|
269
|
+
|
270
|
+
if slow_operations:
|
271
|
+
slowest_op, slowest_time = max(slow_operations, key=lambda x: x[1])
|
272
|
+
recommendations.append(PerformanceRecommendation(
|
273
|
+
strategy_name=strategy_id,
|
274
|
+
recommendation_type="slow_operations",
|
275
|
+
confidence=0.7,
|
276
|
+
reasoning=f"Slow {slowest_op.value} operations (avg: {slowest_time:.3f}s)",
|
277
|
+
estimated_improvement=0.3,
|
278
|
+
alternative_strategy=self._suggest_alternative_strategy(profile.mode)
|
279
|
+
))
|
280
|
+
|
281
|
+
# Check for memory usage
|
282
|
+
if profile.memory_usage > 100 * 1024 * 1024: # 100MB threshold
|
283
|
+
recommendations.append(PerformanceRecommendation(
|
284
|
+
strategy_name=strategy_id,
|
285
|
+
recommendation_type="memory_usage",
|
286
|
+
confidence=0.6,
|
287
|
+
reasoning=f"High memory usage ({profile.memory_usage / 1024 / 1024:.1f}MB)",
|
288
|
+
estimated_improvement=0.4,
|
289
|
+
alternative_strategy=self._suggest_memory_efficient_strategy(profile.mode)
|
290
|
+
))
|
291
|
+
|
292
|
+
# Check for unused strategies
|
293
|
+
time_since_last_use = time.time() - profile.last_used
|
294
|
+
if time_since_last_use > 3600: # 1 hour
|
295
|
+
recommendations.append(PerformanceRecommendation(
|
296
|
+
strategy_name=strategy_id,
|
297
|
+
recommendation_type="unused_strategy",
|
298
|
+
confidence=0.9,
|
299
|
+
reasoning=f"Strategy unused for {time_since_last_use / 3600:.1f} hours",
|
300
|
+
estimated_improvement=0.1,
|
301
|
+
alternative_strategy="Consider removing unused strategy"
|
302
|
+
))
|
303
|
+
|
304
|
+
self._stats['recommendations_given'] += len(recommendations)
|
305
|
+
return recommendations
|
306
|
+
|
307
|
+
def get_performance_summary(self) -> Dict[str, Any]:
|
308
|
+
"""
|
309
|
+
Get overall performance summary.
|
310
|
+
|
311
|
+
Returns:
|
312
|
+
Performance summary dictionary
|
313
|
+
"""
|
314
|
+
with self._lock:
|
315
|
+
if not self._profiles:
|
316
|
+
return {
|
317
|
+
'total_strategies': 0,
|
318
|
+
'total_operations': 0,
|
319
|
+
'average_operation_time': 0.0,
|
320
|
+
'total_error_rate': 0.0,
|
321
|
+
'monitoring_duration': 0.0
|
322
|
+
}
|
323
|
+
|
324
|
+
total_operations = sum(p.total_operations for p in self._profiles.values())
|
325
|
+
total_time = sum(p.total_time for p in self._profiles.values())
|
326
|
+
total_errors = sum(
|
327
|
+
sum(op.error_count for op in p.operations.values())
|
328
|
+
for p in self._profiles.values()
|
329
|
+
)
|
330
|
+
|
331
|
+
avg_operation_time = total_time / total_operations if total_operations > 0 else 0.0
|
332
|
+
total_error_rate = total_errors / total_operations if total_operations > 0 else 0.0
|
333
|
+
monitoring_duration = time.time() - self._stats['monitoring_start_time']
|
334
|
+
|
335
|
+
return {
|
336
|
+
'total_strategies': len(self._profiles),
|
337
|
+
'total_operations': total_operations,
|
338
|
+
'average_operation_time': avg_operation_time,
|
339
|
+
'total_error_rate': total_error_rate,
|
340
|
+
'monitoring_duration': monitoring_duration,
|
341
|
+
'operations_per_second': total_operations / monitoring_duration if monitoring_duration > 0 else 0.0,
|
342
|
+
'top_strategies': [
|
343
|
+
{
|
344
|
+
'strategy_id': sid,
|
345
|
+
'operations': prof.total_operations,
|
346
|
+
'avg_time': prof.total_time / prof.total_operations if prof.total_operations > 0 else 0.0,
|
347
|
+
'error_rate': prof.error_rate
|
348
|
+
}
|
349
|
+
for sid, prof in self.get_top_performing_strategies(3)
|
350
|
+
]
|
351
|
+
}
|
352
|
+
|
353
|
+
def get_operation_history(self, limit: int = 100) -> List[Dict[str, Any]]:
|
354
|
+
"""
|
355
|
+
Get recent operation history.
|
356
|
+
|
357
|
+
Args:
|
358
|
+
limit: Maximum number of operations to return
|
359
|
+
|
360
|
+
Returns:
|
361
|
+
List of recent operations
|
362
|
+
"""
|
363
|
+
with self._lock:
|
364
|
+
return list(self._operation_history)[-limit:]
|
365
|
+
|
366
|
+
def clear_history(self) -> None:
|
367
|
+
"""Clear operation history."""
|
368
|
+
with self._lock:
|
369
|
+
self._operation_history.clear()
|
370
|
+
logger.info("๐งน Cleared performance monitoring history")
|
371
|
+
|
372
|
+
def reset_stats(self) -> None:
|
373
|
+
"""Reset all statistics."""
|
374
|
+
with self._lock:
|
375
|
+
self._profiles.clear()
|
376
|
+
self._operation_history.clear()
|
377
|
+
self._stats = {
|
378
|
+
'total_operations': 0,
|
379
|
+
'total_strategies': 0,
|
380
|
+
'monitoring_start_time': time.time(),
|
381
|
+
'recommendations_given': 0
|
382
|
+
}
|
383
|
+
logger.info("๐ Reset performance monitoring statistics")
|
384
|
+
|
385
|
+
def _extract_mode_from_id(self, strategy_id: str) -> Union[NodeMode, EdgeMode]:
|
386
|
+
"""Extract mode from strategy ID."""
|
387
|
+
# Try to extract mode from strategy ID
|
388
|
+
for mode in list(NodeMode) + list(EdgeMode):
|
389
|
+
if mode.name.lower() in strategy_id.lower():
|
390
|
+
return mode
|
391
|
+
|
392
|
+
# Default fallback
|
393
|
+
return NodeMode.HASH_MAP
|
394
|
+
|
395
|
+
def _suggest_alternative_strategy(self, current_mode: Union[NodeMode, EdgeMode]) -> str:
|
396
|
+
"""Suggest alternative strategy based on current mode."""
|
397
|
+
alternatives = {
|
398
|
+
NodeMode.HASH_MAP: "ARRAY_LIST or TREE_GRAPH_HYBRID",
|
399
|
+
NodeMode.ARRAY_LIST: "HASH_MAP or TREE_GRAPH_HYBRID",
|
400
|
+
NodeMode.TREE_GRAPH_HYBRID: "HASH_MAP or DATA_OPTIMIZED",
|
401
|
+
NodeMode.DATA_OPTIMIZED: "HASH_MAP or TREE_GRAPH_HYBRID",
|
402
|
+
EdgeMode.ADJ_LIST: "ADJACENCY_MATRIX",
|
403
|
+
EdgeMode.ADJACENCY_MATRIX: "adjacency_list"
|
404
|
+
}
|
405
|
+
|
406
|
+
return alternatives.get(current_mode, "Unknown alternative")
|
407
|
+
|
408
|
+
def _suggest_memory_efficient_strategy(self, current_mode: Union[NodeMode, EdgeMode]) -> str:
|
409
|
+
"""Suggest memory-efficient alternative strategy."""
|
410
|
+
memory_efficient = {
|
411
|
+
NodeMode.HASH_MAP: "ARRAY_LIST (for sequential data)",
|
412
|
+
NodeMode.ARRAY_LIST: "HASH_MAP (for sparse data)",
|
413
|
+
NodeMode.TREE_GRAPH_HYBRID: "DATA_OPTIMIZED (for large datasets)",
|
414
|
+
NodeMode.DATA_OPTIMIZED: "HASH_MAP (for small datasets)",
|
415
|
+
EdgeMode.ADJ_LIST: "ADJACENCY_MATRIX (for dense graphs)",
|
416
|
+
EdgeMode.ADJACENCY_MATRIX: "adjacency_list (for sparse graphs)"
|
417
|
+
}
|
418
|
+
|
419
|
+
return memory_efficient.get(current_mode, "Consider data structure optimization")
|
420
|
+
|
421
|
+
|
422
|
+
# Global monitor instance
|
423
|
+
_monitor_instance: Optional[StrategyPerformanceMonitor] = None
|
424
|
+
_monitor_lock = threading.Lock()
|
425
|
+
|
426
|
+
|
427
|
+
def get_monitor() -> StrategyPerformanceMonitor:
|
428
|
+
"""
|
429
|
+
Get the global performance monitor instance.
|
430
|
+
|
431
|
+
Returns:
|
432
|
+
Global StrategyPerformanceMonitor instance
|
433
|
+
"""
|
434
|
+
global _monitor_instance
|
435
|
+
|
436
|
+
if _monitor_instance is None:
|
437
|
+
with _monitor_lock:
|
438
|
+
if _monitor_instance is None:
|
439
|
+
_monitor_instance = StrategyPerformanceMonitor()
|
440
|
+
logger.info("๐ Initialized global strategy performance monitor")
|
441
|
+
|
442
|
+
return _monitor_instance
|
443
|
+
|
444
|
+
|
445
|
+
def record_operation(
|
446
|
+
strategy_id: str,
|
447
|
+
operation: OperationType,
|
448
|
+
duration: float,
|
449
|
+
memory_usage: float = 0.0,
|
450
|
+
error: bool = False,
|
451
|
+
**metadata: Any
|
452
|
+
) -> None:
|
453
|
+
"""
|
454
|
+
Record an operation using the global monitor.
|
455
|
+
|
456
|
+
Args:
|
457
|
+
strategy_id: Strategy identifier
|
458
|
+
operation: Operation type
|
459
|
+
duration: Operation duration
|
460
|
+
memory_usage: Memory usage
|
461
|
+
error: Whether operation failed
|
462
|
+
**metadata: Additional metadata
|
463
|
+
"""
|
464
|
+
get_monitor().record_operation(strategy_id, operation, duration, memory_usage, error, **metadata)
|
465
|
+
|
466
|
+
|
467
|
+
def get_performance_summary() -> Dict[str, Any]:
|
468
|
+
"""
|
469
|
+
Get performance summary from global monitor.
|
470
|
+
|
471
|
+
Returns:
|
472
|
+
Performance summary
|
473
|
+
"""
|
474
|
+
return get_monitor().get_performance_summary()
|
475
|
+
|
476
|
+
|
477
|
+
def get_strategy_recommendations(strategy_id: str) -> List[PerformanceRecommendation]:
|
478
|
+
"""
|
479
|
+
Get recommendations for a strategy from global monitor.
|
480
|
+
|
481
|
+
Args:
|
482
|
+
strategy_id: Strategy identifier
|
483
|
+
|
484
|
+
Returns:
|
485
|
+
List of recommendations
|
486
|
+
"""
|
487
|
+
return get_monitor().generate_recommendations(strategy_id)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"""
|
2
|
+
Query Strategies Package
|
3
|
+
|
4
|
+
This package contains all query strategy implementations organized by type:
|
5
|
+
- Linear queries (index-based, value-based)
|
6
|
+
- Tree queries (key-based, range queries)
|
7
|
+
- Graph queries (path queries, neighbor queries)
|
8
|
+
|
9
|
+
Company: eXonware.com
|
10
|
+
Author: Eng. Muhammad AlShehri
|
11
|
+
Email: connect@exonware.com
|
12
|
+
Version: 0.0.1.12
|
13
|
+
Generation Date: January 2, 2025
|
14
|
+
"""
|
15
|
+
|
16
|
+
from .base import AQueryStrategy
|
17
|
+
from .xwquery_strategy import XWQueryScriptStrategy
|
18
|
+
from .xwnode_executor import XWNodeQueryActionExecutor
|
19
|
+
|
20
|
+
__all__ = [
|
21
|
+
'AQueryStrategy',
|
22
|
+
'XWQueryScriptStrategy',
|
23
|
+
'XWNodeQueryActionExecutor'
|
24
|
+
]
|