Skip to content

Commit d7d8495

Browse files
Merge pull request #27 from Stravaig-Projects/#16/command-timeout
#16 Add ability to set the command timeout
2 parents 8a024a5 + 195400a commit d7d8495

15 files changed

+169
-39
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ with the corresponding information in `appsettings.json`, e.g.:
3737
"SchemaName": "Stravaig",
3838
"TableName": "AppConfiguration",
3939
"RefreshSeconds": 90,
40-
"ConnectionStringName": "ConfigDB"
40+
"ConnectionStringName": "ConfigDB",
41+
"CommandTimeout": 10
4142
}
4243
}
4344
}

Reset-WipReleaseNotes.ps1

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ $content = @(
1313
"",
1414
"### Dependent Packages",
1515
"",
16-
"- .NET 5.0",
17-
" - No changes",
18-
"- .NET Core 3.1",
19-
" - No changes",
20-
"- General",
21-
" - No changes",
16+
"- No changes",
2217
"",
2318
"---",
2419
"",

release-notes/wip-release-notes.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,15 @@
44

55
Date: ???
66

7-
### Bugs
8-
97
### Features
108

11-
### Miscellaneous
9+
- #16: Allow command timeout to be configured.
1210

1311
### Dependent Packages
1412

15-
- .NET 5.0
16-
- No changes
17-
- .NET Core 3.1
18-
- No changes
19-
- General
20-
- No changes
13+
- coverlet.collector from 3.1.1 to 3.1.2
14+
- Microsoft.Extensions.Configuration from 6.0.0 to 6.0.1
15+
- Microsoft.Extensions.Hosting from 6.0.0 to 6.0.1
2116

2217
---
2318

src/Stravaig.Configuration.SqlServer.Tests/FakeDataLoader.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public IDictionary<string, string> RetrieveData(SqlServerConfigurationSource sou
2323

2424
public class FakeSqlServerConfigurationWatcher : ISqlServerConfigurationWatcher
2525
{
26-
ILogger _logger = NullLogger.Instance;
27-
private SqlServerConfigurationProvider _provider;
26+
ILogger? _logger = NullLogger.Instance;
27+
private SqlServerConfigurationProvider? _provider;
2828

2929
public void EnsureStarted()
3030
{
@@ -35,10 +35,8 @@ public void AttachLogger(ILogger logger)
3535
_logger = logger;
3636
}
3737

38-
public void AttacheProvider(SqlServerConfigurationProvider provider)
38+
public void AttachProvider(SqlServerConfigurationProvider provider)
3939
{
4040
_provider = provider;
4141
}
42-
43-
4442
}

src/Stravaig.Configuration.SqlServer.Tests/SqlServerConfigurationProviderTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void ReplayLogExceptionOnLoadFailure()
103103
thirdLog.PropertyDictionary["exceptionMessage"].ShouldBe("Dummy Exception.");
104104
logs[3].OriginalMessage.ShouldBe("End of replay.");
105105
}
106-
106+
107107
private SqlServerConfigurationProvider SetupProvider()
108108
{
109109
_fakeLoader = new FakeDataLoader()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using Microsoft.Extensions.Logging;
3+
using Microsoft.Extensions.Logging.Abstractions;
4+
using NUnit.Framework;
5+
using Shouldly;
6+
using Stravaig.Configuration.SqlServer.Glue;
7+
using Stravaig.Extensions.Logging.Diagnostics;
8+
using Stravaig.Extensions.Logging.Diagnostics.Render;
9+
10+
namespace Stravaig.Configuration.SqlServer.Tests;
11+
12+
[TestFixture]
13+
public class SqlServerConfigurationSourceExtensionTests
14+
{
15+
private const string DummyConnectionString = "Server=localhost;Database=testing;Connection Timeout=5";
16+
17+
[Test]
18+
public void CreateLoggerWhenNotExpectingALoggerIsNullLogger()
19+
{
20+
var source = new SqlServerConfigurationSource(DummyConnectionString, expectLogger: false);
21+
22+
source.CreateLogger().ShouldBe(NullLogger<SqlServerConfigurationProvider>.Instance);
23+
}
24+
25+
[Test]
26+
public void CreateLoggerWhenExpectingALoggerIsReplayLogger()
27+
{
28+
var source = new SqlServerConfigurationSource(DummyConnectionString, expectLogger: true);
29+
30+
source.CreateLogger().ShouldBeOfType<ReplayLogger<SqlServerConfigurationProvider>>();
31+
}
32+
33+
[Test]
34+
public void CreateLoggerIndicatesHowTheProviderIsSetup()
35+
{
36+
var source = new SqlServerConfigurationSource(DummyConnectionString, expectLogger: true, refreshInterval: TimeSpan.FromSeconds(120), commandTimeout: TimeSpan.FromSeconds(10));
37+
var replayLogger = (ReplayLogger<SqlServerConfigurationProvider>)source.CreateLogger();
38+
var captureLogger = new TestCaptureLogger<SqlServerConfigurationProvider>();
39+
replayLogger.Replay(captureLogger);
40+
41+
var logs = captureLogger.GetLogs();
42+
logs.RenderLogs(Formatter.SimpleBySequence, Sink.Console);
43+
44+
var descriptionLog = logs[1];
45+
descriptionLog.OriginalMessage.ShouldBe("SQL Server Configuration Provider will retrieve from [{serverName}].[{databaseName}].[{schemaName}].[{tableName}] {frequency}. Will wait {connectionTimeout} seconds to connect, and {commandTimeout} seconds to retrieve data.");
46+
descriptionLog.PropertyDictionary["serverName"].ShouldBe("localhost");
47+
descriptionLog.PropertyDictionary["databaseName"].ShouldBe("testing");
48+
descriptionLog.PropertyDictionary["schemaName"].ShouldBe("Stravaig");
49+
descriptionLog.PropertyDictionary["tableName"].ShouldBe("AppConfiguration");
50+
descriptionLog.PropertyDictionary["frequency"].ShouldBe("every 120 seconds");
51+
descriptionLog.PropertyDictionary["connectionTimeout"].ShouldBe(5);
52+
descriptionLog.PropertyDictionary["commandTimeout"].ShouldBe(10);
53+
}
54+
55+
[Test]
56+
public void CreateLoggerWarnsOfInterleavePossibility()
57+
{
58+
var source = new SqlServerConfigurationSource(DummyConnectionString, expectLogger: true, refreshInterval: TimeSpan.FromSeconds(12), commandTimeout: TimeSpan.FromSeconds(30));
59+
var replayLogger = (ReplayLogger<SqlServerConfigurationProvider>)source.CreateLogger();
60+
var captureLogger = new TestCaptureLogger<SqlServerConfigurationProvider>();
61+
replayLogger.Replay(captureLogger);
62+
63+
var logs = captureLogger.GetLogs();
64+
logs.RenderLogs(Formatter.SimpleBySequence, Sink.Console);
65+
66+
var descriptionLog = logs[1];
67+
descriptionLog.OriginalMessage.ShouldBe("SQL Server Configuration Provider will retrieve from [{serverName}].[{databaseName}].[{schemaName}].[{tableName}] {frequency}. Will wait {connectionTimeout} seconds to connect, and {commandTimeout} seconds to retrieve data.");
68+
descriptionLog.PropertyDictionary["serverName"].ShouldBe("localhost");
69+
descriptionLog.PropertyDictionary["databaseName"].ShouldBe("testing");
70+
descriptionLog.PropertyDictionary["schemaName"].ShouldBe("Stravaig");
71+
descriptionLog.PropertyDictionary["tableName"].ShouldBe("AppConfiguration");
72+
descriptionLog.PropertyDictionary["frequency"].ShouldBe("every 12 seconds");
73+
descriptionLog.PropertyDictionary["connectionTimeout"].ShouldBe(5);
74+
descriptionLog.PropertyDictionary["commandTimeout"].ShouldBe(30);
75+
76+
var warningLog = logs[2];
77+
warningLog.OriginalMessage.ShouldBe("The refresh interval, {refreshInterval} seconds, should be greater than combined connection, {connectionTimeout} seconds, and command, {commandTimeout} seconds, timeouts. A new refresh cycle may start before the previous cycle is complete.");
78+
warningLog.LogLevel.ShouldBe(LogLevel.Warning);
79+
warningLog.PropertyDictionary["refreshInterval"].ShouldBe(12);
80+
warningLog.PropertyDictionary["connectionTimeout"].ShouldBe(5);
81+
warningLog.PropertyDictionary["commandTimeout"].ShouldBe(30);
82+
}
83+
}

src/Stravaig.Configuration.SqlServer/Glue/DataLoader.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Data.SqlClient;
34

45
namespace Stravaig.Configuration.SqlServer.Glue;
@@ -15,6 +16,7 @@ public IDictionary<string, string> RetrieveData(SqlServerConfigurationSource sou
1516
using SqlConnection connection = new SqlConnection(source.ConnectionString);
1617
connection.Open();
1718
var cmd = new SqlCommand(sql, connection);
19+
cmd.CommandTimeout = (int)source.CommandTimeout.TotalSeconds;
1820
using var reader = cmd.ExecuteReader();
1921
return MaterialiseData(reader);
2022
}

src/Stravaig.Configuration.SqlServer/Glue/DefaultValues.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ namespace Stravaig.Configuration.SqlServer.Glue;
33
internal static class DefaultValues
44
{
55
public const int NoRefresh = 0;
6+
public const int CommandTimeout = 15;
7+
public const int ConnectionTimeOut = 15;
68
public const string SchemaName = "Stravaig";
79
public const string TableName = "AppConfiguration";
810
public const string ConfigurationSection = "Stravaig:AppConfiguration";

src/Stravaig.Configuration.SqlServer/Glue/Log.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,16 @@ public static partial class Log
5858
Level = LogLevel.Warning,
5959
Message = "The replay limit was reached. There are {excessCount} logs that cannot be replayed.")]
6060
public static partial void ReplayLimitExceeded(this ILogger logger, int excessCount);
61+
62+
[LoggerMessage(
63+
EventId = 10,
64+
Level = LogLevel.Information,
65+
Message = "SQL Server Configuration Provider will retrieve from [{serverName}].[{databaseName}].[{schemaName}].[{tableName}] {frequency}. Will wait {connectionTimeout} seconds to connect, and {commandTimeout} seconds to retrieve data.")]
66+
public static partial void InitialProviderDescription(this ILogger logger, string serverName, string databaseName, string schemaName, string tableName, string frequency, int connectionTimeout, int commandTimeout);
67+
68+
[LoggerMessage(
69+
EventId = 11,
70+
Level = LogLevel.Warning,
71+
Message = "The refresh interval, {refreshInterval} seconds, should be greater than combined connection, {connectionTimeout} seconds, and command, {commandTimeout} seconds, timeouts. A new refresh cycle may start before the previous cycle is complete.")]
72+
public static partial void WarnOfCycleInterleave(this ILogger logger, int refreshInterval, int connectionTimeout, int commandTimeout);
6173
}

src/Stravaig.Configuration.SqlServer/Glue/SourceBuilder.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ public static SqlServerConfigurationSource BuildSource(IConfigurationBuilder bui
2121
ApplyFromConnectionStringsSection(options, configRoot.Value);
2222

2323
return new SqlServerConfigurationSource(
24-
options.ConnectionString ?? throw new SqlServerConfigurationProviderException("Cannot build a SQL Server Configuration Provider without a connection string."),
25-
options.IsLoggerExpected,
26-
TimeSpan.FromSeconds(options.RefreshSeconds),
27-
options.SchemaName ?? throw new SqlServerConfigurationProviderException("The schema name is required to use SQL Server Configuration."),
28-
options.TableName ?? throw new SqlServerConfigurationProviderException("The table name is required to use SQL Server Configuration."));
24+
connectionString: options.ConnectionString ?? throw new SqlServerConfigurationProviderException("Cannot build a SQL Server Configuration Provider without a connection string."),
25+
expectLogger: options.IsLoggerExpected,
26+
commandTimeout: TimeSpan.FromSeconds(options.CommandTimeout),
27+
refreshInterval: TimeSpan.FromSeconds(options.RefreshSeconds),
28+
schemaName: options.SchemaName ?? throw new SqlServerConfigurationProviderException("The schema name is required to use SQL Server Configuration."),
29+
tableName: options.TableName ?? throw new SqlServerConfigurationProviderException("The table name is required to use SQL Server Configuration."));
2930
}
3031

3132
private static void ApplyFromConnectionStringsSection(SqlServerConfigurationOptions options, IConfigurationRoot configRoot)

0 commit comments

Comments
 (0)