@gati-framework/runtime
Version:
Gati runtime execution engine for running handler-based applications
127 lines • 4.01 kB
JavaScript
/**
* @module runtime/trace-storage
* @description Storage for request traces with TTL-based expiration
*/
/**
* In-memory trace storage with TTL
*/
export class InMemoryTraceStorage {
traces = new Map();
config;
cleanupInterval;
constructor(config = {}) {
this.config = {
maxTraces: config.maxTraces ?? 1000,
ttlMs: config.ttlMs ?? 300000, // 5 minutes
compression: config.compression ?? true,
};
this.startCleanup();
}
async storeTrace(trace) {
const stored = {
trace: this.config.compression ? this.compress(trace) : trace,
expiresAt: Date.now() + this.config.ttlMs,
compressed: this.config.compression,
};
this.traces.set(trace.id, stored);
this.enforceLimit();
}
async getTrace(traceId) {
const stored = this.traces.get(traceId);
if (!stored)
return null;
if (Date.now() > stored.expiresAt) {
this.traces.delete(traceId);
return null;
}
return stored.compressed ? this.decompress(stored.trace) : stored.trace;
}
async listTraces(filter) {
const now = Date.now();
const traces = [];
for (const [id, stored] of this.traces.entries()) {
if (now > stored.expiresAt) {
this.traces.delete(id);
continue;
}
const trace = stored.compressed ? this.decompress(stored.trace) : stored.trace;
if (this.matchesFilter(trace, filter)) {
traces.push(trace);
}
}
// Apply limit
if (filter?.limit) {
return traces.slice(0, filter.limit);
}
return traces;
}
async deleteTrace(traceId) {
return this.traces.delete(traceId);
}
async clear() {
this.traces.clear();
}
getStats() {
let size = 0;
for (const stored of this.traces.values()) {
size += JSON.stringify(stored.trace).length;
}
return { count: this.traces.size, size };
}
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
this.traces.clear();
}
startCleanup() {
this.cleanupInterval = setInterval(() => {
const now = Date.now();
for (const [id, stored] of this.traces.entries()) {
if (now > stored.expiresAt) {
this.traces.delete(id);
}
}
}, 60000); // Every minute
if (this.cleanupInterval.unref) {
this.cleanupInterval.unref();
}
}
enforceLimit() {
if (this.traces.size <= this.config.maxTraces)
return;
const entries = Array.from(this.traces.entries());
entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt);
const toRemove = entries.slice(0, entries.length - this.config.maxTraces);
toRemove.forEach(([id]) => this.traces.delete(id));
}
matchesFilter(trace, filter) {
if (!filter)
return true;
if (filter.status && trace.status !== filter.status)
return false;
if (filter.path && !trace.request.path.includes(filter.path))
return false;
if (filter.startTime && trace.timestamp < filter.startTime)
return false;
if (filter.endTime && trace.timestamp > filter.endTime)
return false;
return true;
}
compress(trace) {
// Simple compression: just return as-is for now
// Response interface doesn't have body property
return trace;
}
decompress(trace) {
// No actual decompression needed for our simple compression
return trace;
}
}
/**
* Create a trace storage instance
*/
export function createTraceStorage(config) {
return new InMemoryTraceStorage(config);
}
//# sourceMappingURL=trace-storage.js.map