@feltdb/core 0.2.0 → 0.4.0
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 +19 -0
- package/dist/analytics-backend.d.ts +39 -0
- package/dist/analytics-backend.d.ts.map +1 -0
- package/dist/analytics-backend.js +152 -0
- package/dist/application-contract.d.ts +49 -0
- package/dist/application-contract.d.ts.map +1 -0
- package/dist/application-contract.js +13 -0
- package/dist/application-manifest.d.ts +305 -0
- package/dist/application-manifest.d.ts.map +1 -0
- package/dist/application-manifest.js +26 -0
- package/dist/artifact.d.ts +73 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +21 -0
- package/dist/authorization.d.ts +99 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +15 -0
- package/dist/bundle.d.ts +55 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +13 -0
- package/dist/collection.d.ts +28 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +83 -0
- package/dist/db.d.ts +18 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +20 -8
- package/dist/distributed-indexing.d.ts +170 -0
- package/dist/distributed-indexing.d.ts.map +1 -0
- package/dist/distributed-indexing.js +260 -0
- package/dist/flowspec.d.ts +4 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +8 -0
- package/dist/http-client.d.ts +35 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +70 -0
- package/dist/identity.d.ts +40 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +10 -0
- package/dist/index-analytics.d.ts +121 -0
- package/dist/index-analytics.d.ts.map +1 -0
- package/dist/index-analytics.js +279 -0
- package/dist/index-backend.d.ts +26 -0
- package/dist/index-backend.d.ts.map +1 -0
- package/dist/index-backend.js +115 -0
- package/dist/index-dashboard.d.ts +114 -0
- package/dist/index-dashboard.d.ts.map +1 -0
- package/dist/index-dashboard.js +256 -0
- package/dist/index-manager.d.ts +18 -0
- package/dist/index-manager.d.ts.map +1 -0
- package/dist/index-manager.js +333 -0
- package/dist/index-monitoring.d.ts +133 -0
- package/dist/index-monitoring.d.ts.map +1 -0
- package/dist/index-monitoring.js +218 -0
- package/dist/index-recovery.d.ts +57 -0
- package/dist/index-recovery.d.ts.map +1 -0
- package/dist/index-recovery.js +117 -0
- package/dist/index-store.d.ts +62 -0
- package/dist/index-store.d.ts.map +1 -0
- package/dist/index-store.js +141 -0
- package/dist/index-types.d.ts +70 -0
- package/dist/index-types.d.ts.map +1 -0
- package/dist/index-types.js +6 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/observe.d.ts +51 -0
- package/dist/observe.d.ts.map +1 -0
- package/dist/observe.js +14 -0
- package/dist/protocol.d.ts +25 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +18 -0
- package/dist/provider.d.ts +61 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +17 -0
- package/dist/query-planner.d.ts +69 -0
- package/dist/query-planner.d.ts.map +1 -0
- package/dist/query-planner.js +184 -0
- package/dist/release.d.ts +60 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +18 -0
- package/dist/sharding.d.ts +159 -0
- package/dist/sharding.d.ts.map +1 -0
- package/dist/sharding.js +317 -0
- package/dist/state-contract.d.ts +173 -0
- package/dist/state-contract.d.ts.map +1 -0
- package/dist/state-contract.js +24 -0
- package/dist/sync-contract.d.ts +101 -0
- package/dist/sync-contract.d.ts.map +1 -0
- package/dist/sync-contract.js +90 -0
- package/dist/worker.d.ts +90 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +26 -0
- package/dist/workload.d.ts +102 -0
- package/dist/workload.d.ts.map +1 -0
- package/dist/workload.js +23 -0
- package/package.json +17 -5
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Analytics and Performance Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Tracks index usage, query performance, and generates optimization recommendations.
|
|
5
|
+
* Uses Rust implementation via WASM for performance-critical operations,
|
|
6
|
+
* with TypeScript fallback when WASM unavailable.
|
|
7
|
+
*/
|
|
8
|
+
export interface IndexMetrics {
|
|
9
|
+
name: string;
|
|
10
|
+
reads: number;
|
|
11
|
+
writes: number;
|
|
12
|
+
hitRate: number;
|
|
13
|
+
avgQueryTimeMs: number;
|
|
14
|
+
avgWriteTimeMs: number;
|
|
15
|
+
entries: number;
|
|
16
|
+
lastUsedAt: number;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
}
|
|
19
|
+
export interface QueryTrace {
|
|
20
|
+
timestamp: number;
|
|
21
|
+
fields: string[];
|
|
22
|
+
operator: string;
|
|
23
|
+
indexUsed?: string;
|
|
24
|
+
executionTimeMs: number;
|
|
25
|
+
rowsScanned: number;
|
|
26
|
+
resultCount: number;
|
|
27
|
+
}
|
|
28
|
+
export interface PerformanceReport {
|
|
29
|
+
collectionName: string;
|
|
30
|
+
period: 'hourly' | 'daily' | 'weekly';
|
|
31
|
+
totalQueries: number;
|
|
32
|
+
indexedQueries: number;
|
|
33
|
+
fullScans: number;
|
|
34
|
+
avgQueryTimeMs: number;
|
|
35
|
+
slowestQuery?: QueryTrace;
|
|
36
|
+
indexMetrics: IndexMetrics[];
|
|
37
|
+
recommendations: TuningRecommendation[];
|
|
38
|
+
timestamp: number;
|
|
39
|
+
}
|
|
40
|
+
export interface TuningRecommendation {
|
|
41
|
+
type: 'create_index' | 'drop_index' | 'optimize_index';
|
|
42
|
+
priority: 'high' | 'medium' | 'low';
|
|
43
|
+
indexName?: string;
|
|
44
|
+
reason: string;
|
|
45
|
+
expectedSpeedup?: number;
|
|
46
|
+
estimatedROI?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Tracks index usage and query performance.
|
|
50
|
+
*/
|
|
51
|
+
export declare class IndexAnalytics {
|
|
52
|
+
private metrics;
|
|
53
|
+
private queryTraces;
|
|
54
|
+
private fieldQueryCount;
|
|
55
|
+
private maxTraces;
|
|
56
|
+
/**
|
|
57
|
+
* Record an index usage event.
|
|
58
|
+
*/
|
|
59
|
+
recordIndexUsage(indexName: string, operation: 'read' | 'write', timeMs: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* Record a query execution.
|
|
62
|
+
*/
|
|
63
|
+
recordQuery(fields: string[], operator: string, executionTimeMs: number, rowsScanned: number, resultCount: number, indexUsed?: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get metrics for an index.
|
|
66
|
+
*/
|
|
67
|
+
getIndexMetrics(indexName: string): IndexMetrics | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get all index metrics.
|
|
70
|
+
*/
|
|
71
|
+
getAllMetrics(): IndexMetrics[];
|
|
72
|
+
/**
|
|
73
|
+
* Get query traces (for performance analysis).
|
|
74
|
+
*/
|
|
75
|
+
getQueryTraces(limit?: number): QueryTrace[];
|
|
76
|
+
/**
|
|
77
|
+
* Get slowest queries.
|
|
78
|
+
*/
|
|
79
|
+
getSlowestQueries(limit?: number): QueryTrace[];
|
|
80
|
+
/**
|
|
81
|
+
* Get most frequently queried fields.
|
|
82
|
+
*/
|
|
83
|
+
getFrequentFields(limit?: number): Array<[string, number]>;
|
|
84
|
+
/**
|
|
85
|
+
* Calculate hit rate for an index.
|
|
86
|
+
*/
|
|
87
|
+
calculateHitRate(indexName: string): number;
|
|
88
|
+
/**
|
|
89
|
+
* Generate performance report.
|
|
90
|
+
*/
|
|
91
|
+
generateReport(collectionName: string, period?: 'hourly' | 'daily' | 'weekly'): PerformanceReport;
|
|
92
|
+
/**
|
|
93
|
+
* Generate tuning recommendations based on usage patterns.
|
|
94
|
+
*/
|
|
95
|
+
private generateRecommendations;
|
|
96
|
+
/**
|
|
97
|
+
* Reset analytics (for testing or period rollover).
|
|
98
|
+
*/
|
|
99
|
+
reset(): void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Automatic tuning engine - applies recommendations automatically.
|
|
103
|
+
*/
|
|
104
|
+
export declare class AutoTuner {
|
|
105
|
+
private analytics;
|
|
106
|
+
private appliedRecommendations;
|
|
107
|
+
constructor(analytics: IndexAnalytics);
|
|
108
|
+
/**
|
|
109
|
+
* Check if a recommendation should be auto-applied.
|
|
110
|
+
*/
|
|
111
|
+
shouldAutoApply(recommendation: TuningRecommendation): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Apply a recommendation.
|
|
114
|
+
*/
|
|
115
|
+
applyRecommendation(recommendation: TuningRecommendation): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Get recommendations that are ready for auto-apply.
|
|
118
|
+
*/
|
|
119
|
+
getAutoApplyableRecommendations(report: PerformanceReport): TuningRecommendation[];
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=index-analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-analytics.d.ts","sourceRoot":"","sources":["../src/index-analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Analytics and Performance Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Tracks index usage, query performance, and generates optimization recommendations.
|
|
5
|
+
* Uses Rust implementation via WASM for performance-critical operations,
|
|
6
|
+
* with TypeScript fallback when WASM unavailable.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Tracks index usage and query performance.
|
|
10
|
+
*/
|
|
11
|
+
export class IndexAnalytics {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.metrics = new Map();
|
|
14
|
+
this.queryTraces = [];
|
|
15
|
+
this.fieldQueryCount = new Map();
|
|
16
|
+
this.maxTraces = 10000; // Keep last 10K queries
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Record an index usage event.
|
|
20
|
+
*/
|
|
21
|
+
recordIndexUsage(indexName, operation, timeMs) {
|
|
22
|
+
if (!this.metrics.has(indexName)) {
|
|
23
|
+
this.metrics.set(indexName, {
|
|
24
|
+
name: indexName,
|
|
25
|
+
reads: 0,
|
|
26
|
+
writes: 0,
|
|
27
|
+
hitRate: 0,
|
|
28
|
+
avgQueryTimeMs: 0,
|
|
29
|
+
avgWriteTimeMs: 0,
|
|
30
|
+
entries: 0,
|
|
31
|
+
lastUsedAt: Date.now(),
|
|
32
|
+
createdAt: Date.now(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const metric = this.metrics.get(indexName);
|
|
36
|
+
if (operation === 'read') {
|
|
37
|
+
metric.reads++;
|
|
38
|
+
metric.avgQueryTimeMs = (metric.avgQueryTimeMs * (metric.reads - 1) + timeMs) / metric.reads;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
metric.writes++;
|
|
42
|
+
metric.avgWriteTimeMs = (metric.avgWriteTimeMs * (metric.writes - 1) + timeMs) / metric.writes;
|
|
43
|
+
}
|
|
44
|
+
metric.lastUsedAt = Date.now();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Record a query execution.
|
|
48
|
+
*/
|
|
49
|
+
recordQuery(fields, operator, executionTimeMs, rowsScanned, resultCount, indexUsed) {
|
|
50
|
+
const trace = {
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
fields,
|
|
53
|
+
operator,
|
|
54
|
+
indexUsed,
|
|
55
|
+
executionTimeMs,
|
|
56
|
+
rowsScanned,
|
|
57
|
+
resultCount,
|
|
58
|
+
};
|
|
59
|
+
this.queryTraces.push(trace);
|
|
60
|
+
if (this.queryTraces.length > this.maxTraces) {
|
|
61
|
+
this.queryTraces.shift();
|
|
62
|
+
}
|
|
63
|
+
// Track field access patterns
|
|
64
|
+
for (const field of fields) {
|
|
65
|
+
this.fieldQueryCount.set(field, (this.fieldQueryCount.get(field) || 0) + 1);
|
|
66
|
+
}
|
|
67
|
+
// Update hit rate
|
|
68
|
+
if (indexUsed && this.metrics.has(indexUsed)) {
|
|
69
|
+
const metric = this.metrics.get(indexUsed);
|
|
70
|
+
metric.reads++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get metrics for an index.
|
|
75
|
+
*/
|
|
76
|
+
getIndexMetrics(indexName) {
|
|
77
|
+
return this.metrics.get(indexName) || null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get all index metrics.
|
|
81
|
+
*/
|
|
82
|
+
getAllMetrics() {
|
|
83
|
+
return Array.from(this.metrics.values());
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get query traces (for performance analysis).
|
|
87
|
+
*/
|
|
88
|
+
getQueryTraces(limit = 100) {
|
|
89
|
+
return this.queryTraces.slice(-limit);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get slowest queries.
|
|
93
|
+
*/
|
|
94
|
+
getSlowestQueries(limit = 10) {
|
|
95
|
+
return [...this.queryTraces]
|
|
96
|
+
.sort((a, b) => b.executionTimeMs - a.executionTimeMs)
|
|
97
|
+
.slice(0, limit);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get most frequently queried fields.
|
|
101
|
+
*/
|
|
102
|
+
getFrequentFields(limit = 20) {
|
|
103
|
+
return [...this.fieldQueryCount.entries()]
|
|
104
|
+
.sort((a, b) => b[1] - a[1])
|
|
105
|
+
.slice(0, limit);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Calculate hit rate for an index.
|
|
109
|
+
*/
|
|
110
|
+
calculateHitRate(indexName) {
|
|
111
|
+
const metric = this.metrics.get(indexName);
|
|
112
|
+
if (!metric || metric.reads === 0)
|
|
113
|
+
return 0;
|
|
114
|
+
const queriesUsingIndex = this.queryTraces.filter(q => q.indexUsed === indexName).length;
|
|
115
|
+
const totalQueries = this.queryTraces.length;
|
|
116
|
+
return totalQueries > 0 ? queriesUsingIndex / totalQueries : 0;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generate performance report.
|
|
120
|
+
*/
|
|
121
|
+
generateReport(collectionName, period = 'daily') {
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
const cutoff = period === 'hourly'
|
|
124
|
+
? now - 3600000
|
|
125
|
+
: period === 'daily'
|
|
126
|
+
? now - 86400000
|
|
127
|
+
: now - 604800000;
|
|
128
|
+
const recentTraces = this.queryTraces.filter(t => t.timestamp > cutoff);
|
|
129
|
+
const indexedQueries = recentTraces.filter(t => t.indexUsed).length;
|
|
130
|
+
const fullScans = recentTraces.length - indexedQueries;
|
|
131
|
+
const avgQueryTime = recentTraces.length > 0
|
|
132
|
+
? recentTraces.reduce((sum, t) => sum + t.executionTimeMs, 0) / recentTraces.length
|
|
133
|
+
: 0;
|
|
134
|
+
const slowestQuery = recentTraces.length > 0
|
|
135
|
+
? [...recentTraces].sort((a, b) => b.executionTimeMs - a.executionTimeMs)[0]
|
|
136
|
+
: undefined;
|
|
137
|
+
const metrics = this.getAllMetrics().map(m => ({
|
|
138
|
+
...m,
|
|
139
|
+
hitRate: this.calculateHitRate(m.name),
|
|
140
|
+
}));
|
|
141
|
+
const recommendations = this.generateRecommendations(recentTraces, metrics);
|
|
142
|
+
return {
|
|
143
|
+
collectionName,
|
|
144
|
+
period,
|
|
145
|
+
totalQueries: recentTraces.length,
|
|
146
|
+
indexedQueries,
|
|
147
|
+
fullScans,
|
|
148
|
+
avgQueryTimeMs: avgQueryTime,
|
|
149
|
+
slowestQuery,
|
|
150
|
+
indexMetrics: metrics,
|
|
151
|
+
recommendations,
|
|
152
|
+
timestamp: now,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate tuning recommendations based on usage patterns.
|
|
157
|
+
*/
|
|
158
|
+
generateRecommendations(traces, metrics) {
|
|
159
|
+
const recommendations = [];
|
|
160
|
+
if (traces.length === 0)
|
|
161
|
+
return recommendations;
|
|
162
|
+
// Recommend indexes for frequently scanned fields
|
|
163
|
+
const fieldScans = new Map();
|
|
164
|
+
for (const trace of traces) {
|
|
165
|
+
if (!trace.indexUsed && trace.rowsScanned > 1000) {
|
|
166
|
+
// Full scan with many rows
|
|
167
|
+
for (const field of trace.fields) {
|
|
168
|
+
fieldScans.set(field, (fieldScans.get(field) || 0) + trace.rowsScanned);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Recommend creating indexes for high-cost full scans
|
|
173
|
+
for (const [field, totalRowsScanned] of fieldScans) {
|
|
174
|
+
const avgScannedPerQuery = totalRowsScanned / traces.length;
|
|
175
|
+
if (avgScannedPerQuery > 1000) {
|
|
176
|
+
recommendations.push({
|
|
177
|
+
type: 'create_index',
|
|
178
|
+
priority: 'high',
|
|
179
|
+
reason: `Field '${field}' scanned ${avgScannedPerQuery.toFixed(0)} rows per query`,
|
|
180
|
+
expectedSpeedup: avgScannedPerQuery / 10, // Estimate 10x speedup with index
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Recommend dropping unused indexes
|
|
185
|
+
const unusedThreshold = 0.01; // Hit rate < 1%
|
|
186
|
+
for (const metric of metrics) {
|
|
187
|
+
if (metric.hitRate < unusedThreshold && metric.lastUsedAt < Date.now() - 86400000) {
|
|
188
|
+
// Not used in last 24 hours
|
|
189
|
+
recommendations.push({
|
|
190
|
+
type: 'drop_index',
|
|
191
|
+
priority: 'low',
|
|
192
|
+
indexName: metric.name,
|
|
193
|
+
reason: `Index has ${(metric.hitRate * 100).toFixed(1)}% hit rate and unused for 24h`,
|
|
194
|
+
estimatedROI: -0.01, // Minor cleanup benefit
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Recommend compound indexes
|
|
199
|
+
const fieldPairs = new Map();
|
|
200
|
+
for (const trace of traces) {
|
|
201
|
+
if (trace.fields.length === 2) {
|
|
202
|
+
const key = [...trace.fields].sort().join('|');
|
|
203
|
+
fieldPairs.set(key, (fieldPairs.get(key) || 0) + 1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const [fields, count] of fieldPairs) {
|
|
207
|
+
if (count > traces.length * 0.1) {
|
|
208
|
+
// Used in > 10% of queries
|
|
209
|
+
const fieldArray = fields.split('|');
|
|
210
|
+
recommendations.push({
|
|
211
|
+
type: 'create_index',
|
|
212
|
+
priority: 'medium',
|
|
213
|
+
reason: `Fields '${fieldArray.join(', ')}' queried together in ${count} queries`,
|
|
214
|
+
expectedSpeedup: 5, // Estimate 5x speedup with compound index
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Sort by priority and expected impact
|
|
219
|
+
recommendations.sort((a, b) => {
|
|
220
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
221
|
+
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
222
|
+
if (priorityDiff !== 0)
|
|
223
|
+
return priorityDiff;
|
|
224
|
+
return (b.expectedSpeedup || 0) - (a.expectedSpeedup || 0);
|
|
225
|
+
});
|
|
226
|
+
return recommendations.slice(0, 5); // Top 5 recommendations
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reset analytics (for testing or period rollover).
|
|
230
|
+
*/
|
|
231
|
+
reset() {
|
|
232
|
+
this.metrics.clear();
|
|
233
|
+
this.queryTraces = [];
|
|
234
|
+
this.fieldQueryCount.clear();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Automatic tuning engine - applies recommendations automatically.
|
|
239
|
+
*/
|
|
240
|
+
export class AutoTuner {
|
|
241
|
+
constructor(analytics) {
|
|
242
|
+
this.appliedRecommendations = new Set();
|
|
243
|
+
this.analytics = analytics;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Check if a recommendation should be auto-applied.
|
|
247
|
+
*/
|
|
248
|
+
shouldAutoApply(recommendation) {
|
|
249
|
+
// Only auto-apply high-priority recommendations
|
|
250
|
+
if (recommendation.priority !== 'high')
|
|
251
|
+
return false;
|
|
252
|
+
// Don't apply the same recommendation twice
|
|
253
|
+
const key = `${recommendation.type}:${recommendation.indexName || recommendation.reason}`;
|
|
254
|
+
if (this.appliedRecommendations.has(key))
|
|
255
|
+
return false;
|
|
256
|
+
// Expected speedup should be significant (5x or more)
|
|
257
|
+
if ((recommendation.expectedSpeedup || 0) < 5)
|
|
258
|
+
return false;
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Apply a recommendation.
|
|
263
|
+
*/
|
|
264
|
+
applyRecommendation(recommendation) {
|
|
265
|
+
if (!this.shouldAutoApply(recommendation))
|
|
266
|
+
return false;
|
|
267
|
+
const key = `${recommendation.type}:${recommendation.indexName || recommendation.reason}`;
|
|
268
|
+
this.appliedRecommendations.add(key);
|
|
269
|
+
// Implementation would call collection.createIndex() or drop index
|
|
270
|
+
// For now, just track that we would apply it
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get recommendations that are ready for auto-apply.
|
|
275
|
+
*/
|
|
276
|
+
getAutoApplyableRecommendations(report) {
|
|
277
|
+
return report.recommendations.filter(r => this.shouldAutoApply(r));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Backend - Dynamically selects between WASM and TypeScript implementations
|
|
3
|
+
*
|
|
4
|
+
* Uses Rust/WASM for production performance, falls back to TS for testing/development
|
|
5
|
+
*/
|
|
6
|
+
import type { IndexConfig, IndexQuery } from './index-types.js';
|
|
7
|
+
export interface IIndexBackend {
|
|
8
|
+
createIndex(config: IndexConfig): void;
|
|
9
|
+
updateRecord(recordId: string, oldValue: Record<string, any> | null, newValue: Record<string, any>): void;
|
|
10
|
+
queryIndex(name: string, query: IndexQuery): string[];
|
|
11
|
+
queryCompoundIndex(name: string, values: Record<string, any>): string[];
|
|
12
|
+
listIndexes(): IndexConfig[];
|
|
13
|
+
}
|
|
14
|
+
export declare class IndexBackend implements IIndexBackend {
|
|
15
|
+
private tsBackend;
|
|
16
|
+
private wasmBackend;
|
|
17
|
+
private useWasm;
|
|
18
|
+
constructor();
|
|
19
|
+
initializeWasm(): Promise<void>;
|
|
20
|
+
createIndex(config: IndexConfig): void;
|
|
21
|
+
updateRecord(recordId: string, oldValue: Record<string, any> | null, newValue: Record<string, any>): void;
|
|
22
|
+
queryIndex(name: string, query: IndexQuery): string[];
|
|
23
|
+
queryCompoundIndex(name: string, values: Record<string, any>): string[];
|
|
24
|
+
listIndexes(): IndexConfig[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-backend.d.ts","sourceRoot":"","sources":["../src/index-backend.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC1G,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAAC;IACtD,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC;IACxE,WAAW,IAAI,WAAW,EAAE,CAAC;CAC9B;AAyBD,qBAAa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,OAAO,CAAkB;;IAM3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrC,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAYtC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAezG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE;IAYrD,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAgBvE,WAAW,IAAI,WAAW,EAAE;CAW7B"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Backend - Dynamically selects between WASM and TypeScript implementations
|
|
3
|
+
*
|
|
4
|
+
* Uses Rust/WASM for production performance, falls back to TS for testing/development
|
|
5
|
+
*/
|
|
6
|
+
let wasmIndexModule = null;
|
|
7
|
+
let wasmAvailable = false;
|
|
8
|
+
async function loadWasm() {
|
|
9
|
+
if (wasmIndexModule !== null)
|
|
10
|
+
return wasmIndexModule;
|
|
11
|
+
try {
|
|
12
|
+
// Try to import WASM module
|
|
13
|
+
// @ts-ignore - WASM module is optional and built separately
|
|
14
|
+
const wasmModule = await import('@feltdb/wasm');
|
|
15
|
+
wasmIndexModule = wasmModule;
|
|
16
|
+
wasmAvailable = true;
|
|
17
|
+
return wasmModule;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Fall back to TypeScript implementation
|
|
21
|
+
wasmAvailable = false;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Import TS implementation for fallback
|
|
26
|
+
import { IndexManager as TsIndexManager } from './index-manager.js';
|
|
27
|
+
export class IndexBackend {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.tsBackend = null;
|
|
30
|
+
this.wasmBackend = null;
|
|
31
|
+
this.useWasm = false;
|
|
32
|
+
this.tsBackend = new TsIndexManager();
|
|
33
|
+
}
|
|
34
|
+
async initializeWasm() {
|
|
35
|
+
if (this.wasmBackend !== null)
|
|
36
|
+
return;
|
|
37
|
+
const wasm = await loadWasm();
|
|
38
|
+
if (wasm && wasm.WasmIndexManager) {
|
|
39
|
+
try {
|
|
40
|
+
this.wasmBackend = new wasm.WasmIndexManager();
|
|
41
|
+
this.useWasm = true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
this.useWasm = false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
createIndex(config) {
|
|
49
|
+
if (this.useWasm && this.wasmBackend) {
|
|
50
|
+
try {
|
|
51
|
+
this.wasmBackend.create_index(JSON.stringify(config));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Fall back to TS
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
this.tsBackend?.createIndex(config);
|
|
59
|
+
}
|
|
60
|
+
updateRecord(recordId, oldValue, newValue) {
|
|
61
|
+
if (this.useWasm && this.wasmBackend) {
|
|
62
|
+
try {
|
|
63
|
+
// Convert record to index-friendly string format
|
|
64
|
+
const oldStr = oldValue ? JSON.stringify(oldValue) : null;
|
|
65
|
+
const newStr = JSON.stringify(newValue);
|
|
66
|
+
this.wasmBackend.update_record(recordId, oldStr, newStr);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Fall back to TS
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.tsBackend?.updateRecord(recordId, oldValue, newValue);
|
|
74
|
+
}
|
|
75
|
+
queryIndex(name, query) {
|
|
76
|
+
if (this.useWasm && this.wasmBackend) {
|
|
77
|
+
try {
|
|
78
|
+
const results = this.wasmBackend.query_index(name, query.operator, String(query.value || ''));
|
|
79
|
+
return JSON.parse(results);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Fall back to TS
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return this.tsBackend?.queryIndex(name, query) || [];
|
|
86
|
+
}
|
|
87
|
+
queryCompoundIndex(name, values) {
|
|
88
|
+
if (this.useWasm && this.wasmBackend) {
|
|
89
|
+
try {
|
|
90
|
+
const valueString = Object.values(values)
|
|
91
|
+
.map(v => String(v))
|
|
92
|
+
.join('\0');
|
|
93
|
+
const results = this.wasmBackend.query_index(name, 'eq', valueString);
|
|
94
|
+
return JSON.parse(results);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Fall back to TS
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// For TS backend, this would need a similar compound query interface
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
listIndexes() {
|
|
104
|
+
if (this.useWasm && this.wasmBackend) {
|
|
105
|
+
try {
|
|
106
|
+
const indexes = this.wasmBackend.list_indexes();
|
|
107
|
+
return JSON.parse(indexes);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Fall back to TS
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return this.tsBackend?.listIndexes() || [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Performance Dashboard
|
|
3
|
+
*
|
|
4
|
+
* Real-time visualization and monitoring of index performance metrics.
|
|
5
|
+
*/
|
|
6
|
+
import type { IndexMetrics, PerformanceReport, TuningRecommendation } from './index-analytics.js';
|
|
7
|
+
export interface DashboardMetrics {
|
|
8
|
+
timestamp: number;
|
|
9
|
+
indexes: IndexDashboard[];
|
|
10
|
+
queryPerformance: QueryPerformanceSummary;
|
|
11
|
+
recommendations: TuningRecommendation[];
|
|
12
|
+
}
|
|
13
|
+
export interface IndexDashboard {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
reads: number;
|
|
17
|
+
writes: number;
|
|
18
|
+
hitRate: number;
|
|
19
|
+
avgQueryMs: number;
|
|
20
|
+
avgWriteMs: number;
|
|
21
|
+
entries: number;
|
|
22
|
+
lastUsedRelative: string;
|
|
23
|
+
health: 'healthy' | 'warning' | 'critical';
|
|
24
|
+
healthReason?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface QueryPerformanceSummary {
|
|
27
|
+
totalQueries: number;
|
|
28
|
+
indexedQueries: number;
|
|
29
|
+
fullScans: number;
|
|
30
|
+
indexUsageRate: number;
|
|
31
|
+
avgQueryMs: number;
|
|
32
|
+
p50QueryMs: number;
|
|
33
|
+
p95QueryMs: number;
|
|
34
|
+
p99QueryMs: number;
|
|
35
|
+
slowQueries: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Dashboard formatter - converts raw metrics to display format.
|
|
39
|
+
*/
|
|
40
|
+
export declare class DashboardFormatter {
|
|
41
|
+
/**
|
|
42
|
+
* Format index metrics for display.
|
|
43
|
+
*/
|
|
44
|
+
static formatIndexMetrics(metrics: IndexMetrics): IndexDashboard;
|
|
45
|
+
/**
|
|
46
|
+
* Format performance report for display.
|
|
47
|
+
*/
|
|
48
|
+
static formatReport(report: PerformanceReport): DashboardMetrics;
|
|
49
|
+
/**
|
|
50
|
+
* Format duration for display.
|
|
51
|
+
*/
|
|
52
|
+
private static formatDuration;
|
|
53
|
+
/**
|
|
54
|
+
* Generate ASCII dashboard.
|
|
55
|
+
*/
|
|
56
|
+
static renderASCII(dashboard: DashboardMetrics): string;
|
|
57
|
+
/**
|
|
58
|
+
* Generate JSON dashboard data.
|
|
59
|
+
*/
|
|
60
|
+
static renderJSON(dashboard: DashboardMetrics): string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Real-time dashboard updates via events.
|
|
64
|
+
*/
|
|
65
|
+
export declare class LiveDashboard {
|
|
66
|
+
private metrics;
|
|
67
|
+
private listeners;
|
|
68
|
+
private updateInterval;
|
|
69
|
+
private intervalHandle?;
|
|
70
|
+
/**
|
|
71
|
+
* Start streaming dashboard updates.
|
|
72
|
+
*/
|
|
73
|
+
start(updateFn: () => DashboardMetrics, interval?: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Stop streaming updates.
|
|
76
|
+
*/
|
|
77
|
+
stop(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Subscribe to dashboard updates.
|
|
80
|
+
*/
|
|
81
|
+
subscribe(listener: (metrics: DashboardMetrics) => void): () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Get current dashboard state.
|
|
84
|
+
*/
|
|
85
|
+
getCurrent(): DashboardMetrics | null;
|
|
86
|
+
private notifyListeners;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Alert system for performance anomalies.
|
|
90
|
+
*/
|
|
91
|
+
export interface PerformanceAlert {
|
|
92
|
+
severity: 'info' | 'warning' | 'critical';
|
|
93
|
+
type: 'slow_query' | 'high_full_scans' | 'unused_index' | 'index_overhead';
|
|
94
|
+
message: string;
|
|
95
|
+
timestamp: number;
|
|
96
|
+
metrics?: Record<string, number | string>;
|
|
97
|
+
}
|
|
98
|
+
export declare class PerformanceAlerter {
|
|
99
|
+
private alerts;
|
|
100
|
+
private thresholds;
|
|
101
|
+
/**
|
|
102
|
+
* Check dashboard against thresholds and generate alerts.
|
|
103
|
+
*/
|
|
104
|
+
checkThresholds(dashboard: DashboardMetrics): PerformanceAlert[];
|
|
105
|
+
/**
|
|
106
|
+
* Get recent alerts.
|
|
107
|
+
*/
|
|
108
|
+
getRecentAlerts(limit?: number): PerformanceAlert[];
|
|
109
|
+
/**
|
|
110
|
+
* Clear old alerts.
|
|
111
|
+
*/
|
|
112
|
+
clearOldAlerts(olderThanMs?: number): void;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=index-dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-dashboard.d.ts","sourceRoot":"","sources":["../src/index-dashboard.ts"],"names":[],"mappings":"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"}
|