Skip to content

Commit b008854

Browse files
committed
refactor: use a sorted array of logs in advance
1 parent bd2e5f9 commit b008854

File tree

1 file changed

+34
-40
lines changed

1 file changed

+34
-40
lines changed

packages/mongodb-log-writer/src/mongo-log-manager.ts

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { ObjectId } from 'bson';
33
import { once } from 'events';
44
import { createWriteStream, promises as fs } from 'fs';
55
import { createGzip, constants as zlibConstants } from 'zlib';
6-
import { Heap } from 'heap-js';
76
import { MongoLogWriter } from './mongo-log-writer';
87
import { Writable } from 'stream';
98

@@ -40,9 +39,37 @@ export class MongoLogManager {
4039
/** Clean up log files older than `retentionDays`. */
4140
async cleanupOldLogFiles(maxDurationMs = 5_000): Promise<void> {
4241
const dir = this._options.directory;
43-
let dirHandle;
42+
const sortedLogFiles: {
43+
fullPath: string;
44+
id: string;
45+
size?: number;
46+
}[] = [];
47+
let usedStorageSize = this._options.retentionGB ? 0 : -Infinity;
48+
4449
try {
45-
dirHandle = await fs.opendir(dir);
50+
const files = await fs.readdir(dir, { withFileTypes: true });
51+
for (const file of files) {
52+
const { id } =
53+
/^(?<id>[a-f0-9]{24})_log(\.gz)?$/i.exec(file.name)?.groups ?? {};
54+
55+
if (!file.isFile() || !id) {
56+
continue;
57+
}
58+
59+
const fullPath = path.join(dir, file.name);
60+
let size: number | undefined;
61+
if (this._options.retentionGB) {
62+
try {
63+
size = (await fs.stat(fullPath)).size;
64+
usedStorageSize += size;
65+
} catch (err) {
66+
this._options.onerror(err as Error, fullPath);
67+
continue;
68+
}
69+
}
70+
71+
sortedLogFiles.push({ fullPath, id, size });
72+
}
4673
} catch {
4774
return;
4875
}
@@ -51,32 +78,19 @@ export class MongoLogManager {
5178
// Delete files older than N days
5279
const deletionCutoffTimestamp =
5380
deletionStartTimestamp - this._options.retentionDays * 86400 * 1000;
54-
// Store the known set of least recent files in a heap in order to be able to
55-
// delete all but the most recent N files.
56-
const leastRecentFileHeap = new Heap<{
57-
fileTimestamp: number;
58-
fullPath: string;
59-
fileSize?: number;
60-
}>((a, b) => a.fileTimestamp - b.fileTimestamp);
6181

6282
const storageSizeLimit = this._options.retentionGB
6383
? this._options.retentionGB * 1024 * 1024 * 1024
6484
: Infinity;
65-
let usedStorageSize = this._options.retentionGB ? 0 : -Infinity;
6685

67-
for await (const dirent of dirHandle) {
86+
for await (const { id, fullPath } of [...sortedLogFiles]) {
6887
// Cap the overall time spent inside this function. Consider situations like
6988
// a large number of machines using a shared network-mounted $HOME directory
7089
// where lots and lots of log files end up and filesystem operations happen
7190
// with network latency.
7291
if (Date.now() - deletionStartTimestamp > maxDurationMs) break;
7392

74-
if (!dirent.isFile()) continue;
75-
const { id } =
76-
/^(?<id>[a-f0-9]{24})_log(\.gz)?$/i.exec(dirent.name)?.groups ?? {};
77-
if (!id) continue;
7893
const fileTimestamp = +new ObjectId(id).getTimestamp();
79-
const fullPath = path.join(dir, dirent.name);
8094
let toDelete:
8195
| {
8296
fullPath: string;
@@ -94,32 +108,13 @@ export class MongoLogManager {
94108
fullPath,
95109
};
96110
} else if (this._options.retentionGB || this._options.maxLogFileCount) {
97-
let fileSize: number | undefined;
98-
99-
if (this._options.retentionGB) {
100-
try {
101-
fileSize = (await fs.stat(fullPath)).size;
102-
if (this._options.retentionGB) {
103-
usedStorageSize += fileSize;
104-
}
105-
} catch (err) {
106-
this._options.onerror(err as Error, fullPath);
107-
}
108-
}
109-
110-
leastRecentFileHeap.push({
111-
fullPath,
112-
fileTimestamp,
113-
fileSize,
114-
});
115-
116111
const reachedMaxStorageSize = usedStorageSize > storageSizeLimit;
117112
const reachedMaxFileCount =
118113
this._options.maxLogFileCount &&
119-
leastRecentFileHeap.size() > this._options.maxLogFileCount;
114+
sortedLogFiles.length > this._options.maxLogFileCount;
120115

121116
if (reachedMaxStorageSize || reachedMaxFileCount) {
122-
toDelete = leastRecentFileHeap.pop();
117+
toDelete = sortedLogFiles.shift();
123118
}
124119
}
125120

@@ -132,8 +127,7 @@ export class MongoLogManager {
132127
// eslint-disable-next-line @typescript-eslint/no-explicit-any
133128
} catch (err: any) {
134129
if (err?.code !== 'ENOENT') {
135-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
136-
this._options.onerror(err, fullPath);
130+
this._options.onerror(err as Error, fullPath);
137131
}
138132
}
139133
}

0 commit comments

Comments
 (0)