Skip to content

buffer: support multiple pools for Buffer.allocUnsafe #30611

Closed
@puzpuzpuz

Description

@puzpuzpuz

The problem

At the moment Buffer.allocUnsafe uses a global pool (strictly speaking, it's not a "classical" object pool, but let's use this terminology), i.e. pre-allocated internal Buffer which is then sliced for newly allocated buffers, when possible. The size of the pool may be configured by setting Buffer.poolSize property (8KB by default). Once the current internal buffer fills up, a new one is allocated by the pool.

Here is the related fragment of the official documentation:

The Buffer module pre-allocates an internal Buffer instance of size Buffer.poolSize that is used as a pool for the fast allocation of new Buffer instances created using Buffer.allocUnsafe() and the deprecated new Buffer(size) constructor only when size is less than or equal to Buffer.poolSize >> 1 (floor of Buffer.poolSize divided by two).

The problem with this approach is that this pool is global, so its size can't be configured by a Node.js library. Of course, a library can change Buffer.poolSize, but that may clash with another library or the application which may also change it. Also, if the application changes this setting to a sub-optimal value, it may impact the performance of libraries used within the application.

Many libraries, especially client libraries, use Buffer.allocUnsafe on their hot path. So, predictable and optimal size of the pool is critical for their performance. Thus, it would be great to have a way to isolate libraries from each other and the application.

The solution

The idea is to introduce the concept of multiple identified pools for Buffer.allocUnsafe. See the snippet:

// Create a new internal pool with size of 32KB.
// The function returns a pool id (number).
const poolId = Buffer.createUnsafePool(32 * 1024);

// Allocate unsafe buffer using the internal pool.
// The second optional argument is the pool id.
const buf = Buffer.allocUnsafe(16, poolId);

With this change libraries are free to use their own dedicated pool with a size that they find optimal.

Optionally, another function Buffer.destroyUnsafePool could be added. This function will destroy the pool, so its buffer could be GCed. But I don't think it's a must have as with current API it's not possible to destroy the global pool.

Note that this change is backward-compatible, thus it won't impact current API and its behavior.

I'd like to hear some positive feedback from node collaborators first, so I could proceed with the implementation.

Alternatives

This could be implemented as an npm module. But I'm certain that it's a natural addition to the standard API, which already implements such pool, yet it's global and its configuration is really inconvenient for libraries. So, having it in the core will be a valuable addition.

Another thing to mention is that the default size doesn't seem large enough (at least for certain use cases) and could be increased (see #27121). But this won't help with the isolation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bufferIssues and PRs related to the buffer subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions