Skip to content

Commit

Permalink
Drop EFCore in prod since we're not using it for anything more than m…
Browse files Browse the repository at this point in the history
…igrations
  • Loading branch information
OoLunar committed May 4, 2023
1 parent f45eb31 commit bfd6e03
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 55 deletions.
75 changes: 36 additions & 39 deletions src/CookieTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Npgsql;
Expand All @@ -18,52 +17,50 @@ namespace OoLunar.CookieClicker
{
public sealed class CookieTracker : IAsyncDisposable
{
private readonly Dictionary<Ulid, CachedCookie> UnbakedCookies = new();
private readonly CookieDatabaseContext DatabaseContext;
private readonly Dictionary<Ulid, CachedCookie> _unbakedCookies = new();
private readonly NpgsqlConnection _databaseConnection;
private readonly ILogger<CookieTracker> _logger;
private readonly SemaphoreSlim Semaphore = new(1, 1);
private readonly PeriodicTimer Timer;
private readonly Task BakingTask;
private readonly FrozenDictionary<DatabaseOperation, NpgsqlCommand> DatabaseCommands;
private readonly SemaphoreSlim _semaphore = new(1, 1);
private readonly PeriodicTimer _timer;
private readonly Task _bakingTask;
private readonly FrozenDictionary<DatabaseOperation, NpgsqlCommand> _databaseCommands;

public CookieTracker(CookieDatabaseContext databaseContext, IConfiguration configuration, ILogger<CookieTracker> logger)
public CookieTracker(IConfiguration configuration, ILogger<CookieTracker> logger)
{
ArgumentNullException.ThrowIfNull(databaseContext, nameof(databaseContext));
ArgumentNullException.ThrowIfNull(configuration, nameof(configuration));
ArgumentNullException.ThrowIfNull(logger, nameof(logger));

_logger = logger;
Timer = new PeriodicTimer(TimeSpan.FromSeconds(configuration.GetValue("CookieTracker:Period", 30)));
_timer = new PeriodicTimer(TimeSpan.FromSeconds(configuration.GetValue("CookieTracker:Period", 30)));

DatabaseContext = databaseContext;
NpgsqlConnection connection = (NpgsqlConnection)DatabaseContext.Database.GetDbConnection();
connection.Open();
DatabaseCommands = new Dictionary<DatabaseOperation, NpgsqlCommand>
_databaseConnection = NpgsqlDataSource.Create(CookieDatabaseContext.GetConnectionString(configuration)).CreateConnection();
_databaseConnection.Open();
_databaseCommands = new Dictionary<DatabaseOperation, NpgsqlCommand>
{
[DatabaseOperation.Create] = GetInsertCommand(connection),
[DatabaseOperation.Read] = GetSelectCommand(connection),
[DatabaseOperation.Update] = GetUpdateCommand(connection),
[DatabaseOperation.Delete] = GetDeleteCommand(connection)
[DatabaseOperation.Create] = GetInsertCommand(_databaseConnection),
[DatabaseOperation.Read] = GetSelectCommand(_databaseConnection),
[DatabaseOperation.Update] = GetUpdateCommand(_databaseConnection),
[DatabaseOperation.Delete] = GetDeleteCommand(_databaseConnection)
}.ToFrozenDictionary();

BakingTask = StartBakingAsync();
_bakingTask = StartBakingAsync();
}

public void CreateCookie(Cookie cookie)
{
ArgumentNullException.ThrowIfNull(cookie, nameof(cookie));
UnbakedCookies.Add(cookie.Id, new(cookie, false));
_unbakedCookies.Add(cookie.Id, new(cookie, false));
}

public ulong Click(Ulid cookieId)
{
// Check if the cookie is in the cache. If it isn't, pull it from the database.
if (!UnbakedCookies.TryGetValue(cookieId, out CachedCookie? unbakedCookie))
if (!_unbakedCookies.TryGetValue(cookieId, out CachedCookie? unbakedCookie))
{
Semaphore.Wait();
_semaphore.Wait();
try
{
DbCommand command = DatabaseCommands[DatabaseOperation.Read];
DbCommand command = _databaseCommands[DatabaseOperation.Read];
command.Parameters[0].Value = cookieId.ToGuid();
using DbDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow);
if (!reader.Read())
Expand All @@ -76,11 +73,11 @@ public ulong Click(Ulid cookieId)
Id = new Ulid(reader.GetFieldValue<Guid>(0)),
Clicks = (ulong)reader.GetFieldValue<decimal>(1)
}, true);
UnbakedCookies.Add(cookieId, unbakedCookie);
_unbakedCookies.Add(cookieId, unbakedCookie);
}
finally
{
Semaphore.Release();
_semaphore.Release();
}
}

Expand All @@ -90,18 +87,18 @@ public ulong Click(Ulid cookieId)

private async Task StartBakingAsync()
{
DbCommand createCommand = DatabaseCommands[DatabaseOperation.Create];
DbCommand updateCommand = DatabaseCommands[DatabaseOperation.Update];
foreach (DbCommand command in DatabaseCommands.Values)
DbCommand createCommand = _databaseCommands[DatabaseOperation.Create];
DbCommand updateCommand = _databaseCommands[DatabaseOperation.Update];
foreach (DbCommand command in _databaseCommands.Values)
{
await command.PrepareAsync();
}

while (await Timer.WaitForNextTickAsync())
while (await _timer.WaitForNextTickAsync())
{
await Semaphore.WaitAsync();
CachedCookie[] unbakedCookies = UnbakedCookies.Values.ToArray();
Semaphore.Release();
await _semaphore.WaitAsync();
CachedCookie[] unbakedCookies = _unbakedCookies.Values.ToArray();
_semaphore.Release();

List<Guid> updatedCookieIds = new();
List<decimal> updatedCookieCount = new();
Expand All @@ -120,7 +117,7 @@ private async Task StartBakingAsync()
}

// Remove the cookie from the unbaked cookies dictionary. Prefer the returned result over the current result.
if (!UnbakedCookies.Remove(cookie.Cookie.Id, out CachedCookie? unbakedCookie))
if (!_unbakedCookies.Remove(cookie.Cookie.Id, out CachedCookie? unbakedCookie))
{
unbakedCookie = cookie;
}
Expand All @@ -143,7 +140,7 @@ private async Task StartBakingAsync()
continue;
}

await Semaphore.WaitAsync();
await _semaphore.WaitAsync();
try
{
if (newCookieIds.Count != 0)
Expand All @@ -162,17 +159,17 @@ private async Task StartBakingAsync()
}
finally
{
Semaphore.Release();
_semaphore.Release();
}
}
}

public async ValueTask DisposeAsync()
{
Timer.Dispose();
await BakingTask;
await DatabaseContext.DisposeAsync();
Semaphore.Dispose();
_timer.Dispose();
await _bakingTask;
await _databaseConnection.DisposeAsync();
_semaphore.Dispose();
}

private static NpgsqlCommand GetSelectCommand(NpgsqlConnection connection)
Expand Down
25 changes: 11 additions & 14 deletions src/Database/CookieDatabaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) => modelBuild
.ValueGeneratedOnAdd();
});

internal static void ConfigureOptions(DbContextOptionsBuilder optionsBuilder, IConfiguration configuration)
internal static NpgsqlConnectionStringBuilder GetConnectionString(IConfiguration configuration) => new()
{
NpgsqlConnectionStringBuilder connectionBuilder = new()
{
ApplicationName = configuration.GetValue("Database:ApplicationName", "Cookie Clicker"),
Database = configuration.GetValue("Database:DatabaseName", "cookie_clicker"),
Host = configuration.GetValue("Database:Host", "localhost"),
Username = configuration.GetValue("Database:Username", "cookie_clicker"),
Port = configuration.GetValue("Database:Port", 5432),
Password = configuration.GetValue<string>("Database:Password")
};
ApplicationName = configuration.GetValue("Database:ApplicationName", "Cookie Clicker"),
Database = configuration.GetValue("Database:DatabaseName", "cookie_clicker"),
Host = configuration.GetValue("Database:Host", "localhost"),
Username = configuration.GetValue("Database:Username", "cookie_clicker"),
Port = configuration.GetValue("Database:Port", 5432),
Password = configuration.GetValue<string>("Database:Password")
};

optionsBuilder.UseNpgsql(connectionBuilder.ToString(), options => options.EnableRetryOnFailure(5).CommandTimeout(5))
.UseSnakeCaseNamingConvention()
.EnableThreadSafetyChecks(false);
}
internal static void ConfigureOptions(DbContextOptionsBuilder optionsBuilder, IConfiguration configuration) => optionsBuilder.UseNpgsql(GetConnectionString(configuration).ToString(), options => options.EnableRetryOnFailure(5).CommandTimeout(5))
.UseSnakeCaseNamingConvention()
.EnableThreadSafetyChecks(false);
}
}
3 changes: 1 addition & 2 deletions src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OoLunar.CookieClicker.Database;
using OoLunar.CookieClicker.GenHttp;
using OoLunar.CookieClicker.Headers;
using OoLunar.CookieClicker.Routes;
Expand All @@ -30,14 +29,14 @@ public static async Task<int> Main(string[] args)
{
ServiceCollection serviceCollection = new();
ConfigurationBuilder configurationBuilder = new();
configurationBuilder.Sources.Clear();
configurationBuilder.AddJsonFile("config.json", true, true);
configurationBuilder.AddEnvironmentVariables("CookieClicker_");
configurationBuilder.AddCommandLine(args);

IConfiguration configuration = configurationBuilder.Build();
serviceCollection.AddSingleton(configuration);
serviceCollection.AddLogging(loggingBuilder => HttpLogger.ConfigureLogging(loggingBuilder, configuration));
serviceCollection.AddDbContext<CookieDatabaseContext>((services, options) => CookieDatabaseContext.ConfigureOptions(options, services.GetRequiredService<IConfiguration>()), ServiceLifetime.Scoped);
serviceCollection.AddSingleton<CookieTracker>();
serviceCollection.AddSingleton<DiscordHeaderAuthentication>();
serviceCollection.AddSingleton<DiscordSlashCommandHandler>();
Expand Down

0 comments on commit bfd6e03

Please sign in to comment.