Several more database-y refactors
- DbConnectionFactory renamed to "Database", will now be the primary entry point for DB stuff - Created IPKConnection interface mostly containing async extensions to IDbConnection, use this going forward - Reworked the Connection/Command wrappers (that have performance/logging extensions) - Probably more stuff that I forgot???
This commit is contained in:
107
PluralKit.Core/Database/Wrappers/PKConnection.cs
Normal file
107
PluralKit.Core/Database/Wrappers/PKConnection.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using App.Metrics;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using Npgsql;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class PKConnection: DbConnection, IPKConnection
|
||||
{
|
||||
public NpgsqlConnection Inner { get; }
|
||||
public Guid ConnectionId { get; }
|
||||
|
||||
private readonly DbConnectionCountHolder _countHolder;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMetrics _metrics;
|
||||
|
||||
private bool _hasOpened;
|
||||
private bool _hasClosed;
|
||||
private Instant _openTime;
|
||||
|
||||
public PKConnection(NpgsqlConnection inner, DbConnectionCountHolder countHolder, ILogger logger, IMetrics metrics)
|
||||
{
|
||||
Inner = inner;
|
||||
ConnectionId = Guid.NewGuid();
|
||||
_countHolder = countHolder;
|
||||
_logger = logger.ForContext<PKConnection>();
|
||||
_metrics = metrics;
|
||||
}
|
||||
|
||||
public override Task OpenAsync(CancellationToken ct)
|
||||
{
|
||||
if (_hasOpened) return Inner.OpenAsync(ct);
|
||||
_countHolder.Increment();
|
||||
_hasOpened = true;
|
||||
_openTime = SystemClock.Instance.GetCurrentInstant();
|
||||
_logger.Verbose("Opened database connection {ConnectionId}, new connection count {ConnectionCount}", ConnectionId, _countHolder.ConnectionCount);
|
||||
return Inner.OpenAsync(ct);
|
||||
}
|
||||
|
||||
public override Task CloseAsync() => Inner.CloseAsync();
|
||||
|
||||
protected override DbCommand CreateDbCommand() => new PKCommand(Inner.CreateCommand(), this, _logger, _metrics);
|
||||
|
||||
public void ReloadTypes() => Inner.ReloadTypes();
|
||||
|
||||
public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand) => Inner.BeginBinaryImport(copyFromCommand);
|
||||
public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand) => Inner.BeginBinaryExport(copyToCommand);
|
||||
|
||||
public override void ChangeDatabase(string databaseName) => Inner.ChangeDatabase(databaseName);
|
||||
public override Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default) => Inner.ChangeDatabaseAsync(databaseName, ct);
|
||||
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => throw SyncError(nameof(BeginDbTransaction));
|
||||
protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(IsolationLevel level, CancellationToken ct) => await Inner.BeginTransactionAsync(level, ct);
|
||||
|
||||
public override void Open() => throw SyncError(nameof(Open));
|
||||
public override void Close() => throw SyncError(nameof(Close));
|
||||
|
||||
IDbTransaction IPKConnection.BeginTransaction() => throw SyncError(nameof(BeginTransaction));
|
||||
IDbTransaction IPKConnection.BeginTransaction(IsolationLevel level) => throw SyncError(nameof(BeginTransaction));
|
||||
|
||||
public override string ConnectionString
|
||||
{
|
||||
get => Inner.ConnectionString;
|
||||
set => Inner.ConnectionString = value;
|
||||
}
|
||||
|
||||
public override string? Database => Inner.Database;
|
||||
public override ConnectionState State => Inner.State;
|
||||
public override string DataSource => Inner.DataSource;
|
||||
public override string ServerVersion => Inner.ServerVersion;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Inner.Dispose();
|
||||
if (_hasClosed) return;
|
||||
|
||||
LogClose();
|
||||
}
|
||||
|
||||
public override ValueTask DisposeAsync()
|
||||
{
|
||||
if (_hasClosed) return Inner.DisposeAsync();
|
||||
LogClose();
|
||||
return Inner.DisposeAsync();
|
||||
}
|
||||
|
||||
private void LogClose()
|
||||
{
|
||||
_countHolder.Decrement();
|
||||
_hasClosed = true;
|
||||
|
||||
var duration = SystemClock.Instance.GetCurrentInstant() - _openTime;
|
||||
_logger.Verbose("Closed database connection {ConnectionId} (open for {ConnectionDuration}), new connection count {ConnectionCount}", ConnectionId, duration, _countHolder.ConnectionCount);
|
||||
}
|
||||
|
||||
private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IPKConnection function {caller}!");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user