feat: upgrade to .NET 6, refactor everything

This commit is contained in:
spiral
2021-11-26 21:10:56 -05:00
parent d28e99ba43
commit 1918c56937
314 changed files with 27954 additions and 27966 deletions

View File

@@ -1,17 +1,13 @@
using System;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
namespace PluralKit.Core
namespace PluralKit.Core;
public interface IPKCommand: IDbCommand, IAsyncDisposable
{
public interface IPKCommand: IDbCommand, IAsyncDisposable
{
public Task PrepareAsync(CancellationToken ct = default);
public Task<int> ExecuteNonQueryAsync(CancellationToken ct = default);
public Task<object?> ExecuteScalarAsync(CancellationToken ct = default);
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken ct = default);
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken ct = default);
}
public Task PrepareAsync(CancellationToken ct = default);
public Task<int> ExecuteNonQueryAsync(CancellationToken ct = default);
public Task<object?> ExecuteScalarAsync(CancellationToken ct = default);
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken ct = default);
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken ct = default);
}

View File

@@ -1,31 +1,29 @@
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using Npgsql;
namespace PluralKit.Core
namespace PluralKit.Core;
public interface IPKConnection: IDbConnection, IAsyncDisposable
{
public interface IPKConnection: IDbConnection, IAsyncDisposable
{
public Guid ConnectionId { get; }
public Guid ConnectionId { get; }
public Task OpenAsync(CancellationToken cancellationToken = default);
public Task CloseAsync();
public Task OpenAsync(CancellationToken cancellationToken = default);
public Task CloseAsync();
public Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default);
public Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default);
public ValueTask<IPKTransaction> BeginTransactionAsync(CancellationToken ct = default) => BeginTransactionAsync(IsolationLevel.Unspecified, ct);
public ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default);
public ValueTask<IPKTransaction> BeginTransactionAsync(CancellationToken ct = default) =>
BeginTransactionAsync(IsolationLevel.Unspecified, ct);
public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand);
public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand);
public ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default);
[Obsolete] new void Open();
[Obsolete] new void Close();
public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand);
public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand);
[Obsolete] new IDbTransaction BeginTransaction();
[Obsolete] new IDbTransaction BeginTransaction(IsolationLevel il);
}
[Obsolete] new void Open();
[Obsolete] new void Close();
[Obsolete] new IDbTransaction BeginTransaction();
[Obsolete] new IDbTransaction BeginTransaction(IsolationLevel il);
}

View File

@@ -1,13 +1,9 @@
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
namespace PluralKit.Core
namespace PluralKit.Core;
public interface IPKTransaction: IDbTransaction, IAsyncDisposable
{
public interface IPKTransaction: IDbTransaction, IAsyncDisposable
{
public Task CommitAsync(CancellationToken ct = default);
public Task RollbackAsync(CancellationToken ct = default);
}
public Task CommitAsync(CancellationToken ct = default);
public Task RollbackAsync(CancellationToken ct = default);
}

View File

@@ -1,10 +1,7 @@
#nullable enable
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using App.Metrics;
@@ -14,113 +11,125 @@ using Npgsql;
using Serilog;
namespace PluralKit.Core
namespace PluralKit.Core;
internal class PKCommand: DbCommand, IPKCommand
{
internal class PKCommand: DbCommand, IPKCommand
private readonly ILogger _logger;
private readonly IMetrics _metrics;
private readonly PKConnection _ourConnection;
public PKCommand(NpgsqlCommand inner, PKConnection ourConnection, ILogger logger, IMetrics metrics)
{
private NpgsqlCommand Inner { get; }
private readonly PKConnection _ourConnection;
private readonly ILogger _logger;
private readonly IMetrics _metrics;
public PKCommand(NpgsqlCommand inner, PKConnection ourConnection, ILogger logger, IMetrics metrics)
{
Inner = inner;
_ourConnection = ourConnection;
_logger = logger.ForContext<PKCommand>();
_metrics = metrics;
}
public override Task<int> ExecuteNonQueryAsync(CancellationToken ct) => LogQuery(Inner.ExecuteNonQueryAsync(ct));
public override Task<object?> ExecuteScalarAsync(CancellationToken ct) => LogQuery(Inner.ExecuteScalarAsync(ct));
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken ct) => await LogQuery(Inner.ExecuteReaderAsync(behavior, ct));
public override Task PrepareAsync(CancellationToken ct = default) => Inner.PrepareAsync(ct);
public override void Cancel() => Inner.Cancel();
protected override DbParameter CreateDbParameter() => Inner.CreateParameter();
[AllowNull]
public override string CommandText
{
get => Inner.CommandText;
set => Inner.CommandText = value;
}
public override int CommandTimeout
{
get => Inner.CommandTimeout;
set => Inner.CommandTimeout = value;
}
public override CommandType CommandType
{
get => Inner.CommandType;
set => Inner.CommandType = value;
}
public override UpdateRowSource UpdatedRowSource
{
get => Inner.UpdatedRowSource;
set => Inner.UpdatedRowSource = value;
}
protected override DbParameterCollection DbParameterCollection => Inner.Parameters;
protected override DbTransaction? DbTransaction
{
get => Inner.Transaction;
set => Inner.Transaction = value switch
{
NpgsqlTransaction npg => npg,
PKTransaction pk => pk.Inner,
_ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlTransaction")
};
}
public override bool DesignTimeVisible
{
get => Inner.DesignTimeVisible;
set => Inner.DesignTimeVisible = value;
}
protected override DbConnection? DbConnection
{
get => Inner.Connection;
set =>
Inner.Connection = value switch
{
NpgsqlConnection npg => npg,
PKConnection pk => pk.Inner,
_ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlConnection")
};
}
public override int ExecuteNonQuery() => throw SyncError(nameof(ExecuteNonQuery));
public override object ExecuteScalar() => throw SyncError(nameof(ExecuteScalar));
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => throw SyncError(nameof(ExecuteDbDataReader));
public override void Prepare() => throw SyncError(nameof(Prepare));
private async Task<T> LogQuery<T>(Task<T> task)
{
var start = SystemClock.Instance.GetCurrentInstant();
try
{
return await task;
}
finally
{
var end = SystemClock.Instance.GetCurrentInstant();
var elapsed = end - start;
_logger.Verbose("Executed query {Query} in {ElapsedTime} on connection {ConnectionId}", CommandText, elapsed, _ourConnection.ConnectionId);
// One "BCL compatible tick" is 100 nanoseconds
var micros = elapsed.BclCompatibleTicks / 10;
_metrics.Provider.Timer.Instance(CoreMetrics.DatabaseQuery, new MetricTags("query", CommandText))
.Record(micros, TimeUnit.Microseconds, CommandText);
}
}
private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbCommand function {caller}!");
Inner = inner;
_ourConnection = ourConnection;
_logger = logger.ForContext<PKCommand>();
_metrics = metrics;
}
private NpgsqlCommand Inner { get; }
protected override DbParameterCollection DbParameterCollection => Inner.Parameters;
protected override DbTransaction? DbTransaction
{
get => Inner.Transaction;
set => Inner.Transaction = value switch
{
NpgsqlTransaction npg => npg,
PKTransaction pk => pk.Inner,
_ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlTransaction")
};
}
public override bool DesignTimeVisible
{
get => Inner.DesignTimeVisible;
set => Inner.DesignTimeVisible = value;
}
protected override DbConnection? DbConnection
{
get => Inner.Connection;
set =>
Inner.Connection = value switch
{
NpgsqlConnection npg => npg,
PKConnection pk => pk.Inner,
_ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlConnection")
};
}
public override Task<int> ExecuteNonQueryAsync(CancellationToken ct) =>
LogQuery(Inner.ExecuteNonQueryAsync(ct));
public override Task<object?> ExecuteScalarAsync(CancellationToken ct) =>
LogQuery(Inner.ExecuteScalarAsync(ct));
public override Task PrepareAsync(CancellationToken ct = default) => Inner.PrepareAsync(ct);
public override void Cancel() => Inner.Cancel();
[AllowNull]
public override string CommandText
{
get => Inner.CommandText;
set => Inner.CommandText = value;
}
public override int CommandTimeout
{
get => Inner.CommandTimeout;
set => Inner.CommandTimeout = value;
}
public override CommandType CommandType
{
get => Inner.CommandType;
set => Inner.CommandType = value;
}
public override UpdateRowSource UpdatedRowSource
{
get => Inner.UpdatedRowSource;
set => Inner.UpdatedRowSource = value;
}
public override int ExecuteNonQuery() => throw SyncError(nameof(ExecuteNonQuery));
public override object ExecuteScalar() => throw SyncError(nameof(ExecuteScalar));
public override void Prepare() => throw SyncError(nameof(Prepare));
protected override async Task<DbDataReader>
ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken ct) =>
await LogQuery(Inner.ExecuteReaderAsync(behavior, ct));
protected override DbParameter CreateDbParameter() => Inner.CreateParameter();
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
throw SyncError(nameof(ExecuteDbDataReader));
private async Task<T> LogQuery<T>(Task<T> task)
{
var start = SystemClock.Instance.GetCurrentInstant();
try
{
return await task;
}
finally
{
var end = SystemClock.Instance.GetCurrentInstant();
var elapsed = end - start;
_logger.Verbose("Executed query {Query} in {ElapsedTime} on connection {ConnectionId}", CommandText,
elapsed, _ourConnection.ConnectionId);
// One "BCL compatible tick" is 100 nanoseconds
var micros = elapsed.BclCompatibleTicks / 10;
_metrics.Provider.Timer.Instance(CoreMetrics.DatabaseQuery, new MetricTags("query", CommandText))
.Record(micros, TimeUnit.Microseconds, CommandText);
}
}
private static Exception SyncError(string caller) =>
throw new Exception($"Executed synchronous IDbCommand function {caller}!");
}

View File

@@ -1,10 +1,7 @@
#nullable enable
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using App.Metrics;
@@ -14,102 +11,120 @@ using Npgsql;
using Serilog;
namespace PluralKit.Core
namespace PluralKit.Core;
internal class PKConnection: DbConnection, IPKConnection
{
internal class PKConnection: DbConnection, IPKConnection
private readonly DbConnectionCountHolder _countHolder;
private readonly ILogger _logger;
private readonly IMetrics _metrics;
private bool _hasClosed;
private bool _hasOpened;
private Instant _openTime;
public PKConnection(NpgsqlConnection inner, DbConnectionCountHolder countHolder, ILogger logger,
IMetrics metrics)
{
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 new async ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default) => new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
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) => new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
public override void Open() => throw SyncError(nameof(Open));
public override void Close()
{
// Don't throw SyncError here, Dapper calls sync Close() internally so that sucks
Inner.Close();
}
IDbTransaction IPKConnection.BeginTransaction() => throw SyncError(nameof(BeginTransaction));
IDbTransaction IPKConnection.BeginTransaction(IsolationLevel level) => throw SyncError(nameof(BeginTransaction));
[AllowNull]
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 IDbCommand function {caller}!");
Inner = inner;
ConnectionId = Guid.NewGuid();
_countHolder = countHolder;
_logger = logger.ForContext<PKConnection>();
_metrics = metrics;
}
public NpgsqlConnection Inner { get; }
public override string DataSource => Inner.DataSource;
public override string ServerVersion => Inner.ServerVersion;
public Guid ConnectionId { get; }
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();
public new async ValueTask<IPKTransaction>
BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default) =>
new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
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);
public override void Open() => throw SyncError(nameof(Open));
public override void Close()
{
// Don't throw SyncError here, Dapper calls sync Close() internally so that sucks
Inner.Close();
}
IDbTransaction IPKConnection.BeginTransaction() => throw SyncError(nameof(BeginTransaction));
IDbTransaction IPKConnection.BeginTransaction(IsolationLevel level) =>
throw SyncError(nameof(BeginTransaction));
[AllowNull]
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 ValueTask DisposeAsync()
{
if (_hasClosed) return Inner.DisposeAsync();
LogClose();
return Inner.DisposeAsync();
}
protected override DbCommand CreateDbCommand() => new PKCommand(Inner.CreateCommand(), this, _logger, _metrics);
public void ReloadTypes() => Inner.ReloadTypes();
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) =>
throw SyncError(nameof(BeginDbTransaction));
protected override async ValueTask<DbTransaction>
BeginDbTransactionAsync(IsolationLevel level, CancellationToken ct) =>
new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
protected override void Dispose(bool disposing)
{
Inner.Dispose();
if (_hasClosed) return;
LogClose();
}
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 IDbCommand function {caller}!");
}

View File

@@ -1,31 +1,28 @@
using System;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Npgsql;
namespace PluralKit.Core
namespace PluralKit.Core;
internal class PKTransaction: DbTransaction, IPKTransaction
{
internal class PKTransaction: DbTransaction, IPKTransaction
public PKTransaction(NpgsqlTransaction inner)
{
public NpgsqlTransaction Inner { get; }
public PKTransaction(NpgsqlTransaction inner)
{
Inner = inner;
}
public override void Commit() => throw SyncError(nameof(Commit));
public override Task CommitAsync(CancellationToken ct = default) => Inner.CommitAsync(ct);
public override void Rollback() => throw SyncError(nameof(Rollback));
public override Task RollbackAsync(CancellationToken ct = default) => Inner.RollbackAsync(ct);
protected override DbConnection DbConnection => Inner.Connection;
public override IsolationLevel IsolationLevel => Inner.IsolationLevel;
private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbTransaction function {caller}!");
Inner = inner;
}
public NpgsqlTransaction Inner { get; }
protected override DbConnection DbConnection => Inner.Connection;
public override void Commit() => throw SyncError(nameof(Commit));
public override Task CommitAsync(CancellationToken ct = default) => Inner.CommitAsync(ct);
public override void Rollback() => throw SyncError(nameof(Rollback));
public override Task RollbackAsync(CancellationToken ct = default) => Inner.RollbackAsync(ct);
public override IsolationLevel IsolationLevel => Inner.IsolationLevel;
private static Exception SyncError(string caller) =>
throw new Exception($"Executed synchronous IDbTransaction function {caller}!");
}