diff --git a/lib/algos/list/delimiterCurrent.ts b/lib/algos/list/delimiterCurrent.ts index a839101dc..0fe11d511 100644 --- a/lib/algos/list/delimiterCurrent.ts +++ b/lib/algos/list/delimiterCurrent.ts @@ -48,6 +48,19 @@ class DelimiterCurrent extends DelimiterMaster { }; } + // Limit the number of documents MongoDB returns to bound the + // in-memory sort when lifecycle indexes are used (these indexes + // order by value.last-modified, forcing a re-sort on _id). Without + // a limit, MongoDB must sort all matching documents — which can + // exceed the 100MB memory cap and spill to disk. + // We use maxScannedLifecycleListingEntries (default 10,000) as the + // bound because it counts documents scanned, which maps directly + // to cursor documents regardless of bucket format (v0 or v1). + // The +1 allows the listing algorithm to detect truncation. + if (this.maxScannedLifecycleListingEntries) { + params.limit = this.maxScannedLifecycleListingEntries + 1; + } + return params; } diff --git a/tests/unit/algos/list/delimiterCurrent.spec.js b/tests/unit/algos/list/delimiterCurrent.spec.js index 0c06ac07f..653477d94 100644 --- a/tests/unit/algos/list/delimiterCurrent.spec.js +++ b/tests/unit/algos/list/delimiterCurrent.spec.js @@ -56,6 +56,7 @@ function getListingKey(key, vFormat) { }, gt: getListingKey('premark', v), lt: getListingKey('prf', v), + limit: maxScannedLifecycleListingEntries + 1, }; assert.deepStrictEqual(delimiter.genMDParams(), expectedParams); assert.strictEqual(delimiter.maxScannedLifecycleListingEntries, 2);