Skip to content

Commit 92fe712

Browse files
committed
feat: Hopefully this won't break anything.. :D Metric storage of projects for downloads, thumbs up and popularity rank
1 parent 9591b15 commit 92fe712

File tree

5 files changed

+87
-1
lines changed

5 files changed

+87
-1
lines changed

CFLookup/CFLookup.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.22" />
1515
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
1616
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.12.0" />
17+
<PackageReference Include="InfluxDB.Client" Version="4.18.0" />
1718
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.11" />
1819
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.5" />
1920
<PackageReference Include="Highsoft.Highcharts" Version="11.4.6.5" />

CFLookup/InfluxDBWriter.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using CFLookup.Models;
2+
using InfluxDB.Client;
3+
using InfluxDB.Client.Api.Domain;
4+
using InfluxDB.Client.Writes;
5+
6+
namespace CFLookup
7+
{
8+
public class InfluxDBWriter(InfluxDBClient influxClient)
9+
{
10+
public async Task WriteBatchAsync(string org, string bucket, IEnumerable<InfluxProjectMetric> metrics)
11+
{
12+
var writeApi = influxClient.GetWriteApiAsync();
13+
14+
await writeApi.WritePointsAsync(
15+
metrics.Select(m =>
16+
PointData.Measurement("cf_project_metrics")
17+
.Tag("project_id", m.ProjectId.ToString())
18+
.Tag("game_id", m.GameId.ToString())
19+
.Field("download_count", m.DownloadCount)
20+
.Field("thumbs_up_count", m.ThumbsUpCount)
21+
.Field("game_popularity_rank", m.GamePopularityRank)
22+
.Timestamp(m.Timestamp, WritePrecision.Ns)
23+
).ToList(),
24+
bucket,
25+
org,
26+
CancellationToken.None
27+
);
28+
}
29+
}
30+
}

CFLookup/Jobs/StoreCFApiProjects.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using CurseForge.APIClient;
1+
using CFLookup.Models;
2+
using CurseForge.APIClient;
23
using CurseForge.APIClient.Models.Mods;
34
using Hangfire;
45
using Hangfire.Server;
@@ -29,6 +30,8 @@ public async static Task RunAsync(PerformContext context, IJobCancellationToken
2930
token.ThrowIfCancellationRequested();
3031

3132
var cfClient = scope.ServiceProvider.GetRequiredService<ApiClient>();
33+
34+
var influxWriter = scope.ServiceProvider.GetRequiredService<InfluxDBWriter>();
3235

3336
var conn = scope.ServiceProvider.GetRequiredService<NpgsqlConnection>();
3437
await conn.OpenAsync();
@@ -77,6 +80,10 @@ public async static Task RunAsync(PerformContext context, IJobCancellationToken
7780

7881
token.ThrowIfCancellationRequested();
7982

83+
var projectMetrics = new List<InfluxProjectMetric>();
84+
85+
var currentTs = DateTime.UtcNow;
86+
8087
foreach (var mod in modList.Data)
8188
{
8289
token.ThrowIfCancellationRequested();
@@ -237,13 +244,27 @@ ON CONFLICT (projectid, gameid) DO UPDATE
237244
});
238245

239246
batch.BatchCommands.Add(cmd);
247+
248+
projectMetrics.Add(new InfluxProjectMetric
249+
{
250+
ProjectId = mod.Id,
251+
GameId = mod.GameId,
252+
DownloadCount = mod.DownloadCount,
253+
ThumbsUpCount = mod.ThumbsUpCount,
254+
GamePopularityRank = mod.GamePopularityRank,
255+
Timestamp = currentTs
256+
});
240257

241258
if (batch.BatchCommands.Count >= 1000)
242259
{
243260
if (!await ExecuteBatchWithRetries(batch))
244261
{
245262
// No-op for now, maybe Discord logs later
246263
}
264+
265+
await influxWriter.WriteBatchAsync("CFLookup", "cf_project_data", projectMetrics);
266+
projectMetrics.Clear();
267+
currentTs = DateTime.UtcNow;
247268
token.ThrowIfCancellationRequested();
248269
}
249270
}
@@ -254,6 +275,10 @@ ON CONFLICT (projectid, gameid) DO UPDATE
254275
{
255276
// No-op for now, maybe Discord logs later
256277
}
278+
279+
await influxWriter.WriteBatchAsync("CFLookup", "cf_project_data", projectMetrics);
280+
projectMetrics.Clear();
281+
257282
token.ThrowIfCancellationRequested();
258283
}
259284

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace CFLookup.Models
2+
{
3+
public class InfluxProjectMetric
4+
{
5+
public required long ProjectId { get; set; }
6+
public required int GameId { get; set; }
7+
public required double DownloadCount { get; set; }
8+
public required int ThumbsUpCount { get; set; }
9+
public required int GamePopularityRank { get; set; }
10+
public DateTime Timestamp { get; set; }
11+
}
12+
}

CFLookup/Program.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CFLookup;
2+
using InfluxDB.Client;
23
using Microsoft.AspNetCore.DataProtection;
34
#if !DEBUG
45
using CFLookup.Jobs;
@@ -60,6 +61,8 @@ private static async Task Main(string[] args)
6061
var hangfirePassword = string.Empty;
6162

6263
var pgsqlConnString = string.Empty;
64+
var influxDBConnString = string.Empty;
65+
var influxToken = string.Empty;
6366

6467
if (OperatingSystem.IsWindows())
6568
{
@@ -95,6 +98,16 @@ private static async Task Main(string[] args)
9598
Environment.GetEnvironmentVariable("CFLOOKUP_PGSQL", EnvironmentVariableTarget.User) ??
9699
Environment.GetEnvironmentVariable("CFLOOKUP_PGSQL", EnvironmentVariableTarget.Process) ??
97100
string.Empty;
101+
102+
influxDBConnString = Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB", EnvironmentVariableTarget.Machine) ??
103+
Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB", EnvironmentVariableTarget.User) ??
104+
Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB", EnvironmentVariableTarget.Process) ??
105+
string.Empty;
106+
107+
influxToken = Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB_TOKEN", EnvironmentVariableTarget.Machine) ??
108+
Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB_TOKEN", EnvironmentVariableTarget.User) ??
109+
Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB_TOKEN", EnvironmentVariableTarget.Process) ??
110+
string.Empty;
98111
}
99112
else
100113
{
@@ -104,6 +117,8 @@ private static async Task Main(string[] args)
104117
hangfireUser = Environment.GetEnvironmentVariable("CFLOOKUP_HangfireUser") ?? string.Empty;
105118
hangfirePassword = Environment.GetEnvironmentVariable("CFLOOKUP_HangfirePassword") ?? string.Empty;
106119
pgsqlConnString = Environment.GetEnvironmentVariable("CFLOOKUP_PGSQL") ?? string.Empty;
120+
influxDBConnString = Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB") ?? string.Empty;
121+
influxToken = Environment.GetEnvironmentVariable("CFLOOKUP_INFLUXDB_TOKEN") ?? string.Empty;
107122
}
108123

109124
var redis = ConnectionMultiplexer.Connect(redisServer);
@@ -112,6 +127,9 @@ private static async Task Main(string[] args)
112127

113128
builder.Services.AddDataProtection()
114129
.PersistKeysToStackExchangeRedis(redis, "CFLookup-DataProtection-Keys");
130+
131+
builder.Services.AddScoped(x => new InfluxDBClient(influxDBConnString, influxToken));
132+
builder.Services.AddScoped<InfluxDBWriter>();
115133

116134
builder.Services.AddScoped(x => new SqlConnection(dbConnectionString));
117135

0 commit comments

Comments
 (0)