diff --git a/Neo4j.Driver/Neo4j.Driver/Config.cs b/Neo4j.Driver/Neo4j.Driver/Config.cs index f45b8ea69..66be415e1 100644 --- a/Neo4j.Driver/Neo4j.Driver/Config.cs +++ b/Neo4j.Driver/Neo4j.Driver/Config.cs @@ -232,11 +232,11 @@ public int MaxIdleConnectionPoolSize /// By default the driver allows the collection of this telemetry. /// public bool TelemetryDisabled { get; set; } - + /// /// The configuration for the driver's underlying message reading from the network. /// - public MessageReaderConfig MessageReaderConfig { get; internal set; } = MessageReaderConfig.Default; + public MessageReaderConfig MessageReaderConfig { get; internal set; } = new(); } /// @@ -244,53 +244,50 @@ public int MaxIdleConnectionPoolSize /// public sealed class MessageReaderConfig { - internal static MessageReaderConfig Default { get; } = new(); - /// /// Constructs a new instance of .
/// The configuration for the driver's underlying message reading from the network. ///
- /// As of 5.15, the driver has migrated the underlying message reading - /// mechanism utilizing ; this optimizes the reading and memory usage of the driver, and - /// setting this to true will revert the driver to the legacy message reader. /// The memory pool for creating buffers when reading messages. The PipeReader will borrow /// memory from the pool of at least ReadBufferSize size. The message reader can request larger memory blocks to - /// host an entire message. User code can provide an implementation for monitoring; by default, the driver will use - /// .NET's pool. + /// host an entire message. User code can provide an implementation for monitoring; by default, the driver will + /// allocate a new array pool that does not take advantage of shared memory pools. /// The minimum buffer size to use when renting memory from the pool. The default value /// is 65,539. /// /// /// + /// + /// To optimize the memory usage of the driver pass .NET's shared memory pool() as + /// the , this should only be used when there is complete trust over the usage of + /// shared memory buffers in the application as other components may be using the same memory pool. + /// /// If is less than 1. - public MessageReaderConfig(bool disablePipelinedMessageReader = false, MemoryPool memoryPool = null, int minBufferSize = -1) + public MessageReaderConfig(MemoryPool memoryPool = null, int minBufferSize = -1) { - DisablePipelinedMessageReader = disablePipelinedMessageReader; - if (disablePipelinedMessageReader) - { - return; - } - MemoryPool = memoryPool ?? MemoryPool.Shared; if (minBufferSize is < -1 or 0) { throw new ArgumentOutOfRangeException(nameof(minBufferSize)); } + + DisablePipelinedMessageReader = false; MinBufferSize = minBufferSize == -1 ? 65_535 + 4 : minBufferSize; + MemoryPool = memoryPool ?? new PipeReaderMemoryPool(MinBufferSize); StreamPipeReaderOptions = new(MemoryPool, MinBufferSize, leaveOpen: true); } - + /// /// As of 5.15, the driver has migrated the underlying message reading mechanism utilizing ; /// this optimizes the reading and memory usage of the driver, and setting this to true will revert the driver to /// the legacy message reader. /// - public bool DisablePipelinedMessageReader { get; } + internal bool DisablePipelinedMessageReader { get; } /// /// The memory pool for creating buffers when reading messages. The PipeReader will borrow memory from the pool of /// at least size. The message reader can request larger memory blocks to host - /// an entire message. User code can provide an implementation for monitoring; by default, the driver will use - /// .NET's pool. + /// an entire message. User code can provide an implementation for monitoring; by default, the driver will allocate + /// a new array pool that does not take advantage of shared memory pools. /// public MemoryPool MemoryPool { get; } diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Util/PipeReaderMemoryPool.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Util/PipeReaderMemoryPool.cs new file mode 100644 index 000000000..6f9ff5ed4 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Util/PipeReaderMemoryPool.cs @@ -0,0 +1,94 @@ +// Copyright (c) "Neo4j" +// Neo4j Sweden AB [https://neo4j.com] +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Buffers; + +namespace Neo4j.Driver.Internal; + +/// +/// Simple memory pool based on the .NET's Pool. +/// +internal sealed class PipeReaderMemoryPool : MemoryPool +{ + private readonly int _defaultSize; + private readonly ArrayPool _pool; + + public PipeReaderMemoryPool(int defaultSize) + { + _defaultSize = defaultSize; + _pool = ArrayPool.Create(); + } + + public override int MaxBufferSize => int.MaxValue; + + public override IMemoryOwner Rent(int minimumBufferSize = -1) + { + if (minimumBufferSize == -1) + { + minimumBufferSize = _defaultSize; + } + + if (minimumBufferSize < 0 || minimumBufferSize > MaxBufferSize) + { + throw new ArgumentOutOfRangeException(nameof(minimumBufferSize), minimumBufferSize, "requested size is invalid"); + } + + return new PooledMemory(minimumBufferSize, _pool); + } + + protected override void Dispose(bool disposing) + { + } + + private sealed class PooledMemory : IMemoryOwner + { + private byte[] _array; + private readonly ArrayPool _pool; + + public PooledMemory(int size, ArrayPool pool) + { + _array = pool.Rent(size); + _pool = pool; + } + + public Memory Memory + { + get + { + var array = _array; + if (array == null) + { + throw new ObjectDisposedException(nameof(PooledMemory)); + } + + return new Memory(array); + } + } + + public void Dispose() + { + var array = _array; + + if (array == null) + { + return; + } + + _array = null; + _pool.Return(array); + } + } +}