@unrdf/dark-matter 5.0.0 → 26.4.2
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.
- package/LICENSE +21 -0
- package/package.json +10 -6
- package/src/dark-matter/query-analyzer.mjs +1 -1
- package/README.md +0 -81
- package/src/dark-matter/critical-path.mjs +0 -367
- package/src/dark-matter/index-advisor.mjs +0 -242
- package/src/dark-matter/index.mjs +0 -244
- package/src/dark-matter/optimizer.mjs +0 -426
- package/src/dark-matter/performance-metrics.mjs +0 -242
- package/src/dark-matter/query-optimizer.mjs +0 -283
- package/src/dark-matter-core.mjs +0 -743
- package/src/index.mjs +0 -60
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unrdf/dark-matter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.4.2",
|
|
4
4
|
"description": "UNRDF Dark Matter - Query Optimization and Performance Analysis (Optional Extension)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"test:fast": "vitest run --coverage",
|
|
22
22
|
"test:watch": "vitest --coverage",
|
|
23
23
|
"build": "node build.config.mjs",
|
|
24
|
-
"lint": "eslint src/
|
|
25
|
-
"lint:fix": "eslint src/
|
|
26
|
-
"format": "prettier --write src/
|
|
27
|
-
"format:check": "prettier --check src/
|
|
24
|
+
"lint": "eslint src/ --max-warnings=0",
|
|
25
|
+
"lint:fix": "eslint src/ --fix",
|
|
26
|
+
"format": "prettier --write src/",
|
|
27
|
+
"format:check": "prettier --check src/",
|
|
28
28
|
"clean": "rm -rf dist/ .nyc_output/ coverage/",
|
|
29
29
|
"dev": "echo 'Development mode for @unrdf/dark-matter'"
|
|
30
30
|
},
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@unrdf/core": "workspace:*",
|
|
40
|
+
"@unrdf/oxigraph": "workspace:*",
|
|
40
41
|
"typhonjs-escomplex": "^0.1.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
@@ -55,5 +56,8 @@
|
|
|
55
56
|
"url": "https://github.com/unrdf/unrdf/issues"
|
|
56
57
|
},
|
|
57
58
|
"homepage": "https://github.com/unrdf/unrdf#readme",
|
|
58
|
-
"license": "MIT"
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
59
63
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and identify expensive operations for 80/20 optimization.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { analyzeSPARQLQuery, extractVariables } from '
|
|
10
|
+
import { analyzeSPARQLQuery, extractVariables } from '@unrdf/core/utils/sparql-utils';
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
|
|
13
13
|
/**
|
package/README.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# @unrdf/dark-matter
|
|
2
|
-
|
|
3
|
-
 
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
**Query Optimization and Performance Analysis** *(Optional Extension)*
|
|
7
|
-
|
|
8
|
-
Optimize SPARQL queries and analyze RDF graph performance. Implements the 80/20 principle for query optimization.
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
pnpm add @unrdf/dark-matter
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## 📚 Examples
|
|
17
|
-
|
|
18
|
-
See these examples that demonstrate @unrdf/dark-matter:
|
|
19
|
-
|
|
20
|
-
- **[dark-matter-80-20.mjs](../../examples/dark-matter-80-20.mjs)** - Query optimization basics (30 min, 5-10x speedup)
|
|
21
|
-
- **[dark-matter-query-optimization.mjs](../../examples/dark-matter-query-optimization.mjs)** - Advanced optimization strategies
|
|
22
|
-
- **[profiling-example.mjs](../../examples/profiling-example.mjs)** - Performance profiling and analysis
|
|
23
|
-
- **[lockchain-dark-matter-test.mjs](../../examples/lockchain-dark-matter-test.mjs)** - Combined optimization + audit
|
|
24
|
-
|
|
25
|
-
**Need faster queries?** Start with [dark-matter-80-20.mjs](../../examples/dark-matter-80-20.mjs).
|
|
26
|
-
|
|
27
|
-
## Quick Start
|
|
28
|
-
|
|
29
|
-
```javascript
|
|
30
|
-
import { optimizeQuery, analyzePerformance } from '@unrdf/dark-matter'
|
|
31
|
-
|
|
32
|
-
// Optimize a SPARQL query
|
|
33
|
-
const optimized = optimizeQuery(
|
|
34
|
-
'SELECT ?name WHERE { ?s foaf:name ?name. ?s foaf:age ?age }'
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
// Analyze graph performance
|
|
38
|
-
const metrics = await analyzePerformance(store)
|
|
39
|
-
console.log('Query execution time:', metrics.queryTime)
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Features
|
|
43
|
-
|
|
44
|
-
- ✅ SPARQL query optimization
|
|
45
|
-
- ✅ Critical path analysis
|
|
46
|
-
- ✅ Performance metrics collection
|
|
47
|
-
- ✅ Query plan analysis
|
|
48
|
-
- ✅ Index recommendations
|
|
49
|
-
- ✅ Bottleneck identification
|
|
50
|
-
|
|
51
|
-
## Use Cases
|
|
52
|
-
|
|
53
|
-
- **Performance tuning**: Optimize slow queries
|
|
54
|
-
- **Capacity planning**: Understand resource usage
|
|
55
|
-
- **Monitoring**: Track graph performance
|
|
56
|
-
- **Optimization**: Find and fix bottlenecks
|
|
57
|
-
- **Benchmarking**: Compare query strategies
|
|
58
|
-
|
|
59
|
-
## Documentation
|
|
60
|
-
|
|
61
|
-
- **[API Reference](./docs/API.md)** - Complete API documentation
|
|
62
|
-
- **[User Guide](./docs/GUIDE.md)** - Optimization guide
|
|
63
|
-
- **[Examples](./examples/)** - Code examples
|
|
64
|
-
- **[Contributing](./docs/CONTRIBUTING.md)** - How to contribute
|
|
65
|
-
|
|
66
|
-
## Status
|
|
67
|
-
|
|
68
|
-
**Optional Extension** - Use only if you need query optimization.
|
|
69
|
-
Most applications don't need this.
|
|
70
|
-
|
|
71
|
-
## Depends On
|
|
72
|
-
|
|
73
|
-
- `@unrdf/core` - RDF substrate
|
|
74
|
-
|
|
75
|
-
## VOC Usage
|
|
76
|
-
|
|
77
|
-
- Performance-critical applications
|
|
78
|
-
|
|
79
|
-
## License
|
|
80
|
-
|
|
81
|
-
MIT
|
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Dark Matter 80/20 Critical Path Identification
|
|
3
|
-
* @module dark-matter/critical-path
|
|
4
|
-
*
|
|
5
|
-
* @description
|
|
6
|
-
* Implements the 80/20 algorithm to identify the top 20% slowest queries
|
|
7
|
-
* that account for 80% of performance impact.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { z } from 'zod';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Schema for query execution log
|
|
14
|
-
*/
|
|
15
|
-
const QueryExecutionLogSchema = z.object({
|
|
16
|
-
queryId: z.string(),
|
|
17
|
-
query: z.string(),
|
|
18
|
-
executionTime: z.number(),
|
|
19
|
-
timestamp: z.number(),
|
|
20
|
-
metadata: z.object({}).passthrough().optional(),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Schema for critical path result
|
|
25
|
-
*/
|
|
26
|
-
const CriticalPathResultSchema = z.object({
|
|
27
|
-
criticalQueries: z.array(
|
|
28
|
-
z.object({
|
|
29
|
-
queryId: z.string(),
|
|
30
|
-
query: z.string(),
|
|
31
|
-
executionTime: z.number(),
|
|
32
|
-
occurrences: z.number(),
|
|
33
|
-
totalTime: z.number(),
|
|
34
|
-
percentageOfTotal: z.number(),
|
|
35
|
-
rank: z.number(),
|
|
36
|
-
})
|
|
37
|
-
),
|
|
38
|
-
metrics: z.object({
|
|
39
|
-
totalQueries: z.number(),
|
|
40
|
-
criticalQueryCount: z.number(),
|
|
41
|
-
criticalQueryPercentage: z.number(),
|
|
42
|
-
totalExecutionTime: z.number(),
|
|
43
|
-
criticalExecutionTime: z.number(),
|
|
44
|
-
impactRatio: z.number(),
|
|
45
|
-
avgExecutionTime: z.number(),
|
|
46
|
-
p50: z.number(),
|
|
47
|
-
p90: z.number(),
|
|
48
|
-
p99: z.number(),
|
|
49
|
-
}),
|
|
50
|
-
timestamp: z.number(),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Critical Path Identifier for Dark Matter 80/20
|
|
55
|
-
*/
|
|
56
|
-
export class CriticalPathIdentifier {
|
|
57
|
-
/**
|
|
58
|
-
* Create a new critical path identifier
|
|
59
|
-
* @param {Object} [config] - Configuration
|
|
60
|
-
*/
|
|
61
|
-
constructor(config = {}) {
|
|
62
|
-
this.config = {
|
|
63
|
-
targetImpactRatio: config.targetImpactRatio || 0.8, // 80% of impact
|
|
64
|
-
targetQueryRatio: config.targetQueryRatio || 0.2, // from 20% of queries
|
|
65
|
-
minSampleSize: config.minSampleSize || 10,
|
|
66
|
-
windowSize: config.windowSize || 1000, // Keep last 1000 queries
|
|
67
|
-
...config,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.executionLogs = [];
|
|
71
|
-
this.cache = new Map();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Log a query execution
|
|
76
|
-
* @param {string} queryId - Query identifier
|
|
77
|
-
* @param {string} query - Query string
|
|
78
|
-
* @param {number} executionTime - Execution time in ms
|
|
79
|
-
* @param {Object} [metadata] - Optional metadata
|
|
80
|
-
*/
|
|
81
|
-
logExecution(queryId, query, executionTime, metadata = {}) {
|
|
82
|
-
const log = {
|
|
83
|
-
queryId,
|
|
84
|
-
query,
|
|
85
|
-
executionTime,
|
|
86
|
-
timestamp: Date.now(),
|
|
87
|
-
metadata,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
this.executionLogs.push(QueryExecutionLogSchema.parse(log));
|
|
91
|
-
|
|
92
|
-
// Keep window size
|
|
93
|
-
if (this.executionLogs.length > this.config.windowSize) {
|
|
94
|
-
this.executionLogs.shift();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Invalidate cache
|
|
98
|
-
this.cache.clear();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Analyze and identify critical path
|
|
103
|
-
* @returns {Object} Critical path analysis
|
|
104
|
-
*/
|
|
105
|
-
identify() {
|
|
106
|
-
if (this.executionLogs.length < this.config.minSampleSize) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Insufficient data: ${this.executionLogs.length} queries logged, ` +
|
|
109
|
-
`minimum ${this.config.minSampleSize} required`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Check cache
|
|
114
|
-
const cacheKey = 'critical-path';
|
|
115
|
-
if (this.cache.has(cacheKey)) {
|
|
116
|
-
return this.cache.get(cacheKey);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Group queries by queryId and calculate statistics
|
|
120
|
-
const queryStats = this._aggregateQueryStats();
|
|
121
|
-
|
|
122
|
-
// Sort by total execution time (descending)
|
|
123
|
-
const sortedQueries = Array.from(queryStats.values()).sort((a, b) => b.totalTime - a.totalTime);
|
|
124
|
-
|
|
125
|
-
// Calculate total execution time
|
|
126
|
-
const totalExecutionTime = sortedQueries.reduce((sum, q) => sum + q.totalTime, 0);
|
|
127
|
-
|
|
128
|
-
// Find critical queries (top 20% that account for 80% of time)
|
|
129
|
-
const criticalQueries = this._findCriticalQueries(sortedQueries, totalExecutionTime);
|
|
130
|
-
|
|
131
|
-
// Calculate metrics
|
|
132
|
-
const metrics = this._calculateMetrics(sortedQueries, criticalQueries, totalExecutionTime);
|
|
133
|
-
|
|
134
|
-
const result = {
|
|
135
|
-
criticalQueries: criticalQueries.map((q, index) => ({
|
|
136
|
-
...q,
|
|
137
|
-
rank: index + 1,
|
|
138
|
-
})),
|
|
139
|
-
metrics,
|
|
140
|
-
timestamp: Date.now(),
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const validated = CriticalPathResultSchema.parse(result);
|
|
144
|
-
|
|
145
|
-
// Cache result
|
|
146
|
-
this.cache.set(cacheKey, validated);
|
|
147
|
-
|
|
148
|
-
return validated;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Aggregate query statistics
|
|
153
|
-
* @returns {Map} Query statistics
|
|
154
|
-
* @private
|
|
155
|
-
*/
|
|
156
|
-
_aggregateQueryStats() {
|
|
157
|
-
const stats = new Map();
|
|
158
|
-
|
|
159
|
-
for (const log of this.executionLogs) {
|
|
160
|
-
if (!stats.has(log.queryId)) {
|
|
161
|
-
stats.set(log.queryId, {
|
|
162
|
-
queryId: log.queryId,
|
|
163
|
-
query: log.query,
|
|
164
|
-
executionTime: 0,
|
|
165
|
-
occurrences: 0,
|
|
166
|
-
totalTime: 0,
|
|
167
|
-
percentageOfTotal: 0,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const stat = stats.get(log.queryId);
|
|
172
|
-
stat.occurrences++;
|
|
173
|
-
stat.totalTime += log.executionTime;
|
|
174
|
-
stat.executionTime = stat.totalTime / stat.occurrences; // Average
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return stats;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Find critical queries using 80/20 algorithm
|
|
182
|
-
* @param {Array} sortedQueries - Queries sorted by total time
|
|
183
|
-
* @param {number} totalExecutionTime - Total execution time
|
|
184
|
-
* @returns {Array} Critical queries
|
|
185
|
-
* @private
|
|
186
|
-
*/
|
|
187
|
-
_findCriticalQueries(sortedQueries, totalExecutionTime) {
|
|
188
|
-
const critical = [];
|
|
189
|
-
let cumulativeTime = 0;
|
|
190
|
-
let cumulativePercentage = 0;
|
|
191
|
-
|
|
192
|
-
// Find queries that contribute to target impact ratio (80%)
|
|
193
|
-
for (const query of sortedQueries) {
|
|
194
|
-
cumulativeTime += query.totalTime;
|
|
195
|
-
cumulativePercentage = cumulativeTime / totalExecutionTime;
|
|
196
|
-
|
|
197
|
-
query.percentageOfTotal = (query.totalTime / totalExecutionTime) * 100;
|
|
198
|
-
critical.push(query);
|
|
199
|
-
|
|
200
|
-
// Stop when we reach the target impact ratio
|
|
201
|
-
if (cumulativePercentage >= this.config.targetImpactRatio) {
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Ensure we don't exceed target query ratio (20%)
|
|
207
|
-
const maxCriticalQueries = Math.ceil(sortedQueries.length * this.config.targetQueryRatio);
|
|
208
|
-
|
|
209
|
-
// Return either the queries that hit 80% impact or top 20%, whichever is smaller
|
|
210
|
-
return critical.slice(0, Math.min(critical.length, maxCriticalQueries));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Calculate performance metrics
|
|
215
|
-
* @param {Array} allQueries - All queries
|
|
216
|
-
* @param {Array} criticalQueries - Critical queries
|
|
217
|
-
* @param {number} totalExecutionTime - Total execution time
|
|
218
|
-
* @returns {Object} Metrics
|
|
219
|
-
* @private
|
|
220
|
-
*/
|
|
221
|
-
_calculateMetrics(allQueries, criticalQueries, totalExecutionTime) {
|
|
222
|
-
const criticalExecutionTime = criticalQueries.reduce((sum, q) => sum + q.totalTime, 0);
|
|
223
|
-
|
|
224
|
-
// Calculate percentiles
|
|
225
|
-
const executionTimes = this.executionLogs.map(log => log.executionTime).sort((a, b) => a - b);
|
|
226
|
-
|
|
227
|
-
const p50 = this._percentile(executionTimes, 0.5);
|
|
228
|
-
const p90 = this._percentile(executionTimes, 0.9);
|
|
229
|
-
const p99 = this._percentile(executionTimes, 0.99);
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
totalQueries: allQueries.length,
|
|
233
|
-
criticalQueryCount: criticalQueries.length,
|
|
234
|
-
criticalQueryPercentage: (criticalQueries.length / allQueries.length) * 100,
|
|
235
|
-
totalExecutionTime,
|
|
236
|
-
criticalExecutionTime,
|
|
237
|
-
impactRatio: criticalExecutionTime / totalExecutionTime,
|
|
238
|
-
avgExecutionTime: totalExecutionTime / this.executionLogs.length,
|
|
239
|
-
p50,
|
|
240
|
-
p90,
|
|
241
|
-
p99,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Calculate percentile
|
|
247
|
-
* @param {Array} sortedValues - Sorted array of values
|
|
248
|
-
* @param {number} percentile - Percentile (0-1)
|
|
249
|
-
* @returns {number} Percentile value
|
|
250
|
-
* @private
|
|
251
|
-
*/
|
|
252
|
-
_percentile(sortedValues, percentile) {
|
|
253
|
-
if (sortedValues.length === 0) return 0;
|
|
254
|
-
|
|
255
|
-
const index = Math.ceil(sortedValues.length * percentile) - 1;
|
|
256
|
-
return sortedValues[Math.max(0, index)];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Get report in markdown format
|
|
261
|
-
* @returns {string} Markdown report
|
|
262
|
-
*/
|
|
263
|
-
getReport() {
|
|
264
|
-
const analysis = this.identify();
|
|
265
|
-
|
|
266
|
-
let report = '# Dark Matter 80/20 Critical Path Report\n\n';
|
|
267
|
-
|
|
268
|
-
// Summary
|
|
269
|
-
report += '## Summary\n\n';
|
|
270
|
-
report += `- **Total Queries Analyzed**: ${analysis.metrics.totalQueries}\n`;
|
|
271
|
-
report += `- **Critical Queries (Top ${(this.config.targetQueryRatio * 100).toFixed(0)}%)**: ${analysis.metrics.criticalQueryCount}\n`;
|
|
272
|
-
report += `- **Impact Ratio**: ${(analysis.metrics.impactRatio * 100).toFixed(1)}% of total execution time\n`;
|
|
273
|
-
report += `- **Average Execution Time**: ${analysis.metrics.avgExecutionTime.toFixed(2)}ms\n`;
|
|
274
|
-
report += `- **P50 Latency**: ${analysis.metrics.p50.toFixed(2)}ms\n`;
|
|
275
|
-
report += `- **P90 Latency**: ${analysis.metrics.p90.toFixed(2)}ms\n`;
|
|
276
|
-
report += `- **P99 Latency**: ${analysis.metrics.p99.toFixed(2)}ms\n\n`;
|
|
277
|
-
|
|
278
|
-
// Critical queries
|
|
279
|
-
report += '## Critical Queries\n\n';
|
|
280
|
-
report += '| Rank | Query ID | Occurrences | Avg Time | Total Time | % of Total |\n';
|
|
281
|
-
report += '|------|----------|-------------|----------|------------|------------|\n';
|
|
282
|
-
|
|
283
|
-
for (const query of analysis.criticalQueries) {
|
|
284
|
-
report += `| ${query.rank} | ${query.queryId} | ${query.occurrences} | ${query.executionTime.toFixed(2)}ms | ${query.totalTime.toFixed(2)}ms | ${query.percentageOfTotal.toFixed(1)}% |\n`;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
report += '\n## Recommendations\n\n';
|
|
288
|
-
report +=
|
|
289
|
-
'Focus optimization efforts on the queries listed above. These represent the critical path:\n\n';
|
|
290
|
-
report += `- Optimizing the top ${analysis.metrics.criticalQueryCount} queries will improve ${(analysis.metrics.impactRatio * 100).toFixed(1)}% of query performance\n`;
|
|
291
|
-
report +=
|
|
292
|
-
'- Consider adding indexes, rewriting queries, or caching results for critical queries\n';
|
|
293
|
-
report += '- Monitor P99 latency to catch performance regressions\n';
|
|
294
|
-
|
|
295
|
-
return report;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Get execution logs
|
|
300
|
-
* @param {Object} [filter] - Optional filter
|
|
301
|
-
* @returns {Array} Execution logs
|
|
302
|
-
*/
|
|
303
|
-
getLogs(filter = {}) {
|
|
304
|
-
let logs = [...this.executionLogs];
|
|
305
|
-
|
|
306
|
-
if (filter.queryId) {
|
|
307
|
-
logs = logs.filter(log => log.queryId === filter.queryId);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (filter.minExecutionTime) {
|
|
311
|
-
logs = logs.filter(log => log.executionTime >= filter.minExecutionTime);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (filter.startTime && filter.endTime) {
|
|
315
|
-
logs = logs.filter(
|
|
316
|
-
log => log.timestamp >= filter.startTime && log.timestamp <= filter.endTime
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return logs;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Clear all logs
|
|
325
|
-
*/
|
|
326
|
-
clearLogs() {
|
|
327
|
-
this.executionLogs = [];
|
|
328
|
-
this.cache.clear();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Export logs to JSON
|
|
333
|
-
* @returns {string} JSON string
|
|
334
|
-
*/
|
|
335
|
-
exportLogs() {
|
|
336
|
-
return JSON.stringify(
|
|
337
|
-
{
|
|
338
|
-
logs: this.executionLogs,
|
|
339
|
-
config: this.config,
|
|
340
|
-
timestamp: Date.now(),
|
|
341
|
-
},
|
|
342
|
-
null,
|
|
343
|
-
2
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Import logs from JSON
|
|
349
|
-
* @param {string} json - JSON string
|
|
350
|
-
*/
|
|
351
|
-
importLogs(json) {
|
|
352
|
-
const data = JSON.parse(json);
|
|
353
|
-
this.executionLogs = data.logs.map(log => QueryExecutionLogSchema.parse(log));
|
|
354
|
-
this.cache.clear();
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Create a critical path identifier instance
|
|
360
|
-
* @param {Object} [config] - Configuration
|
|
361
|
-
* @returns {CriticalPathIdentifier} Critical path identifier
|
|
362
|
-
*/
|
|
363
|
-
export function createCriticalPathIdentifier(config = {}) {
|
|
364
|
-
return new CriticalPathIdentifier(config);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export default CriticalPathIdentifier;
|