Skip to content

Commit bd2e5f9

Browse files
committed
feat: handle fs.stat error by emitting an error and ignoring the file
1 parent 6e4e929 commit bd2e5f9

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { MongoLogManager, mongoLogId } from '.';
22
import { ObjectId } from 'bson';
33
import { once } from 'events';
4+
import type { Stats } from 'fs';
45
import { promises as fs } from 'fs';
56
import path from 'path';
67
import os from 'os';
@@ -27,6 +28,7 @@ describe('MongoLogManager', function () {
2728
});
2829
afterEach(async function () {
2930
await fs.rmdir(directory, { recursive: true });
31+
sinon.restore();
3032
});
3133

3234
it('allows creating and writing to log files', async function () {
@@ -124,6 +126,58 @@ describe('MongoLogManager', function () {
124126
expect(await getFilesState(paths)).to.equal('0000011111');
125127
});
126128

129+
it('if fs.stat fails, it errors and is not considered towards the logs limit', async function () {
130+
const manager = new MongoLogManager({
131+
directory,
132+
retentionDays,
133+
retentionGB: 3,
134+
onwarn,
135+
onerror,
136+
});
137+
138+
const offset = Math.floor(Date.now() / 1000);
139+
140+
const faultyFile = path.join(
141+
directory,
142+
ObjectId.createFromTime(offset - 10).toHexString() + '_log'
143+
);
144+
await fs.writeFile(faultyFile, '');
145+
146+
const faultyFileError = new Error('test error');
147+
148+
const validFiles: string[] = [];
149+
// Create 5 valid files.
150+
for (let i = 5; i >= 0; i--) {
151+
const filename = path.join(
152+
directory,
153+
ObjectId.createFromTime(offset - i).toHexString() + '_log'
154+
);
155+
await fs.writeFile(filename, '');
156+
validFiles.push(filename);
157+
}
158+
159+
expect(onerror).not.called;
160+
161+
const fsStatStub = sinon.stub(fs, 'stat');
162+
163+
fsStatStub.resolves({
164+
size: 1024 * 1024 * 1024,
165+
} as Stats);
166+
fsStatStub.withArgs(faultyFile).rejects(faultyFileError);
167+
168+
await manager.cleanupOldLogFiles();
169+
170+
expect(onerror).calledOnceWithExactly(faultyFileError, faultyFile);
171+
172+
// fs.stat is stubbed so getFilesState will not be accurate.
173+
const leftoverFiles = (await fs.readdir(directory))
174+
.sort()
175+
.map((file) => path.join(directory, file));
176+
177+
expect(leftoverFiles).to.have.lengthOf(4);
178+
expect(leftoverFiles).deep.equals([faultyFile, ...validFiles.slice(3)]);
179+
});
180+
127181
it('cleans up least recent log files when requested with a storage limit', async function () {
128182
const manager = new MongoLogManager({
129183
directory,

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ interface MongoLogOptions {
1919
maxLogFileCount?: number;
2020
/** The maximal GB of log files which are kept. */
2121
retentionGB?: number;
22-
/** A handler for warnings related to a specific filesystem path. */
23-
onerror: (err: Error, path: string) => unknown | Promise<void>;
2422
/** A handler for errors related to a specific filesystem path. */
23+
onerror: (err: Error, path: string) => unknown | Promise<void>;
24+
/** A handler for warnings related to a specific filesystem path. */
2525
onwarn: (err: Error, path: string) => unknown | Promise<void>;
2626
}
2727

@@ -94,9 +94,17 @@ export class MongoLogManager {
9494
fullPath,
9595
};
9696
} else if (this._options.retentionGB || this._options.maxLogFileCount) {
97-
const fileSize = (await fs.stat(fullPath)).size;
97+
let fileSize: number | undefined;
98+
9899
if (this._options.retentionGB) {
99-
usedStorageSize += fileSize;
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+
}
100108
}
101109

102110
leastRecentFileHeap.push({

0 commit comments

Comments
 (0)