perf: optimize authorization scoreline and workers

This commit is contained in:
2026-07-30 09:12:27 +08:00
parent 4bea745b79
commit bedc77bffd
52 changed files with 21911 additions and 347 deletions

View File

@@ -0,0 +1,106 @@
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Security.Cryptography;
using System.Text;
namespace Tiku.Infrastructure.Observability;
public sealed record DatabaseRequestSnapshot(
int CommandCount,
double TotalDurationMilliseconds,
double SlowestDurationMilliseconds,
string? SlowestCommandFingerprint);
public static class DatabasePerformanceTelemetry
{
public const string MeterName = "Tiku.Database";
internal static readonly Meter Meter = new(MeterName, "1.0.0");
internal static readonly Counter<long> CommandCounter = Meter.CreateCounter<long>(
"tiku.database.commands",
description: "Number of database commands executed.");
internal static readonly Histogram<double> CommandDuration = Meter.CreateHistogram<double>(
"tiku.database.command.duration",
unit: "ms",
description: "Database command execution duration.");
internal static readonly Histogram<long> RequestCommandCount = Meter.CreateHistogram<long>(
"tiku.database.request.commands",
description: "Database commands executed during one HTTP request.");
internal static readonly Histogram<double> RequestCommandDuration = Meter.CreateHistogram<double>(
"tiku.database.request.duration",
unit: "ms",
description: "Aggregate database command duration during one HTTP request.");
public static string Fingerprint(string commandText)
{
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(commandText));
return Convert.ToHexString(hash.AsSpan(0, 8)).ToLowerInvariant();
}
}
public static class DatabaseRequestMetrics
{
private static readonly AsyncLocal<RequestState?> CurrentState = new();
public static IDisposable Begin()
{
var previous = CurrentState.Value;
var current = new RequestState();
CurrentState.Value = current;
return new Scope(previous, current);
}
internal static void Record(TimeSpan duration, string fingerprint)
{
CurrentState.Value?.Record(duration, fingerprint);
}
private sealed class RequestState
{
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
private int commandCount;
private long totalTicks;
private long slowestTicks;
private string? slowestFingerprint;
public void Record(TimeSpan duration, string fingerprint)
{
commandCount++;
totalTicks += duration.Ticks;
if (duration.Ticks > slowestTicks)
{
slowestTicks = duration.Ticks;
slowestFingerprint = fingerprint;
}
}
public DatabaseRequestSnapshot Complete()
{
stopwatch.Stop();
return new DatabaseRequestSnapshot(
commandCount,
TimeSpan.FromTicks(totalTicks).TotalMilliseconds,
TimeSpan.FromTicks(slowestTicks).TotalMilliseconds,
slowestFingerprint);
}
}
private sealed class Scope(RequestState? previous, RequestState current) : IDisposable
{
private bool disposed;
public void Dispose()
{
if (disposed)
{
return;
}
disposed = true;
var snapshot = current.Complete();
DatabasePerformanceTelemetry.RequestCommandCount.Record(snapshot.CommandCount);
DatabasePerformanceTelemetry.RequestCommandDuration.Record(snapshot.TotalDurationMilliseconds);
CurrentState.Value = previous;
}
}
}