Skip to content

Possible memory leak in StreamDispatcher #4287

@seuuuul

Description

@seuuuul

Please describe the problem you are having in as much detail as possible:
The bot is continuously (every X seconds) streaming a different Youtube video with a random seek to a voice channel. The memory (heap size) keeps increasing as time goes on.

I don't understand much about memory/heap, but these are some heap snapshots I made with the node inspector, the last snapshot is 30 minutes in. The actual memory on my pc is larger when you count all the node processes.

What I think keeps allocating high 'retained size' are ArrayBuffer and Buffer, a lot of them with references to StreamDispatcher.js, VolumeTransformer.js, _stream_readable.js etc.

This is the 'size delta' on another run I did:

I think it has to do with me ending the dispatcher before the stream is finished and/or me seeking to a certain part of the video and then ending the dispatcher. I also tried this however without the seek option, and it shows the same result. Hopefully someone can point out something I'm missing.

Include a reproducible code sample here, if possible:
I made a simple re-producible sample which I run in a single guild with 1 member. Say "!start" once you are in a voice channel.
https://github.com/seuuuul/example

import { Client, Message, VoiceChannel } from "discord.js";
import ytdl from "ytdl-core";

const TOKEN = "token";
const YOUTUBE_LINKS = [
  "https://youtu.be/FzVR_fymZw4",
  "https://youtu.be/9pdj4iJD08s",
];

const client = new Client();

client.on("message", async (message) => {
  if (message.content === "!start") {
    const voiceChannel = message.member!.voice.channel!;
    while (true) {
      await procedure(voiceChannel);
    }
  }
});

const procedure = async (voiceChannel: VoiceChannel) => {
  console.log("New procedure.");
  
  // Set up stream/dispatcher and play at a random time.
  let voiceConnection = await voiceChannel.join();
  let stream = ytdl(YOUTUBE_LINKS[Math.round(Math.random())]);
  let dispatcher = voiceConnection.play(stream, {
    seek: Math.floor(Math.random() * 100),
  });

  // Let it play for 10 seconds.
  await new Promise((resolve) => {
    setTimeout(resolve, 10_000);
  });

  dispatcher.end();

  return;
};

(async () => {
  await client.login(TOKEN);
})();

Things I tried (all same issue):

  • dispatcher.destroy() instead of dispatcher.end()
  • stream.destroy() instead of dispatcher.end()
  • both dispatcher.end() and stream.destroy()
  • No seek in voiceConnection.play
  • No seek in voiceConnection.play and filter: "audioonly" in ytdl
  • Changing ytdl settings
let stream = ytdl(YOUTUBE_LINKS[Math.round(Math.random())], {
    highWaterMark: 1 << 25,
    filter: "audioonly"
  });

Also causes faster increase in memory usage, because of the high water mark probably.

let stream = ytdl(YOUTUBE_LINKS[Math.round(Math.random())], {
    highWaterMark: 1 << 25,
  });

Same as above.

let stream = ytdl(YOUTUBE_LINKS[Math.round(Math.random())], {
    filter: "audioonly"
  });

I don't use this option because streaming with "audioonly" gives a long delay when starting the audio, probably due to seeking to a timestamp. This does not happen when streaming both video and audio. However, they all have the same issue in the end.

Further details:

  • discord.js version: ^12.2.0
  • Node.js version: 13.9
  • Operating system: Windows 10
  • Priority this issue should have – please be realistic and elaborate if possible: not sure, depends on if this is a bug
  • I have also tested the issue on latest master, commit hash:

Some related issues are #2951. I asked this question on the discord and another user had the same problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions