diff --git a/test/js/bun/s3/s3-stream-leak-fixture.js b/test/js/bun/s3/s3-stream-leak-fixture.js new file mode 100644 index 00000000000000..315f6d76948aba --- /dev/null +++ b/test/js/bun/s3/s3-stream-leak-fixture.js @@ -0,0 +1,37 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 10; +const dest = process.argv.at(-1); +const { randomUUID } = require("crypto"); + +const s3Dest = randomUUID(); + +const s3file = Bun.s3(s3Dest); +async function readLargeFile(inputType) { + const stream = s3file.stream(); + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + } + Bun.gc(true); +} +async function run(inputType) { + await s3file.write(inputType); + for (let i = 0; i < 10; i++) { + await readLargeFile(inputType); + Bun.gc(true); + if (!MAX_ALLOWED_MEMORY_USAGE) { + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + console.log("Memory usage:", rss, "MB"); + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + await s3file.unlink(); + throw new Error("Memory usage is too high"); + } + } +} +await run(new Buffer(1024 * 1024 * 1).fill("A".charCodeAt(0)).toString("utf-8")); +await s3file.unlink(); diff --git a/test/js/bun/s3/s3.test.ts b/test/js/bun/s3/s3.test.ts index ef3f2551a21e6c..566f7baad5aaab 100644 --- a/test/js/bun/s3/s3.test.ts +++ b/test/js/bun/s3/s3.test.ts @@ -620,6 +620,37 @@ describe.skipIf(!s3Options.accessKeyId)("s3", () => { }); describe("leak tests", () => { + it( + "fsFile.stream() should not leak", + async () => { + const dir = tempDirWithFiles("bun-write-leak-fixture", { + "s3-stream-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "s3-stream-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "s3-stream-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); it( "fsFile.writer().write() should not leak", async () => {