Track database handles over metrics
This commit is contained in:
		| @@ -79,7 +79,8 @@ namespace PluralKit.Bot | ||||
|             .AddTransient(_ => _config.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig()) | ||||
|             .AddTransient(_ => _config.GetSection("PluralKit").GetSection("Bot").Get<BotConfig>() ?? new BotConfig()) | ||||
|  | ||||
|             .AddTransient(svc => new DbConnectionFactory(svc.GetRequiredService<CoreConfig>().Database)) | ||||
|             .AddSingleton<DbConnectionCountHolder>() | ||||
|             .AddTransient<DbConnectionFactory>() | ||||
|  | ||||
|             .AddSingleton<IDiscordClient, DiscordShardedClient>(_ => new DiscordShardedClient(new DiscordSocketConfig | ||||
|             { | ||||
|   | ||||
| @@ -24,9 +24,11 @@ namespace PluralKit.Bot | ||||
|  | ||||
|         private WebhookCacheService _webhookCache; | ||||
|  | ||||
|         private DbConnectionCountHolder _countHolder; | ||||
|  | ||||
|         private ILogger _logger; | ||||
|  | ||||
|         public PeriodicStatCollector(IDiscordClient client, IMetrics metrics, SystemStore systems, MemberStore members, SwitchStore switches, MessageStore messages, ILogger logger, WebhookCacheService webhookCache) | ||||
|         public PeriodicStatCollector(IDiscordClient client, IMetrics metrics, SystemStore systems, MemberStore members, SwitchStore switches, MessageStore messages, ILogger logger, WebhookCacheService webhookCache, DbConnectionCountHolder countHolder) | ||||
|         { | ||||
|             _client = (DiscordShardedClient) client; | ||||
|             _metrics = metrics; | ||||
| @@ -35,6 +37,7 @@ namespace PluralKit.Bot | ||||
|             _switches = switches; | ||||
|             _messages = messages; | ||||
|             _webhookCache = webhookCache; | ||||
|             _countHolder = countHolder; | ||||
|             _logger = logger.ForContext<PeriodicStatCollector>(); | ||||
|         } | ||||
|  | ||||
| @@ -75,6 +78,9 @@ namespace PluralKit.Bot | ||||
|             _metrics.Measure.Gauge.SetValue(CoreMetrics.ProcessHandles, process.HandleCount); | ||||
|             _metrics.Measure.Gauge.SetValue(CoreMetrics.CpuUsage, await EstimateCpuUsage()); | ||||
|              | ||||
|             // Database info | ||||
|             _metrics.Measure.Gauge.SetValue(CoreMetrics.DatabaseConnections, _countHolder.ConnectionCount); | ||||
|              | ||||
|             // Other shiz | ||||
|             _metrics.Measure.Gauge.SetValue(BotMetrics.WebhookCacheSize, _webhookCache.CacheSize); | ||||
|  | ||||
|   | ||||
| @@ -17,5 +17,8 @@ namespace PluralKit.Core | ||||
|         public static GaugeOptions ProcessThreads => new GaugeOptions { Name = "Process Thread Count", MeasurementUnit = Unit.Threads, Context = "Process" }; | ||||
|         public static GaugeOptions ProcessHandles => new GaugeOptions { Name = "Process Handle Count", MeasurementUnit = Unit.Items, Context = "Process" }; | ||||
|         public static GaugeOptions CpuUsage => new GaugeOptions { Name = "CPU Usage", MeasurementUnit = Unit.Percent, Context = "Process" }; | ||||
|          | ||||
|         public static MeterOptions DatabaseRequests => new MeterOptions() { Name = "Database Requests", MeasurementUnit = Unit.Requests, Context = "Database" }; | ||||
|         public static GaugeOptions DatabaseConnections => new GaugeOptions() { Name = "Database Connections", MeasurementUnit = Unit.Connections, Context = "Database" }; | ||||
|     } | ||||
| } | ||||
| @@ -5,7 +5,9 @@ using System.IO; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
| using System.Text.RegularExpressions; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using App.Metrics; | ||||
| using Dapper; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Newtonsoft.Json; | ||||
| @@ -13,6 +15,7 @@ using NodaTime; | ||||
| using NodaTime.Serialization.JsonNet; | ||||
| using NodaTime.Text; | ||||
| using Npgsql; | ||||
| using PluralKit.Core; | ||||
| using Serilog; | ||||
| using Serilog.Formatting.Compact; | ||||
| using Serilog.Sinks.SystemConsole.Themes; | ||||
| @@ -349,18 +352,109 @@ namespace PluralKit | ||||
|      | ||||
|     public class DbConnectionFactory | ||||
|     { | ||||
|         private string _connectionString; | ||||
|         private CoreConfig _config; | ||||
|         private IMetrics _metrics; | ||||
|         private DbConnectionCountHolder _countHolder; | ||||
|  | ||||
|         public DbConnectionFactory(string connectionString) | ||||
|         public DbConnectionFactory(CoreConfig config, DbConnectionCountHolder countHolder, IMetrics metrics) | ||||
|         { | ||||
|             _connectionString = connectionString; | ||||
|             _config = config; | ||||
|             _countHolder = countHolder; | ||||
|             _metrics = metrics; | ||||
|         } | ||||
|  | ||||
|         public async Task<IDbConnection> Obtain() | ||||
|         { | ||||
|             var conn = new NpgsqlConnection(_connectionString); | ||||
|             // Mark the request (for a handle, I guess) in the metrics | ||||
|             _metrics.Measure.Meter.Mark(CoreMetrics.DatabaseRequests); | ||||
|              | ||||
|             // Actually create and try to open the connection | ||||
|             var conn = new NpgsqlConnection(_config.Database); | ||||
|             await conn.OpenAsync(); | ||||
|             return conn; | ||||
|              | ||||
|             // Increment the count | ||||
|             _countHolder.Increment(); | ||||
|             // Return a wrapped connection which will decrement the counter on dispose | ||||
|             return new DbConnectionTrackingConnection(conn, _countHolder); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class DbConnectionCountHolder | ||||
|     { | ||||
|         private int _connectionCount; | ||||
|         public int ConnectionCount => _connectionCount; | ||||
|  | ||||
|         public void Increment() | ||||
|         { | ||||
|             Interlocked.Increment(ref _connectionCount); | ||||
|         } | ||||
|  | ||||
|         public void Decrement() | ||||
|         { | ||||
|             Interlocked.Decrement(ref _connectionCount); | ||||
|         } | ||||
|     }  | ||||
|      | ||||
|     public class DbConnectionTrackingConnection: IDbConnection | ||||
|     { | ||||
|         // Simple delegation of everything. | ||||
|         private IDbConnection _impl; | ||||
|  | ||||
|         private DbConnectionCountHolder _countHolder; | ||||
|  | ||||
|         public DbConnectionTrackingConnection(IDbConnection impl, DbConnectionCountHolder countHolder) | ||||
|         { | ||||
|             _impl = impl; | ||||
|             _countHolder = countHolder; | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|             _impl.Dispose(); | ||||
|              | ||||
|             _countHolder.Decrement(); | ||||
|         } | ||||
|  | ||||
|         public IDbTransaction BeginTransaction() | ||||
|         { | ||||
|             return _impl.BeginTransaction(); | ||||
|         } | ||||
|  | ||||
|         public IDbTransaction BeginTransaction(IsolationLevel il) | ||||
|         { | ||||
|             return _impl.BeginTransaction(il); | ||||
|         } | ||||
|  | ||||
|         public void ChangeDatabase(string databaseName) | ||||
|         { | ||||
|             _impl.ChangeDatabase(databaseName); | ||||
|         } | ||||
|  | ||||
|         public void Close() | ||||
|         { | ||||
|             _impl.Close(); | ||||
|         } | ||||
|  | ||||
|         public IDbCommand CreateCommand() | ||||
|         { | ||||
|             return _impl.CreateCommand(); | ||||
|         } | ||||
|  | ||||
|         public void Open() | ||||
|         { | ||||
|             _impl.Open(); | ||||
|         } | ||||
|  | ||||
|         public string ConnectionString | ||||
|         { | ||||
|             get => _impl.ConnectionString; | ||||
|             set => _impl.ConnectionString = value; | ||||
|         } | ||||
|  | ||||
|         public int ConnectionTimeout => _impl.ConnectionTimeout; | ||||
|  | ||||
|         public string Database => _impl.Database; | ||||
|  | ||||
|         public ConnectionState State => _impl.State; | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user