Skip to content
Permalink
Browse files

Merge branch 'hotfix/4.1.2'

  • Loading branch information
roji committed Nov 14, 2019
2 parents b6f0ce8 + f86ddbe commit 8898b0f63d9b66f2644625d4bf2173ec6c28e4e1
@@ -1,5 +1,5 @@
image: Visual Studio 2019 Preview
version: 4.1.1-{build}
version: 4.1.2-{build}
environment:
global:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -52,6 +52,8 @@ test:
artifacts:
- path: 'src\**\*.nupkg'
name: Nuget
- path: 'src\**\*.snupkg'
name: Nuget symbol packages
- path: 'src\VSIX\bin\**\*.vsix'
name: Visual Studio Extension
- path: 'src\MSI\bin\**\*.msi'
@@ -3,7 +3,7 @@
<PropertyGroup>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
<Company>Npgsql</Company>
<VersionPrefix>4.1.1</VersionPrefix>
<VersionPrefix>4.1.2</VersionPrefix>

<RepositoryType>git</RepositoryType>
<RepositoryUrl>git://github.com/npgsql/npgsql</RepositoryUrl>
@@ -1,6 +1,11 @@
<Project>
<Import Project="../Directory.Build.props" />

<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<!-- Build configuration -->
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -74,17 +74,19 @@ public void Deconstruct(out int open, out int idle, out int busy)
idle = copy.Idle;
busy = copy.Open - copy.Idle;
}
}

// Mutable struct, do not make this readonly.
PoolState State;

public override string ToString()
internal (int Open, int Idle, int Busy, int Waiters) Statistics {
get
{
var (open, idle, busy) = this;
return $"[{open} total, {idle} idle, {busy} busy]";
var (open, idle, busy) = State;
return (open, idle, busy, _waiting.Count);
}
}

// Mutable struct, do not make this readonly.
internal PoolState State;

/// <summary>
/// Incremented every time this pool is cleared via <see cref="NpgsqlConnection.ClearPool"/> or
/// <see cref="NpgsqlConnection.ClearAllPools"/>. Allows us to identify connections which were
@@ -98,9 +100,9 @@ public override string ToString()
readonly TimeSpan _pruningSamplingInterval;
readonly int _pruningSampleSize;
readonly int[] _pruningSamples;
int _pruningSampleIndex;
int _pruningMedianIndex;
readonly int _pruningMedianIndex;
volatile bool _pruningTimerEnabled;
int _pruningSampleIndex;

/// <summary>
/// Maximum number of possible connections in any pool.
@@ -119,28 +121,31 @@ internal ConnectorPool(NpgsqlConnectionStringBuilder settings, string connString
if (settings.MaxPoolSize < settings.MinPoolSize)
throw new ArgumentException($"Connection can't have MaxPoolSize {settings.MaxPoolSize} under MinPoolSize {settings.MinPoolSize}");

Settings = settings;

_max = settings.MaxPoolSize;
_min = settings.MinPoolSize;

UserFacingConnectionString = settings.PersistSecurityInfo
? connString
: settings.ToStringWithoutPassword();

var connectionIdleLifetime = TimeSpan.FromSeconds(Settings.ConnectionIdleLifetime);
_pruningSamplingInterval = TimeSpan.FromSeconds(Settings.ConnectionPruningInterval);
if (connectionIdleLifetime < _pruningSamplingInterval)
if (settings.ConnectionPruningInterval == 0)
throw new ArgumentException("ConnectionPruningInterval can't be 0.");
var connectionIdleLifetime = TimeSpan.FromSeconds(settings.ConnectionIdleLifetime);
var pruningSamplingInterval = TimeSpan.FromSeconds(settings.ConnectionPruningInterval);
if (connectionIdleLifetime < pruningSamplingInterval)
throw new ArgumentException($"Connection can't have ConnectionIdleLifetime {connectionIdleLifetime} under ConnectionPruningInterval {_pruningSamplingInterval}");

_pruningMedianIndex = Divide(_pruningSampleSize, 2);
_pruningTimer = new Timer(PruningTimerCallback, this, Timeout.Infinite, Timeout.Infinite);
_pruningSampleSize = Divide(Settings.ConnectionIdleLifetime, Settings.ConnectionPruningInterval);
_pruningSampleSize = DivideRoundingUp(connectionIdleLifetime.Seconds, pruningSamplingInterval.Seconds);
_pruningMedianIndex = DivideRoundingUp(_pruningSampleSize, 2) - 1; // - 1 to go from length to index
_pruningSamplingInterval = pruningSamplingInterval;
_pruningSamples = new int[_pruningSampleSize];
_pruningTimerEnabled = false;

_max = settings.MaxPoolSize;
_min = settings.MinPoolSize;
_idle = new NpgsqlConnector[_max];
_open = new NpgsqlConnector[_max];
_waiting = new ConcurrentQueue<(TaskCompletionSource<NpgsqlConnector?> TaskCompletionSource, bool IsAsync)>();

UserFacingConnectionString = settings.PersistSecurityInfo
? connString
: settings.ToStringWithoutPassword();

Settings = settings;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -228,7 +233,6 @@ internal async ValueTask<NpgsqlConnector> AllocateLong(NpgsqlConnection conn, Np
}
// Restart the outer loop if we're at max opens.
if (prevOpenCount == _max) continue;
openCount = prevOpenCount + 1;

try
{
@@ -257,8 +261,8 @@ internal async ValueTask<NpgsqlConnector> AllocateLong(NpgsqlConnection conn, Np
}
Debug.Assert(connector.PoolIndex != int.MaxValue);

// Only when we are the ones that incremented openCount past _min Change the timer.
if (openCount == _min)
// Only start pruning if it was this thread that incremented open count past _min.
if (prevOpenCount == _min)
EnablePruning();
Counters.NumberOfPooledConnections.Increment();
Counters.NumberOfActiveConnections.Increment();
@@ -604,7 +608,7 @@ internal void TryRemovePendingEnlistedConnector(NpgsqlConnector connector, Trans

#region Misc

static int Divide(int value, int divisor) => 1 + (value - 1) / divisor;
static int DivideRoundingUp(int value, int divisor) => 1 + (value - 1) / divisor;

[Conditional("DEBUG")]
void CheckInvariants(PoolState state)
@@ -621,7 +625,11 @@ void CheckInvariants(PoolState state)

public void Dispose() => _pruningTimer?.Dispose();

public override string ToString() => State.ToString();
public override string ToString()
{
var (open, idle, busy, waiters) = Statistics;
return $"[{open} total, {idle} idle, {busy} busy, {waiters} waiters]";
}

#endregion Misc
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
@@ -114,7 +114,7 @@ async Task StartRow(bool async)
CheckReady();

if (_column != -1 && _column != NumColumns)
throw new InvalidOperationException("Row has already been started and must be finished");
ThrowHelper.ThrowInvalidOperationException_BinaryImportParametersMismatch(NumColumns, _column);

if (_buf.WriteSpaceLeft < 2)
await _buf.Flush(async);
@@ -154,6 +154,8 @@ public Task WriteAsync<T>([AllowNull] T value, CancellationToken cancellationTok

Task Write<T>([AllowNull] T value, bool async)
{
CheckColumnIndex();

var p = _params[_column];
if (p == null)
{
@@ -202,6 +204,8 @@ public Task WriteAsync<T>([AllowNull] T value, NpgsqlDbType npgsqlDbType, Cancel

Task Write<T>([AllowNull] T value, NpgsqlDbType npgsqlDbType, bool async)
{
CheckColumnIndex();

var p = _params[_column];
if (p == null)
{
@@ -250,6 +254,8 @@ public Task WriteAsync<T>([AllowNull] T value, string dataTypeName, Cancellation

Task Write<T>([AllowNull] T value, string dataTypeName, bool async)
{
CheckColumnIndex();

var p = _params[_column];
if (p == null)
{
@@ -358,6 +364,12 @@ async Task WriteRow(bool async, params object[] values)
await Write(value, async);
}

void CheckColumnIndex()
{
if (_column >= NumColumns)
ThrowHelper.ThrowInvalidOperationException_BinaryImportParametersMismatch(NumColumns, _column + 1);
}

#endregion

#region Commit / Cancel / Close / Dispose
@@ -747,8 +747,8 @@ void ProcessRawQuery(bool deriveParameters = false)
}

foreach (var s in _statements)
if (s.InputParameters.Count > 65535)
throw new Exception("A statement cannot have more than 65535 parameters");
if (s.InputParameters.Count > short.MaxValue)
throw new NpgsqlException($"A statement cannot have more than {short.MaxValue} parameters");
}

#endregion

0 comments on commit 8898b0f

Please sign in to comment.
You can’t perform that action at this time.