-
Notifications
You must be signed in to change notification settings - Fork 8
feat(mongodb-log-writer): add retentionGB
configuration MONGOSH-1985
#504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
b638b34
6e4e929
bd2e5f9
b008854
99afade
9f3c1bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,8 @@ interface MongoLogOptions { | |
retentionDays: number; | ||
/** The maximal number of log files which are kept. */ | ||
maxLogFileCount?: number; | ||
/** The maximal GB of log files which are kept. */ | ||
retentionGB?: number; | ||
/** A handler for warnings related to a specific filesystem path. */ | ||
onerror: (err: Error, path: string) => unknown | Promise<void>; | ||
/** A handler for errors related to a specific filesystem path. */ | ||
|
@@ -54,8 +56,14 @@ export class MongoLogManager { | |
const leastRecentFileHeap = new Heap<{ | ||
fileTimestamp: number; | ||
fullPath: string; | ||
fileSize?: number; | ||
}>((a, b) => a.fileTimestamp - b.fileTimestamp); | ||
|
||
const storageSizeLimit = this._options.retentionGB | ||
? this._options.retentionGB * 1024 * 1024 * 1024 | ||
: Infinity; | ||
let usedStorageSize = this._options.retentionGB ? 0 : -Infinity; | ||
|
||
for await (const dirent of dirHandle) { | ||
// Cap the overall time spent inside this function. Consider situations like | ||
// a large number of machines using a shared network-mounted $HOME directory | ||
|
@@ -69,23 +77,50 @@ export class MongoLogManager { | |
if (!id) continue; | ||
const fileTimestamp = +new ObjectId(id).getTimestamp(); | ||
const fullPath = path.join(dir, dirent.name); | ||
let toDelete: string | undefined; | ||
let toDelete: | ||
| { | ||
fullPath: string; | ||
/** If the file wasn't deleted right away and there is a | ||
* retention size limit, its size should be accounted */ | ||
fileSize?: number; | ||
} | ||
| undefined; | ||
|
||
// If the file is older than expected, delete it. If the file is recent, | ||
// add it to the list of seen files, and if that list is too large, remove | ||
// the least recent file we've seen so far. | ||
if (fileTimestamp < deletionCutoffTimestamp) { | ||
toDelete = fullPath; | ||
} else if (this._options.maxLogFileCount) { | ||
leastRecentFileHeap.push({ fullPath, fileTimestamp }); | ||
if (leastRecentFileHeap.size() > this._options.maxLogFileCount) { | ||
toDelete = leastRecentFileHeap.pop()?.fullPath; | ||
toDelete = { | ||
fullPath, | ||
}; | ||
} else if (this._options.retentionGB || this._options.maxLogFileCount) { | ||
const fileSize = (await fs.stat(fullPath)).size; | ||
addaleax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (this._options.retentionGB) { | ||
usedStorageSize += fileSize; | ||
} | ||
|
||
leastRecentFileHeap.push({ | ||
fullPath, | ||
fileTimestamp, | ||
fileSize, | ||
}); | ||
|
||
const reachedMaxStorageSize = usedStorageSize > storageSizeLimit; | ||
const reachedMaxFileCount = | ||
this._options.maxLogFileCount && | ||
leastRecentFileHeap.size() > this._options.maxLogFileCount; | ||
|
||
if (reachedMaxStorageSize || reachedMaxFileCount) { | ||
toDelete = leastRecentFileHeap.pop(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So ... took some time to think this through, but the problem with this approach is that we'll generally want to delete the oldest files that are necessary to ensure that max file count + max file size limitations But with this approach, that's no longer the case – you could end up with e.g. the most recent file being deleted, if it happens to be the first file we read from the directory and it's already over the size limit – right? (I'm not sure if we need to let go of the heap as the underlying data structure, certainly wouldn't be the end of the world, but I'm pretty sure that the "read one file, check conditions, maybe delete one file" approach won't work anymore) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm yeah, we could get rid of the heap, sort by creation date (which would be same as the log's name?) from the getgo and adjust the logic accordingly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that's an assumption I'd still keep around 👍 |
||
} | ||
} | ||
|
||
if (!toDelete) continue; | ||
try { | ||
await fs.unlink(toDelete); | ||
await fs.unlink(toDelete.fullPath); | ||
if (toDelete.fileSize) { | ||
usedStorageSize -= toDelete.fileSize; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} catch (err: any) { | ||
if (err?.code !== 'ENOENT') { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.