Track database handles over metrics

This commit is contained in:
Ske 2019-08-11 22:56:20 +02:00
parent 1cfeaf281b
commit 942022d408
4 changed files with 111 additions and 7 deletions

View File

@ -79,7 +79,8 @@ namespace PluralKit.Bot
.AddTransient(_ => _config.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig()) .AddTransient(_ => _config.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig())
.AddTransient(_ => _config.GetSection("PluralKit").GetSection("Bot").Get<BotConfig>() ?? new BotConfig()) .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 .AddSingleton<IDiscordClient, DiscordShardedClient>(_ => new DiscordShardedClient(new DiscordSocketConfig
{ {

View File

@ -24,9 +24,11 @@ namespace PluralKit.Bot
private WebhookCacheService _webhookCache; private WebhookCacheService _webhookCache;
private DbConnectionCountHolder _countHolder;
private ILogger _logger; 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; _client = (DiscordShardedClient) client;
_metrics = metrics; _metrics = metrics;
@ -35,6 +37,7 @@ namespace PluralKit.Bot
_switches = switches; _switches = switches;
_messages = messages; _messages = messages;
_webhookCache = webhookCache; _webhookCache = webhookCache;
_countHolder = countHolder;
_logger = logger.ForContext<PeriodicStatCollector>(); _logger = logger.ForContext<PeriodicStatCollector>();
} }
@ -75,6 +78,9 @@ namespace PluralKit.Bot
_metrics.Measure.Gauge.SetValue(CoreMetrics.ProcessHandles, process.HandleCount); _metrics.Measure.Gauge.SetValue(CoreMetrics.ProcessHandles, process.HandleCount);
_metrics.Measure.Gauge.SetValue(CoreMetrics.CpuUsage, await EstimateCpuUsage()); _metrics.Measure.Gauge.SetValue(CoreMetrics.CpuUsage, await EstimateCpuUsage());
// Database info
_metrics.Measure.Gauge.SetValue(CoreMetrics.DatabaseConnections, _countHolder.ConnectionCount);
// Other shiz // Other shiz
_metrics.Measure.Gauge.SetValue(BotMetrics.WebhookCacheSize, _webhookCache.CacheSize); _metrics.Measure.Gauge.SetValue(BotMetrics.WebhookCacheSize, _webhookCache.CacheSize);

View File

@ -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 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 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 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" };
} }
} }

View File

@ -5,7 +5,9 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using App.Metrics;
using Dapper; using Dapper;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -13,6 +15,7 @@ using NodaTime;
using NodaTime.Serialization.JsonNet; using NodaTime.Serialization.JsonNet;
using NodaTime.Text; using NodaTime.Text;
using Npgsql; using Npgsql;
using PluralKit.Core;
using Serilog; using Serilog;
using Serilog.Formatting.Compact; using Serilog.Formatting.Compact;
using Serilog.Sinks.SystemConsole.Themes; using Serilog.Sinks.SystemConsole.Themes;
@ -349,18 +352,109 @@ namespace PluralKit
public class DbConnectionFactory 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() 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(); 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;
}
} }