Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 26x 26x 26x 9x 1x 1x 1x 2x 2x 1x 2x 2x 18x 58x 58x 58x 57x 57x 58x 1x 1x 5x 1x 52x 26x 25x 25x 25x 25x 57x 20x 20x | import type { LogCluster } from "./LogCluster.js";
/**
* LRU-backed cache for LogCluster instances.
*
* Maps 1:1 to Python `LogClusterCache(LRUCache)` class (drain.py L54-L70).
*
* Critical design decisions:
* - The `get()` method bypasses LRU eviction tracking (used by `fastMatch` for
* low-overhead lookups without perturbing the access order).
* - The `touch()` method explicitly updates the LRU access record (used by
* `addLogMessage` after a cluster is matched and updated).
* - Eviction removes the least-recently-used cluster when `maxSize` is exceeded.
*
* This dual-access pattern matches Python's behavior where `Cache.__getitem__`
* triggers LRU update but `LogClusterCache.get()` (calling `Cache.__getitem__`
* directly) does not.
*/
export class LogClusterCache implements Map<number, LogCluster> {
private readonly _store: Map<number, LogCluster>;
private readonly _accessOrder: number[] = [];
private readonly _maxSize: number;
/**
* @param maxSize - Maximum number of clusters before LRU eviction begins.
*/
constructor(maxSize: number) {
this._maxSize = maxSize;
this._store = new Map();
}
// ============================================================
// Map interface implementation
// ============================================================
get size(): number {
return this._store.size;
}
get [Symbol.toStringTag](): string {
return "LogClusterCache";
}
clear(): void {
this._store.clear();
this._accessOrder.length = 0;
}
delete(key: number): boolean {
const idx = this._accessOrder.indexOf(key);
if (idx >= 0) {
this._accessOrder.splice(idx, 1);
}
return this._store.delete(key);
}
forEach(
callback: (value: LogCluster, key: number, map: Map<number, LogCluster>) => void,
): void {
this._store.forEach((value, key) => callback(value, key, this));
}
has(key: number): boolean {
return this._store.has(key);
}
set(key: number, value: LogCluster): this {
const existed = this._store.has(key);
this._store.set(key, value);
if (!existed) {
this._accessOrder.push(key);
this._evictIfNeeded();
}
// If the key already existed, we don't update the access order.
// This mirrors Python behavior where replacing an existing key
// doesn't change its LRU position.
return this;
}
entries(): IterableIterator<[number, LogCluster]> {
return this._store.entries();
}
keys(): IterableIterator<number> {
return this._store.keys();
}
values(): IterableIterator<LogCluster> {
return this._store.values();
}
[Symbol.iterator](): IterableIterator<[number, LogCluster]> {
return this._store[Symbol.iterator]();
}
// ============================================================
// LRU-specific methods
// ============================================================
/**
* Retrieves a cluster WITHOUT updating the LRU access order.
*
* Python: LogClusterCache.get(key) → Cache.__getitem__(key)
*
* Used by `fastMatch` for efficient lookups — we don't want every
* similarity check to perturb the eviction order.
*
* @returns The cluster, or undefined if the key doesn't exist.
*/
get(key: number): LogCluster | undefined {
return this._store.get(key);
}
/**
* Explicitly records an access to the given key for LRU tracking.
*
* Python: self.id_to_cluster[cluster.cluster_id]
* (triggers Cache.__getitem__ which updates the access order)
*
* Call this after a cluster has been matched and updated in `addLogMessage`
* to ensure the cluster is marked as recently used.
*/
touch(key: number): void {
if (!this._store.has(key)) return;
const idx = this._accessOrder.indexOf(key);
Eif (idx >= 0) {
this._accessOrder.splice(idx, 1);
this._accessOrder.push(key);
}
}
// ============================================================
// Internal
// ============================================================
/**
* Evicts the least-recently-used cluster(s) until the size is within limits.
*/
private _evictIfNeeded(): void {
while (this._store.size > this._maxSize && this._accessOrder.length > 0) {
const lruKey = this._accessOrder.shift()!;
this._store.delete(lruKey);
}
}
}
|