@@ -17,6 +17,8 @@ interface MongoLogOptions {
17
17
retentionDays : number ;
18
18
/** The maximal number of log files which are kept. */
19
19
maxLogFileCount ?: number ;
20
+ /** The maximal GB of log files which are kept. */
21
+ logRetentionGB ?: number ;
20
22
/** A handler for warnings related to a specific filesystem path. */
21
23
onerror : ( err : Error , path : string ) => unknown | Promise < void > ;
22
24
/** A handler for errors related to a specific filesystem path. */
@@ -54,8 +56,15 @@ export class MongoLogManager {
54
56
const leastRecentFileHeap = new Heap < {
55
57
fileTimestamp : number ;
56
58
fullPath : string ;
59
+ fileSize ?: number ;
57
60
} > ( ( a , b ) => a . fileTimestamp - b . fileTimestamp ) ;
58
61
62
+ const storageSizeLimit = this . _options . logRetentionGB
63
+ ? this . _options . logRetentionGB * 1024 * 1024 * 1024
64
+ : Infinity ;
65
+ let usedStorageSize = this . _options . logRetentionGB ? 0 : - Infinity ;
66
+ // eslint-disable-next-line no-console
67
+
59
68
for await ( const dirent of dirHandle ) {
60
69
// Cap the overall time spent inside this function. Consider situations like
61
70
// a large number of machines using a shared network-mounted $HOME directory
@@ -69,23 +78,53 @@ export class MongoLogManager {
69
78
if ( ! id ) continue ;
70
79
const fileTimestamp = + new ObjectId ( id ) . getTimestamp ( ) ;
71
80
const fullPath = path . join ( dir , dirent . name ) ;
72
- let toDelete : string | undefined ;
81
+ let toDelete :
82
+ | {
83
+ fullPath : string ;
84
+ /** If the file wasn't deleted right away and there is a
85
+ * retention size limit, its size should be accounted */
86
+ fileSize ?: number ;
87
+ }
88
+ | undefined ;
73
89
74
90
// If the file is older than expected, delete it. If the file is recent,
75
91
// add it to the list of seen files, and if that list is too large, remove
76
92
// the least recent file we've seen so far.
77
93
if ( fileTimestamp < deletionCutoffTimestamp ) {
78
- toDelete = fullPath ;
79
- } else if ( this . _options . maxLogFileCount ) {
80
- leastRecentFileHeap . push ( { fullPath, fileTimestamp } ) ;
81
- if ( leastRecentFileHeap . size ( ) > this . _options . maxLogFileCount ) {
82
- toDelete = leastRecentFileHeap . pop ( ) ?. fullPath ;
94
+ toDelete = {
95
+ fullPath,
96
+ } ;
97
+ } else if (
98
+ this . _options . logRetentionGB ||
99
+ this . _options . maxLogFileCount
100
+ ) {
101
+ const fileSize = ( await fs . stat ( fullPath ) ) . size ;
102
+ if ( this . _options . logRetentionGB ) {
103
+ usedStorageSize += fileSize ;
104
+ }
105
+
106
+ leastRecentFileHeap . push ( {
107
+ fullPath,
108
+ fileTimestamp,
109
+ fileSize,
110
+ } ) ;
111
+
112
+ const reachedMaxStorageSize = usedStorageSize > storageSizeLimit ;
113
+ const reachedMaxFileCount =
114
+ this . _options . maxLogFileCount &&
115
+ leastRecentFileHeap . size ( ) > this . _options . maxLogFileCount ;
116
+
117
+ if ( reachedMaxStorageSize || reachedMaxFileCount ) {
118
+ toDelete = leastRecentFileHeap . pop ( ) ;
83
119
}
84
120
}
85
121
86
122
if ( ! toDelete ) continue ;
87
123
try {
88
- await fs . unlink ( toDelete ) ;
124
+ await fs . unlink ( toDelete . fullPath ) ;
125
+ if ( toDelete . fileSize ) {
126
+ usedStorageSize -= toDelete . fileSize ;
127
+ }
89
128
// eslint-disable-next-line @typescript-eslint/no-explicit-any
90
129
} catch ( err : any ) {
91
130
if ( err ?. code !== 'ENOENT' ) {
0 commit comments