Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/Npgsql/Internal/NpgsqlConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,10 @@ internal NpgsqlConnector(NpgsqlDataSource dataSource, NpgsqlConnection conn)

_isKeepAliveEnabled = Settings.KeepAlive > 0;
if (_isKeepAliveEnabled)
_keepAliveTimer = new Timer(PerformKeepAlive, null, Timeout.Infinite, Timeout.Infinite);
{
using (ExecutionContext.SuppressFlow()) // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
_keepAliveTimer = new Timer(PerformKeepAlive, null, Timeout.Infinite, Timeout.Infinite);
}

DataReader = new NpgsqlDataReader(this);

Expand Down
5 changes: 3 additions & 2 deletions src/Npgsql/NpgsqlDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ internal NpgsqlDataSource(

_timerPasswordProviderCancellationTokenSource = new();

// Create the timer, but don't start it; the manual run below will will schedule the first refresh.
_periodicPasswordProviderTimer = new Timer(state => _ = RefreshPassword(), null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
// Create the timer, but don't start it; the manual run below will schedule the first refresh.
using (ExecutionContext.SuppressFlow()) // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to extend that so the first refresh attempt behaves the exact same way as the later ones.

_periodicPasswordProviderTimer = new Timer(state => _ = RefreshPassword(), null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
// Trigger the first refresh attempt right now, outside the timer; this allows us to capture the Task so it can be observed
// in GetPasswordAsync.
_passwordRefreshTask = Task.Run(RefreshPassword);
Expand Down
3 changes: 2 additions & 1 deletion src/Npgsql/PoolingDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ internal PoolingDataSource(
if (connectionIdleLifetime < pruningSamplingInterval)
throw new ArgumentException($"Connection can't have {nameof(settings.ConnectionIdleLifetime)} {connectionIdleLifetime} under {nameof(settings.ConnectionPruningInterval)} {pruningSamplingInterval}");

_pruningTimer = new Timer(PruningTimerCallback, this, Timeout.Infinite, Timeout.Infinite);
using (ExecutionContext.SuppressFlow()) // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
_pruningTimer = new Timer(PruningTimerCallback, this, Timeout.Infinite, Timeout.Infinite);
_pruningSampleSize = DivideRoundingUp(settings.ConnectionIdleLifetime, settings.ConnectionPruningInterval);
_pruningMedianIndex = DivideRoundingUp(_pruningSampleSize, 2) - 1; // - 1 to go from length to index
_pruningSamplingInterval = pruningSamplingInterval;
Expand Down
7 changes: 5 additions & 2 deletions src/Npgsql/Replication/ReplicationConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,11 @@ internal async IAsyncEnumerator<XLogDataMessage> StartReplicationInternal(

SetTimeouts(_walReceiverTimeout, CommandTimeout);

_sendFeedbackTimer = new Timer(TimerSendFeedback, state: null, WalReceiverStatusInterval, Timeout.InfiniteTimeSpan);
_requestFeedbackTimer = new Timer(TimerRequestFeedback, state: null, _requestFeedbackInterval, Timeout.InfiniteTimeSpan);
using (ExecutionContext.SuppressFlow()) // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, this shouldn't really be necessary since this is IAsyncEnumerator and timers will be disposed whenever the enumerator is, but might as well do this for consistency.

{
_sendFeedbackTimer = new Timer(TimerSendFeedback, state: null, WalReceiverStatusInterval, Timeout.InfiniteTimeSpan);
_requestFeedbackTimer = new Timer(TimerRequestFeedback, state: null, _requestFeedbackInterval, Timeout.InfiniteTimeSpan);
}

while (true)
{
Expand Down