Skip to content

Commit ee7e665

Browse files
committed
feat(infrastructure): add DB connection string provider with thread-safe caching
- Implement IDbConnectionStringProvider interface * Thread-safe singleton with lock-based synchronization * Initialize once during Lambda cold start * GetConnectionString for cached retrieval * Comprehensive error handling and validation
1 parent 9bf532f commit ee7e665

File tree

3 files changed

+419
-0
lines changed

3 files changed

+419
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
namespace LeadProcessor.Infrastructure.Services;
2+
3+
/// <summary>
4+
/// Thread-safe implementation of <see cref="IDbConnectionStringProvider"/> that caches the database connection string.
5+
/// </summary>
6+
/// <remarks>
7+
/// This class is designed to be registered as a singleton in the dependency injection container.
8+
/// The connection string is initialized during Lambda cold start and cached for the lifetime
9+
/// of the Lambda instance, avoiding repeated calls to AWS Secrets Manager.
10+
/// All methods are thread-safe and can be called concurrently from multiple threads.
11+
/// </remarks>
12+
public sealed class DbConnectionStringProvider : IDbConnectionStringProvider
13+
{
14+
private string? _connectionString;
15+
private readonly object _lock = new();
16+
17+
/// <summary>
18+
/// Initializes the connection string provider with the specified connection string.
19+
/// </summary>
20+
/// <param name="connectionString">The database connection string to store.</param>
21+
/// <exception cref="ArgumentNullException">Thrown when connectionString is null.</exception>
22+
/// <exception cref="ArgumentException">Thrown when connectionString is empty or whitespace.</exception>
23+
/// <remarks>
24+
/// This method is thread-safe and can be called multiple times. Each call will overwrite
25+
/// the previously stored connection string. In a typical Lambda scenario, this is called
26+
/// once during cold start initialization.
27+
/// </remarks>
28+
public void Initialize(string connectionString)
29+
{
30+
if (connectionString == null)
31+
{
32+
throw new ArgumentNullException(nameof(connectionString));
33+
}
34+
35+
if (string.IsNullOrWhiteSpace(connectionString))
36+
{
37+
throw new ArgumentException("Connection string cannot be empty or whitespace.", nameof(connectionString));
38+
}
39+
40+
lock (_lock)
41+
{
42+
_connectionString = connectionString;
43+
}
44+
}
45+
46+
/// <summary>
47+
/// Retrieves the database connection string.
48+
/// </summary>
49+
/// <returns>The database connection string that was previously initialized.</returns>
50+
/// <exception cref="InvalidOperationException">
51+
/// Thrown when the connection string has not been initialized via <see cref="Initialize"/>.
52+
/// </exception>
53+
/// <remarks>
54+
/// This method is thread-safe and can be called concurrently from multiple threads.
55+
/// It will always return the most recently initialized connection string.
56+
/// </remarks>
57+
public string GetConnectionString()
58+
{
59+
lock (_lock)
60+
{
61+
return _connectionString ?? throw new InvalidOperationException(
62+
"Connection string not initialized. Call Initialize() before GetConnectionString().");
63+
}
64+
}
65+
}
66+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace LeadProcessor.Infrastructure.Services;
2+
3+
/// <summary>
4+
/// Provides access to the database connection string.
5+
/// </summary>
6+
/// <remarks>
7+
/// This interface enables thread-safe access to the database connection string
8+
/// which is initialized during Lambda cold start. The connection string is typically
9+
/// retrieved from AWS Secrets Manager and cached for the lifetime of the Lambda instance.
10+
/// Implementations must be thread-safe.
11+
/// </remarks>
12+
public interface IDbConnectionStringProvider
13+
{
14+
/// <summary>
15+
/// Initializes the connection string provider with the specified connection string.
16+
/// </summary>
17+
/// <param name="connectionString">The database connection string to store.</param>
18+
/// <exception cref="ArgumentNullException">Thrown when connectionString is null.</exception>
19+
/// <exception cref="ArgumentException">Thrown when connectionString is empty or whitespace.</exception>
20+
/// <remarks>
21+
/// This method should be called once during Lambda initialization (cold start)
22+
/// before any database operations are performed. Subsequent calls will overwrite
23+
/// the existing connection string.
24+
/// Thread-safe: Can be called from multiple threads simultaneously.
25+
/// </remarks>
26+
void Initialize(string connectionString);
27+
28+
/// <summary>
29+
/// Retrieves the database connection string.
30+
/// </summary>
31+
/// <returns>The database connection string.</returns>
32+
/// <exception cref="InvalidOperationException">Thrown when the connection string has not been initialized.</exception>
33+
/// <remarks>
34+
/// This method is called by the DbContext factory to obtain the connection string
35+
/// for creating database connections. It must be called after Initialize().
36+
/// Thread-safe: Can be called from multiple threads simultaneously.
37+
/// </remarks>
38+
string GetConnectionString();
39+
}
40+

0 commit comments

Comments
 (0)