Skip to content

Commit

Permalink
cache only the first datasource instead of all
Browse files Browse the repository at this point in the history
  • Loading branch information
Dalet committed Oct 20, 2023
1 parent 6cc9feb commit 3f8cfde
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 29 deletions.
72 changes: 43 additions & 29 deletions LeaderboardBackend/Models/Entities/ApplicationContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
Expand All @@ -10,15 +9,12 @@ public class ApplicationContext : DbContext
{
public const string CASE_INSENSITIVE_COLLATION = "case_insensitive";

// the HashCode is calculated with all of the config's property because PostgresConfig is a record
private static readonly ConcurrentDictionary<PostgresConfig, NpgsqlDataSource> _dataSourceCache = new();
private readonly AppContextDataSourceProvider _dataSourceProvider;

private readonly NpgsqlDataSource _dataSource;

public ApplicationContext(DbContextOptions<ApplicationContext> options, IOptions<ApplicationContextConfig> config)
public ApplicationContext(DbContextOptions<ApplicationContext> options, AppContextDataSourceProvider dataSourceProvider)
: base(options)
{
_dataSource = CreateDataSource(config.Value.Pg);
_dataSourceProvider = dataSourceProvider;
}

public DbSet<AccountRecovery> AccountRecoveries { get; set; } = null!;
Expand All @@ -28,6 +24,28 @@ public ApplicationContext(DbContextOptions<ApplicationContext> options, IOptions
public DbSet<Run> Runs { get; set; } = null!;
public DbSet<User> Users { get; set; } = null!;

public static NpgsqlDataSource CreateDataSource(PostgresConfig config)
{
NpgsqlConnectionStringBuilder connectionBuilder = new()
{
Host = config.Host,
Username = config.User,
Password = config.Password,
Database = config.Db,
IncludeErrorDetail = true,
};

if (config.Port is not null)
{
connectionBuilder.Port = config.Port.Value;
}

NpgsqlDataSourceBuilder dataSourceBuilder = new(connectionBuilder.ConnectionString);
dataSourceBuilder.UseNodaTime().MapEnum<UserRole>();

return dataSourceBuilder.Build();
}

public void MigrateDatabase()
{
Database.Migrate();
Expand Down Expand Up @@ -75,32 +93,28 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

protected override void OnConfiguring(DbContextOptionsBuilder opt)
{
opt.UseNpgsql(_dataSource, o => o.UseNodaTime());
opt.UseNpgsql(_dataSourceProvider.Value, o => o.UseNodaTime());
opt.UseSnakeCaseNamingConvention();
}
}

public class AppContextDataSourceProvider
{
private static int _cacheKey;
private static NpgsqlDataSource? _cachedDataSource;

private static NpgsqlDataSource CreateDataSource(PostgresConfig c)
public NpgsqlDataSource Value => _cachedDataSource!;

public AppContextDataSourceProvider(IOptions<ApplicationContextConfig> appContextConfig)
{
return _dataSourceCache.GetOrAdd(c, config =>
PostgresConfig config = appContextConfig.Value.Pg;

int key = config.GetHashCode(); // a record's HashCode is calculated with all its properties values
if (_cacheKey != key)
{
NpgsqlConnectionStringBuilder connectionBuilder = new()
{
Host = config.Host,
Username = config.User,
Password = config.Password,
Database = config.Db,
IncludeErrorDetail = true,
};
if (config.Port is not null)
{
connectionBuilder.Port = config.Port.Value;
}
NpgsqlDataSourceBuilder dataSourceBuilder = new(connectionBuilder.ConnectionString);
dataSourceBuilder.UseNodaTime().MapEnum<UserRole>();
return dataSourceBuilder.Build();
});
// if we ever want to parallelize tests, this code will need to be made thread-safe
_cacheKey = key;
_cachedDataSource = ApplicationContext.CreateDataSource(config);
}
}
}
1 change: 1 addition & 0 deletions LeaderboardBackend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
.ValidateDataAnnotationsRecursively()
.ValidateOnStart();

builder.Services.AddSingleton<AppContextDataSourceProvider>();
builder.Services.AddDbContext<ApplicationContext>();

// Add services to the container.
Expand Down

0 comments on commit 3f8cfde

Please sign in to comment.