Skip to content

Commit 41d0726

Browse files
committed
feat(api): create singleton for database connection management
Introduces `DatabaseConnectionManager`, a singleton class to manage a single, shared PostgreSQL connection for the application. This ensures the database is connected only once, improving performance and resource management. It uses a `Completer` to make the connection available asynchronously and includes logging for the initialization process.
1 parent 2ceed31 commit 41d0726

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'dart:async';
2+
3+
import 'package:ht_api/src/config/environment_config.dart';
4+
import 'package:logging/logging.dart';
5+
import 'package:postgres/postgres.dart';
6+
7+
/// A singleton class to manage a single, shared PostgreSQL database connection.
8+
///
9+
/// This pattern ensures that the application establishes a connection to the
10+
/// database only once and reuses it for all subsequent operations, which is
11+
/// crucial for performance and resource management.
12+
class DatabaseConnectionManager {
13+
// Private constructor for the singleton pattern.
14+
DatabaseConnectionManager._();
15+
16+
/// The single, global instance of the [DatabaseConnectionManager].
17+
static final instance = DatabaseConnectionManager._();
18+
19+
final _log = Logger('DatabaseConnectionManager');
20+
21+
// A completer to signal when the database connection is established.
22+
final _completer = Completer<Connection>();
23+
24+
/// Returns a future that completes with the established database connection.
25+
///
26+
/// If the connection has not been initialized yet, it calls `init()` to
27+
/// establish it. Subsequent calls will return the same connection future.
28+
Future<Connection> get connection => _completer.future;
29+
30+
/// Initializes the database connection.
31+
///
32+
/// This method is idempotent. It parses the database URL from the
33+
/// environment, opens a connection to the PostgreSQL server, and completes
34+
/// the `_completer` with the connection. It only performs the connection
35+
/// logic on the very first call.
36+
Future<void> init() async {
37+
if (_completer.isCompleted) {
38+
_log.fine('Database connection already initializing/initialized.');
39+
return;
40+
}
41+
42+
_log.info('Initializing database connection...');
43+
final dbUri = Uri.parse(EnvironmentConfig.databaseUrl);
44+
String? username;
45+
String? password;
46+
if (dbUri.userInfo.isNotEmpty) {
47+
final parts = dbUri.userInfo.split(':');
48+
username = Uri.decodeComponent(parts.first);
49+
if (parts.length > 1) {
50+
password = Uri.decodeComponent(parts.last);
51+
}
52+
}
53+
54+
final connection = await Connection.open(
55+
Endpoint(
56+
host: dbUri.host,
57+
port: dbUri.port,
58+
database: dbUri.path.substring(1),
59+
username: username,
60+
password: password,
61+
),
62+
settings: const ConnectionSettings(sslMode: SslMode.require),
63+
);
64+
_log.info('Database connection established successfully.');
65+
_completer.complete(connection);
66+
}
67+
}

0 commit comments

Comments
 (0)