Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

AudioResource memory leak #164

Open
FireNameFN opened this issue Aug 7, 2021 · 12 comments
Open

AudioResource memory leak #164

FireNameFN opened this issue Aug 7, 2021 · 12 comments

Comments

@FireNameFN
Copy link

FireNameFN commented Aug 7, 2021

Voice.createAudioResource() creates memory leak.
There is no AudioResource.destroy() but without destroy() it eats memory.

With creating resource (eats memory):

Voice.createAudioResource(res.stream, {inputType: res.type, inlineVolume: true});

Without creating resource (all memory is fine):

//Voice.createAudioResource(res.stream, {inputType: res.type, inlineVolume: true});
@FireNameFN FireNameFN added the bug Something isn't working label Aug 7, 2021
@FireNameFN
Copy link
Author

FireNameFN commented Aug 7, 2021

Also if use this resource:

player.play(resource);
player.stop();

Memory will not be freed.

@iim-ayush
Copy link
Contributor

If you are using ytdl-core, then it's your coding problem not djs voice issue. Ytdl core explicitly tells everyone to destroy it's stream once you are done downloading.

See this

Don't think that it is issue of voice module.

@FireNameFN
Copy link
Author

FireNameFN commented Aug 7, 2021

I also destroy ytdl's stream below but AudioResource still takes memory.

res?.stream.destroy();
stream?.destroy();

@amishshah
Copy link
Member

Please send a full code sample

@FireNameFN
Copy link
Author

FireNameFN commented Aug 7, 2021

[TBD]

async #playCycle(guild) {
		let playMusic = async () => {
			try {
				connection.rejoin({channelId: guild.channel});

				var music = this.#channels.getMusic(guild);
				let info = await YTDL.getInfo(music.link, {lang: "ru"});
				music.name = this.#getName(info);

				let bitrate = this.#channels.getChannel(guild).bitrate;

				try {
					var format = YTDL.chooseFormat(info.formats, {quality: "lowestaudio", filter: form => form.bitrate >= bitrate});
					bitrate = format.audioBitrate * 1000;
				} catch {
					var format = YTDL.chooseFormat(info.formats, {quality: "highestaudio", filter: form => form.bitrate < bitrate});
				}

				info.formats = [format];

				var stream = YTDL.downloadFromInfo(info, {highWaterMark: 1 << 30});
				var res = await Voice.demuxProbe(stream);
				let resource = Voice.createAudioResource(res.stream, {inputType: res.type, inlineVolume: true});

				resource.encoder.setBitrate(bitrate);
				resource.volume.volume = guild.volume * this.#settings.volume;
				
				await Voice.entersState(connection, "ready", this.#settings.waitTime);

				guild.player.play(resource);

				await Voice.entersState(guild.player, "playing", this.#settings.waitTime);

				let ann = this.#channels.getAnnouncment(guild);
				if(ann != null)
					sendChannel(ann, this.#settings.messages.info.playing, music.name);

				this.#channels.changeMusic(guild, 1);

				await Voice.entersState(guild.player, "idle", this.#settings.maxTime);
			} catch(e) {
				if(e.message != "Status code: 403") {
					console.log("#playCycle");
					console.log(music.link);
					console.log(guild.player.state);
					console.trace(e);
				}

				let ann = this.#channels.getAnnouncment(guild);
				if(ann != null)
					sendChannel(ann, this.#settings.messages.info.invalid, music.name);

				this.#channels.changeMusic(guild, 1);
			}

			res?.stream.destroy();
			stream?.destroy();
		};

		let canPlay = () => this.#channels.joinable(guild) && guild.musics.length > 0 && guild.channel != null
			&& this.#channels.getChannel(guild).members.size > (this.#channels.getChannel(guild).members.has(this.#channels.getRealGuild(guild).me.id) ? 1 : 0);

		if(canPlay()) {
			this.#channels.stopAwaiting(guild);

			guild.player = Voice.createAudioPlayer();

			var connection = Voice.joinVoiceChannel({
				channelId: guild.channel,
				guildId: guild.id,
				adapterCreator: this.#channels.createAdapter(guild)
			});

			connection.subscribe(guild.player);

			while(canPlay())
				await playMusic();

			connection.destroy();

			guild.player = null;
		}

		this.#channels.await(guild);
	}

Have luck in reading.

@twlite
Copy link

twlite commented Aug 7, 2021

var stream = YTDL.downloadFromInfo(info, {highWaterMark: 1 << 30});
var res = await Voice.demuxProbe(stream);
let resource = Voice.createAudioResource(res.stream, {inputType: res.type, inlineVolume: true});

🤔 is demuxProbe important here? You have inline volume enabled (inlineVolume: true) which would obviously convert your stream to pcm

@cjh980402
Copy link

cjh980402 commented Aug 7, 2021

I also destroy ytdl's stream below but AudioResource still takes memory.

res?.stream.destroy();
stream?.destroy();

I agree with that issue because I have also suffered a memory loss that has destroyed the stream.

@cjh980402
Copy link

cjh980402 commented Aug 7, 2021

state setter of AudioPlayer does not clean-up operations about resource.volume.
Could this be the cause of memory leaks?

if (oldState.status !== AudioPlayerStatus.Idle && oldState.resource !== newResource) {
	oldState.resource.playStream.on('error', noop);
	oldState.resource.playStream.off('error', oldState.onStreamError);
	oldState.resource.audioPlayer = undefined;
	oldState.resource.playStream.destroy();
	oldState.resource.playStream.read(); // required to ensure buffered data is drained, prevents memory leak
}

just operation about playStream

@amishshah
Copy link
Member

amishshah commented Aug 7, 2021

I don't think this is the issue. The volume stream is included in the pipeline that creates the playStream, so ending the playStream will end the volume stream too.

I still think this is an issue specific to your usage of ytdl-core, as memory leaks don't seem to happen for other users

@amishshah amishshah reopened this Aug 7, 2021
@cjh980402
Copy link

Even if I use youtube-dl-exec as the code of the example, but memory usage still increase. Is there a solution to the increase in memory usage?

@iCrawl
Copy link
Member

iCrawl commented Aug 7, 2021

Do you wait for the garbage collector?

To detect a memory leak you would need to observe it over 24-72h and check if you see a steady increase in memory, solely from playing music (no new members, no new guilds, etc)

@amishshah amishshah added unverified and removed bug Something isn't working labels Aug 7, 2021
@FireNameFN
Copy link
Author

Do you wait for the garbage collector?

To detect a memory leak you would need to observe it over 24-72h and check if you see a steady increase in memory, solely from playing music (no new members, no new guilds, etc)

I tried to wait for GC, but bot very slows down when memory is running out.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants