feat: add Redis identify ratelimiter

This commit is contained in:
spiral 2022-01-13 12:26:25 -05:00
parent 8aa2b0b7aa
commit 2aead40449
No known key found for this signature in database
GPG Key ID: A6059F0CA0E1BD31
15 changed files with 449 additions and 40 deletions

View File

@ -5,6 +5,8 @@ using Myriad.Types;
using Serilog; using Serilog;
using StackExchange.Redis;
namespace Myriad.Gateway; namespace Myriad.Gateway;
public class Cluster public class Cluster
@ -25,14 +27,14 @@ public class Cluster
public IReadOnlyDictionary<int, Shard> Shards => _shards; public IReadOnlyDictionary<int, Shard> Shards => _shards;
public event Action<Shard>? ShardCreated; public event Action<Shard>? ShardCreated;
public async Task Start(GatewayInfo.Bot info) public async Task Start(GatewayInfo.Bot info, ConnectionMultiplexer? conn = null)
{ {
await Start(info.Url, 0, info.Shards - 1, info.Shards, info.SessionStartLimit.MaxConcurrency); await Start(info.Url, 0, info.Shards - 1, info.Shards, info.SessionStartLimit.MaxConcurrency, conn);
} }
public async Task Start(string url, int shardMin, int shardMax, int shardTotal, int recommendedConcurrency) public async Task Start(string url, int shardMin, int shardMax, int shardTotal, int recommendedConcurrency, ConnectionMultiplexer? conn = null)
{ {
_ratelimiter = GetRateLimiter(recommendedConcurrency); _ratelimiter = GetRateLimiter(recommendedConcurrency, conn);
var shardCount = shardMax - shardMin + 1; var shardCount = shardMax - shardMin + 1;
_logger.Information("Starting {ShardCount} of {ShardTotal} shards (#{ShardMin}-#{ShardMax}) at {Url}", _logger.Information("Starting {ShardCount} of {ShardTotal} shards (#{ShardMin}-#{ShardMax}) at {Url}",
@ -73,12 +75,21 @@ public class Cluster
return Math.Min(_gatewaySettings.MaxShardConcurrency.Value, recommendedConcurrency); return Math.Min(_gatewaySettings.MaxShardConcurrency.Value, recommendedConcurrency);
} }
private IGatewayRatelimiter GetRateLimiter(int recommendedConcurrency) private IGatewayRatelimiter GetRateLimiter(int recommendedConcurrency, ConnectionMultiplexer? conn = null)
{ {
var concurrency = GetActualShardConcurrency(recommendedConcurrency);
if (_gatewaySettings.UseRedisRatelimiter)
{
if (conn != null)
return new RedisRatelimiter(_logger, conn, concurrency);
else
_logger.Warning("Tried to get Redis ratelimiter but connection is null! Continuing with local ratelimiter.");
}
if (_gatewaySettings.GatewayQueueUrl != null) if (_gatewaySettings.GatewayQueueUrl != null)
return new TwilightGatewayRatelimiter(_logger, _gatewaySettings.GatewayQueueUrl); return new TwilightGatewayRatelimiter(_logger, _gatewaySettings.GatewayQueueUrl);
var concurrency = GetActualShardConcurrency(recommendedConcurrency);
return new LocalGatewayRatelimiter(_logger, concurrency); return new LocalGatewayRatelimiter(_logger, concurrency);
} }
} }

View File

@ -4,6 +4,7 @@ public record GatewaySettings
{ {
public string Token { get; init; } public string Token { get; init; }
public GatewayIntent Intents { get; init; } public GatewayIntent Intents { get; init; }
public bool UseRedisRatelimiter { get; init; } = false;
public int? MaxShardConcurrency { get; init; } public int? MaxShardConcurrency { get; init; }
public string? GatewayQueueUrl { get; init; } public string? GatewayQueueUrl { get; init; }
} }

View File

@ -0,0 +1,46 @@
using Serilog;
using StackExchange.Redis;
namespace Myriad.Gateway.Limit;
public class RedisRatelimiter: IGatewayRatelimiter
{
private readonly ILogger _logger;
private readonly ConnectionMultiplexer _redis;
private int _concurrency { get; init; }
// todo: these might need to be tweaked a little
private static TimeSpan expiry = TimeSpan.FromSeconds(5);
private static TimeSpan retryInterval = TimeSpan.FromSeconds(1);
public RedisRatelimiter(ILogger logger, ConnectionMultiplexer redis, int concurrency)
{
_logger = logger.ForContext<TwilightGatewayRatelimiter>();
_redis = redis;
_concurrency = concurrency;
}
public async Task Identify(int shard)
{
_logger.Information("Shard {ShardId}: requesting identify from Redis", shard);
var key = "pluralkit:identify:" + (shard % _concurrency).ToString();
await AcquireLock(key);
}
public async Task AcquireLock(string key)
{
var conn = _redis.GetDatabase();
async Task<bool> TryAcquire()
{
_logger.Verbose("Trying to acquire lock on key {key} from Redis...", key);
await Task.Delay(retryInterval);
return await conn!.StringSetAsync(key, 0, expiry, When.NotExists);
}
var acquired = false;
while (!acquired) acquired = await TryAcquire();
}
}

View File

@ -24,6 +24,7 @@
<PackageReference Include="Polly" Version="7.2.1"/> <PackageReference Include="Polly" Version="7.2.1"/>
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1"/> <PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1"/>
<PackageReference Include="Serilog" Version="2.10.0"/> <PackageReference Include="Serilog" Version="2.10.0"/>
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
<PackageReference Include="System.Linq.Async" Version="5.0.0"/> <PackageReference Include="System.Linq.Async" Version="5.0.0"/>
</ItemGroup> </ItemGroup>

View File

@ -1,31 +1,140 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
"net6.0": { "net6.0": {
"Polly": { "Polly": {
"type": "Direct", "type": "Direct",
"requested": "[7.2.1, )", "requested": "[7.2.1, )",
"resolved": "7.2.1", "resolved": "7.2.1",
"contentHash": "Od8SnPlpQr+PuAS0YzY3jgtzaDNknlIuAaldN2VEIyTvm/wCg22C5nUkUV1BEG8rIsub5RFMoV/NEQ0tM/+7Uw==" "contentHash": "Od8SnPlpQr+PuAS0YzY3jgtzaDNknlIuAaldN2VEIyTvm/wCg22C5nUkUV1BEG8rIsub5RFMoV/NEQ0tM/+7Uw=="
}, },
"Polly.Contrib.WaitAndRetry": { "Polly.Contrib.WaitAndRetry": {
"type": "Direct", "type": "Direct",
"requested": "[1.1.1, )", "requested": "[1.1.1, )",
"resolved": "1.1.1", "resolved": "1.1.1",
"contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA=="
}, },
"Serilog": { "Serilog": {
"type": "Direct", "type": "Direct",
"requested": "[2.10.0, )", "requested": "[2.10.0, )",
"resolved": "2.10.0", "resolved": "2.10.0",
"contentHash": "+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==" "contentHash": "+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA=="
}, },
"System.Linq.Async": { "StackExchange.Redis": {
"type": "Direct", "type": "Direct",
"requested": "[5.0.0, )", "requested": "[2.2.88, )",
"resolved": "5.0.0", "resolved": "2.2.88",
"contentHash": "cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==" "contentHash": "JJi1jcO3/ZiamBhlsC/TR8aZmYf+nqpGzMi0HRRCy5wJkUPmMnRp0kBA6V84uhU8b531FHSdTDaFCAyCUJomjA==",
} "dependencies": {
"Pipelines.Sockets.Unofficial": "2.2.0",
"System.Diagnostics.PerformanceCounter": "5.0.0"
} }
},
"System.Linq.Async": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
},
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0"
}
},
"Pipelines.Sockets.Unofficial": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "7hzHplEIVOGBl5zOQZGX/DiJDHjq+RVRVrYgDiqXb6RriqWAdacXxp+XO9WSrATCEXyNOUOQg9aqQArsjase/A==",
"dependencies": {
"System.IO.Pipelines": "5.0.0"
}
},
"System.Configuration.ConfigurationManager": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==",
"dependencies": {
"System.Security.Cryptography.ProtectedData": "5.0.0",
"System.Security.Permissions": "5.0.0"
}
},
"System.Diagnostics.PerformanceCounter": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"Microsoft.Win32.Registry": "5.0.0",
"System.Configuration.ConfigurationManager": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==",
"dependencies": {
"Microsoft.Win32.SystemEvents": "5.0.0"
}
},
"System.IO.Pipelines": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "irMYm3vhVgRsYvHTU5b2gsT2CwT/SMM6LZFzuJjpIvT5Z4CshxNsaoBC1X/LltwuR3Opp8d6jOS/60WwOb7Q2Q=="
},
"System.Security.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA=="
},
"System.Security.Permissions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Windows.Extensions": "5.0.0"
}
},
"System.Security.Principal.Windows": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
},
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==",
"dependencies": {
"System.Drawing.Common": "5.0.0"
}
}
} }
}
} }

View File

@ -19,6 +19,7 @@ public class BotConfig
public ClusterSettings? Cluster { get; set; } public ClusterSettings? Cluster { get; set; }
public string? GatewayQueueUrl { get; set; } public string? GatewayQueueUrl { get; set; }
public bool UseRedisRatelimiter { get; set; } = false;
public string? DiscordBaseUrl { get; set; } public string? DiscordBaseUrl { get; set; }

View File

@ -42,6 +42,11 @@ public class Init
opts.DisableTaskUnobservedTaskExceptionCapture(); opts.DisableTaskUnobservedTaskExceptionCapture();
}); });
// initialize Redis
var coreConfig = services.Resolve<CoreConfig>();
var redis = services.Resolve<RedisService>();
await redis.InitAsync(coreConfig);
var config = services.Resolve<BotConfig>(); var config = services.Resolve<BotConfig>();
if (config.Cluster == null) if (config.Cluster == null)
{ {
@ -141,6 +146,8 @@ public class Init
{ {
var info = await services.Resolve<DiscordApiClient>().GetGatewayBot(); var info = await services.Resolve<DiscordApiClient>().GetGatewayBot();
var redis = services.Resolve<RedisService>();
var cluster = services.Resolve<Cluster>(); var cluster = services.Resolve<Cluster>();
var config = services.Resolve<BotConfig>(); var config = services.Resolve<BotConfig>();
@ -155,11 +162,11 @@ public class Init
var shardMin = (int)Math.Round(totalShards * (float)nodeIndex / totalNodes); var shardMin = (int)Math.Round(totalShards * (float)nodeIndex / totalNodes);
var shardMax = (int)Math.Round(totalShards * (float)(nodeIndex + 1) / totalNodes) - 1; var shardMax = (int)Math.Round(totalShards * (float)(nodeIndex + 1) / totalNodes) - 1;
await cluster.Start(info.Url, shardMin, shardMax, totalShards, info.SessionStartLimit.MaxConcurrency); await cluster.Start(info.Url, shardMin, shardMax, totalShards, info.SessionStartLimit.MaxConcurrency, redis.Connection);
} }
else else
{ {
await cluster.Start(info); await cluster.Start(info, redis.Connection);
} }
} }
} }

View File

@ -31,6 +31,7 @@ public class BotModule: Module
Token = botConfig.Token, Token = botConfig.Token,
MaxShardConcurrency = botConfig.MaxShardConcurrency, MaxShardConcurrency = botConfig.MaxShardConcurrency,
GatewayQueueUrl = botConfig.GatewayQueueUrl, GatewayQueueUrl = botConfig.GatewayQueueUrl,
UseRedisRatelimiter = botConfig.UseRedisRatelimiter,
Intents = GatewayIntent.Guilds | Intents = GatewayIntent.Guilds |
GatewayIntent.DirectMessages | GatewayIntent.DirectMessages |
GatewayIntent.DirectMessageReactions | GatewayIntent.DirectMessageReactions |

View File

@ -294,8 +294,8 @@
}, },
"Microsoft.NETCore.Platforms": { "Microsoft.NETCore.Platforms": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.1.0", "resolved": "5.0.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
}, },
"Microsoft.NETCore.Targets": { "Microsoft.NETCore.Targets": {
"type": "Transitive", "type": "Transitive",
@ -312,6 +312,23 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0"
}
},
"NETStandard.Library": { "NETStandard.Library": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.6.1", "resolved": "1.6.1",
@ -402,6 +419,14 @@
"Npgsql": "4.1.5" "Npgsql": "4.1.5"
} }
}, },
"Pipelines.Sockets.Unofficial": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "7hzHplEIVOGBl5zOQZGX/DiJDHjq+RVRVrYgDiqXb6RriqWAdacXxp+XO9WSrATCEXyNOUOQg9aqQArsjase/A==",
"dependencies": {
"System.IO.Pipelines": "5.0.0"
}
},
"Polly": { "Polly": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.2.1", "resolved": "7.2.1",
@ -631,6 +656,15 @@
"dapper": "1.50.5" "dapper": "1.50.5"
} }
}, },
"StackExchange.Redis": {
"type": "Transitive",
"resolved": "2.2.88",
"contentHash": "JJi1jcO3/ZiamBhlsC/TR8aZmYf+nqpGzMi0HRRCy5wJkUPmMnRp0kBA6V84uhU8b531FHSdTDaFCAyCUJomjA==",
"dependencies": {
"Pipelines.Sockets.Unofficial": "2.2.0",
"System.Diagnostics.PerformanceCounter": "5.0.0"
}
},
"System.AppContext": { "System.AppContext": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -671,6 +705,15 @@
"System.Threading.Tasks": "4.3.0" "System.Threading.Tasks": "4.3.0"
} }
}, },
"System.Configuration.ConfigurationManager": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==",
"dependencies": {
"System.Security.Cryptography.ProtectedData": "5.0.0",
"System.Security.Permissions": "5.0.0"
}
},
"System.Console": { "System.Console": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -698,6 +741,17 @@
"resolved": "4.7.1", "resolved": "4.7.1",
"contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw==" "contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw=="
}, },
"System.Diagnostics.PerformanceCounter": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"Microsoft.Win32.Registry": "5.0.0",
"System.Configuration.ConfigurationManager": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Diagnostics.Tools": { "System.Diagnostics.Tools": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -718,6 +772,14 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==",
"dependencies": {
"Microsoft.Win32.SystemEvents": "5.0.0"
}
},
"System.Globalization": { "System.Globalization": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -833,6 +895,11 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.IO.Pipelines": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "irMYm3vhVgRsYvHTU5b2gsT2CwT/SMM6LZFzuJjpIvT5Z4CshxNsaoBC1X/LltwuR3Opp8d6jOS/60WwOb7Q2Q=="
},
"System.Linq": { "System.Linq": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1089,6 +1156,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Security.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.Cryptography.Algorithms": { "System.Security.Cryptography.Algorithms": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1201,6 +1277,11 @@
"System.Threading.Tasks": "4.3.0" "System.Threading.Tasks": "4.3.0"
} }
}, },
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA=="
},
"System.Security.Cryptography.X509Certificates": { "System.Security.Cryptography.X509Certificates": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1233,6 +1314,20 @@
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
} }
}, },
"System.Security.Permissions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Windows.Extensions": "5.0.0"
}
},
"System.Security.Principal.Windows": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
},
"System.Text.Encoding": { "System.Text.Encoding": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1306,6 +1401,14 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==",
"dependencies": {
"System.Drawing.Common": "5.0.0"
}
},
"System.Xml.ReaderWriter": { "System.Xml.ReaderWriter": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1353,6 +1456,7 @@
"Polly": "7.2.1", "Polly": "7.2.1",
"Polly.Contrib.WaitAndRetry": "1.1.1", "Polly.Contrib.WaitAndRetry": "1.1.1",
"Serilog": "2.10.0", "Serilog": "2.10.0",
"StackExchange.Redis": "2.2.88",
"System.Linq.Async": "5.0.0" "System.Linq.Async": "5.0.0"
} }
}, },
@ -1388,6 +1492,7 @@
"Serilog.Sinks.File": "4.1.0", "Serilog.Sinks.File": "4.1.0",
"SqlKata": "2.3.7", "SqlKata": "2.3.7",
"SqlKata.Execution": "2.3.7", "SqlKata.Execution": "2.3.7",
"StackExchange.Redis": "2.2.88",
"System.Interactive.Async": "5.0.0", "System.Interactive.Async": "5.0.0",
"ipnetwork2": "2.5.381" "ipnetwork2": "2.5.381"
} }

View File

@ -5,6 +5,7 @@ namespace PluralKit.Core;
public class CoreConfig public class CoreConfig
{ {
public string Database { get; set; } public string Database { get; set; }
public string RedisAddr { get; set; }
public string SentryUrl { get; set; } public string SentryUrl { get; set; }
public string InfluxUrl { get; set; } public string InfluxUrl { get; set; }
public string InfluxDb { get; set; } public string InfluxDb { get; set; }

View File

@ -14,6 +14,8 @@ public class DataStoreModule: Module
builder.RegisterType<Database>().As<IDatabase>().SingleInstance(); builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
builder.RegisterType<ModelRepository>().AsSelf().SingleInstance(); builder.RegisterType<ModelRepository>().AsSelf().SingleInstance();
builder.RegisterType<RedisService>().AsSelf().SingleInstance();
builder.RegisterType<DispatchService>().AsSelf().SingleInstance(); builder.RegisterType<DispatchService>().AsSelf().SingleInstance();
builder.Populate(new ServiceCollection().AddMemoryCache()); builder.Populate(new ServiceCollection().AddMemoryCache());

View File

@ -46,6 +46,7 @@
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="SqlKata" Version="2.3.7" /> <PackageReference Include="SqlKata" Version="2.3.7" />
<PackageReference Include="SqlKata.Execution" Version="2.3.7" /> <PackageReference Include="SqlKata.Execution" Version="2.3.7" />
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
<PackageReference Include="System.Interactive.Async" Version="5.0.0" /> <PackageReference Include="System.Interactive.Async" Version="5.0.0" />
</ItemGroup> </ItemGroup>
@ -54,7 +55,7 @@
</ItemGroup> </ItemGroup>
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation"> <Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
<Exec Command="git rev-parse HEAD > ../.version" IgnoreExitCode="False"> <Exec Command="git rev-parse HEAD &gt; ../.version" IgnoreExitCode="False">
</Exec> </Exec>
</Target> </Target>

View File

@ -0,0 +1,14 @@
using StackExchange.Redis;
namespace PluralKit.Core;
public class RedisService
{
public ConnectionMultiplexer Connection { get; set; }
public async Task InitAsync(CoreConfig config)
{
if (config.RedisAddr != null)
Connection = await ConnectionMultiplexer.ConnectAsync(config.RedisAddr);
}
}

View File

@ -294,6 +294,16 @@
"dapper": "1.50.5" "dapper": "1.50.5"
} }
}, },
"StackExchange.Redis": {
"type": "Direct",
"requested": "[2.2.88, )",
"resolved": "2.2.88",
"contentHash": "JJi1jcO3/ZiamBhlsC/TR8aZmYf+nqpGzMi0HRRCy5wJkUPmMnRp0kBA6V84uhU8b531FHSdTDaFCAyCUJomjA==",
"dependencies": {
"Pipelines.Sockets.Unofficial": "2.2.0",
"System.Diagnostics.PerformanceCounter": "5.0.0"
}
},
"System.Interactive.Async": { "System.Interactive.Async": {
"type": "Direct", "type": "Direct",
"requested": "[5.0.0, )", "requested": "[5.0.0, )",
@ -453,8 +463,8 @@
}, },
"Microsoft.NETCore.Platforms": { "Microsoft.NETCore.Platforms": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.1.0", "resolved": "5.0.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
}, },
"Microsoft.NETCore.Targets": { "Microsoft.NETCore.Targets": {
"type": "Transitive", "type": "Transitive",
@ -471,6 +481,23 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0"
}
},
"NETStandard.Library": { "NETStandard.Library": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.6.1", "resolved": "1.6.1",
@ -522,6 +549,14 @@
"System.Xml.XDocument": "4.3.0" "System.Xml.XDocument": "4.3.0"
} }
}, },
"Pipelines.Sockets.Unofficial": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "7hzHplEIVOGBl5zOQZGX/DiJDHjq+RVRVrYgDiqXb6RriqWAdacXxp+XO9WSrATCEXyNOUOQg9aqQArsjase/A==",
"dependencies": {
"System.IO.Pipelines": "5.0.0"
}
},
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -687,6 +722,15 @@
"System.Threading.Tasks": "4.3.0" "System.Threading.Tasks": "4.3.0"
} }
}, },
"System.Configuration.ConfigurationManager": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==",
"dependencies": {
"System.Security.Cryptography.ProtectedData": "5.0.0",
"System.Security.Permissions": "5.0.0"
}
},
"System.Console": { "System.Console": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -714,6 +758,17 @@
"resolved": "4.7.1", "resolved": "4.7.1",
"contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw==" "contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw=="
}, },
"System.Diagnostics.PerformanceCounter": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"Microsoft.Win32.Registry": "5.0.0",
"System.Configuration.ConfigurationManager": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Diagnostics.Tools": { "System.Diagnostics.Tools": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -734,6 +789,14 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==",
"dependencies": {
"Microsoft.Win32.SystemEvents": "5.0.0"
}
},
"System.Globalization": { "System.Globalization": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -841,6 +904,11 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.IO.Pipelines": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "irMYm3vhVgRsYvHTU5b2gsT2CwT/SMM6LZFzuJjpIvT5Z4CshxNsaoBC1X/LltwuR3Opp8d6jOS/60WwOb7Q2Q=="
},
"System.Linq": { "System.Linq": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1097,6 +1165,15 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Security.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.Cryptography.Algorithms": { "System.Security.Cryptography.Algorithms": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1209,6 +1286,11 @@
"System.Threading.Tasks": "4.3.0" "System.Threading.Tasks": "4.3.0"
} }
}, },
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA=="
},
"System.Security.Cryptography.X509Certificates": { "System.Security.Cryptography.X509Certificates": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1241,6 +1323,20 @@
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
} }
}, },
"System.Security.Permissions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Windows.Extensions": "5.0.0"
}
},
"System.Security.Principal.Windows": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
},
"System.Text.Encoding": { "System.Text.Encoding": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1314,6 +1410,14 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==",
"dependencies": {
"System.Drawing.Common": "5.0.0"
}
},
"System.Xml.ReaderWriter": { "System.Xml.ReaderWriter": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",

View File

@ -10,6 +10,7 @@ services:
command: ["bin/PluralKit.Bot.dll"] command: ["bin/PluralKit.Bot.dll"]
environment: environment:
- "PluralKit:Database=Host=db;Username=postgres;Password=postgres;Database=postgres;Maximum Pool Size=1000" - "PluralKit:Database=Host=db;Username=postgres;Password=postgres;Database=postgres;Maximum Pool Size=1000"
- "PluralKit:RedisAddr=redis"
- "PluralKit:InfluxUrl=http://influx:8086" - "PluralKit:InfluxUrl=http://influx:8086"
- "PluralKit:InfluxDb=pluralkit" - "PluralKit:InfluxDb=pluralkit"
- "PluralKit:LogDir=/var/log/pluralkit" - "PluralKit:LogDir=/var/log/pluralkit"
@ -49,6 +50,10 @@ services:
- "POSTGRES_PASSWORD=postgres" - "POSTGRES_PASSWORD=postgres"
restart: unless-stopped restart: unless-stopped
redis:
image: redis:alpine
restart: unless-stopped
influx: influx:
image: influxdb:1.8 image: influxdb:1.8
volumes: volumes: