Limit postgres database concurrency (#17246)
This commit is contained in:
committed by
GitHub
parent
f055faeebd
commit
50257c3bd7
@@ -283,11 +283,11 @@ namespace Content.Server.Database
|
|||||||
{
|
{
|
||||||
case "sqlite":
|
case "sqlite":
|
||||||
SetupSqlite(out var contextFunc, out var inMemory);
|
SetupSqlite(out var contextFunc, out var inMemory);
|
||||||
_db = new ServerDbSqlite(contextFunc, inMemory);
|
_db = new ServerDbSqlite(contextFunc, inMemory, _cfg);
|
||||||
break;
|
break;
|
||||||
case "postgres":
|
case "postgres":
|
||||||
var pgOptions = CreatePostgresOptions();
|
var pgOptions = CreatePostgresOptions();
|
||||||
_db = new ServerDbPostgres(pgOptions);
|
_db = new ServerDbPostgres(pgOptions, _cfg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataException($"Unknown database engine {engine}.");
|
throw new InvalidDataException($"Unknown database engine {engine}.");
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -13,11 +15,15 @@ namespace Content.Server.Database
|
|||||||
public sealed class ServerDbPostgres : ServerDbBase
|
public sealed class ServerDbPostgres : ServerDbBase
|
||||||
{
|
{
|
||||||
private readonly DbContextOptions<PostgresServerDbContext> _options;
|
private readonly DbContextOptions<PostgresServerDbContext> _options;
|
||||||
|
private readonly SemaphoreSlim _prefsSemaphore;
|
||||||
private readonly Task _dbReadyTask;
|
private readonly Task _dbReadyTask;
|
||||||
|
|
||||||
public ServerDbPostgres(DbContextOptions<PostgresServerDbContext> options)
|
public ServerDbPostgres(DbContextOptions<PostgresServerDbContext> options, IConfigurationManager cfg)
|
||||||
{
|
{
|
||||||
|
var concurrency = cfg.GetCVar(CCVars.DatabasePgConcurrency);
|
||||||
|
|
||||||
_options = options;
|
_options = options;
|
||||||
|
_prefsSemaphore = new SemaphoreSlim(concurrency, concurrency);
|
||||||
|
|
||||||
_dbReadyTask = Task.Run(async () =>
|
_dbReadyTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -485,8 +491,9 @@ namespace Content.Server.Database
|
|||||||
private async Task<DbGuardImpl> GetDbImpl()
|
private async Task<DbGuardImpl> GetDbImpl()
|
||||||
{
|
{
|
||||||
await _dbReadyTask;
|
await _dbReadyTask;
|
||||||
|
await _prefsSemaphore.WaitAsync();
|
||||||
|
|
||||||
return new DbGuardImpl(new PostgresServerDbContext(_options));
|
return new DbGuardImpl(this, new PostgresServerDbContext(_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<DbGuard> GetDb()
|
protected override async Task<DbGuard> GetDb()
|
||||||
@@ -496,17 +503,21 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
private sealed class DbGuardImpl : DbGuard
|
private sealed class DbGuardImpl : DbGuard
|
||||||
{
|
{
|
||||||
public DbGuardImpl(PostgresServerDbContext dbC)
|
private readonly ServerDbPostgres _db;
|
||||||
|
|
||||||
|
public DbGuardImpl(ServerDbPostgres db, PostgresServerDbContext dbC)
|
||||||
{
|
{
|
||||||
|
_db = db;
|
||||||
PgDbContext = dbC;
|
PgDbContext = dbC;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PostgresServerDbContext PgDbContext { get; }
|
public PostgresServerDbContext PgDbContext { get; }
|
||||||
public override ServerDbContext DbContext => PgDbContext;
|
public override ServerDbContext DbContext => PgDbContext;
|
||||||
|
|
||||||
public override ValueTask DisposeAsync()
|
public override async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
return DbContext.DisposeAsync();
|
await DbContext.DisposeAsync();
|
||||||
|
_db._prefsSemaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,22 +20,21 @@ namespace Content.Server.Database
|
|||||||
{
|
{
|
||||||
private readonly Func<DbContextOptions<SqliteServerDbContext>> _options;
|
private readonly Func<DbContextOptions<SqliteServerDbContext>> _options;
|
||||||
|
|
||||||
// This doesn't allow concurrent access so that's what the semaphore is for.
|
|
||||||
// That said, this is bloody SQLite, I don't even think EFCore bothers to truly async it.
|
|
||||||
private readonly SemaphoreSlim _prefsSemaphore;
|
private readonly SemaphoreSlim _prefsSemaphore;
|
||||||
|
|
||||||
private readonly Task _dbReadyTask;
|
private readonly Task _dbReadyTask;
|
||||||
|
|
||||||
private int _msDelay;
|
private int _msDelay;
|
||||||
|
|
||||||
public ServerDbSqlite(Func<DbContextOptions<SqliteServerDbContext>> options, bool inMemory)
|
public ServerDbSqlite(
|
||||||
|
Func<DbContextOptions<SqliteServerDbContext>> options,
|
||||||
|
bool inMemory,
|
||||||
|
IConfigurationManager cfg)
|
||||||
{
|
{
|
||||||
_options = options;
|
_options = options;
|
||||||
|
|
||||||
var prefsCtx = new SqliteServerDbContext(options());
|
var prefsCtx = new SqliteServerDbContext(options());
|
||||||
|
|
||||||
var cfg = IoCManager.Resolve<IConfigurationManager>();
|
|
||||||
|
|
||||||
// When inMemory we re-use the same connection, so we can't have any concurrency.
|
// When inMemory we re-use the same connection, so we can't have any concurrency.
|
||||||
var concurrency = inMemory ? 1 : cfg.GetCVar(CCVars.DatabaseSqliteConcurrency);
|
var concurrency = inMemory ? 1 : cfg.GetCVar(CCVars.DatabaseSqliteConcurrency);
|
||||||
_prefsSemaphore = new SemaphoreSlim(concurrency, concurrency);
|
_prefsSemaphore = new SemaphoreSlim(concurrency, concurrency);
|
||||||
|
|||||||
@@ -479,11 +479,16 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<string> DatabasePgPassword =
|
public static readonly CVarDef<string> DatabasePgPassword =
|
||||||
CVarDef.Create("database.pg_password", "", CVar.SERVERONLY | CVar.CONFIDENTIAL);
|
CVarDef.Create("database.pg_password", "", CVar.SERVERONLY | CVar.CONFIDENTIAL);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max amount of concurrent Postgres database operations.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<int> DatabasePgConcurrency =
|
||||||
|
CVarDef.Create("database.pg_concurrency", 8, CVar.SERVERONLY);
|
||||||
|
|
||||||
// Basically only exists for integration tests to avoid race conditions.
|
// Basically only exists for integration tests to avoid race conditions.
|
||||||
public static readonly CVarDef<bool> DatabaseSynchronous =
|
public static readonly CVarDef<bool> DatabaseSynchronous =
|
||||||
CVarDef.Create("database.sync", false, CVar.SERVERONLY);
|
CVarDef.Create("database.sync", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Outline
|
* Outline
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Content.Shared.Preferences;
|
|||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
@@ -74,7 +75,7 @@ namespace Content.Tests.Server.Preferences
|
|||||||
var conn = new SqliteConnection("Data Source=:memory:");
|
var conn = new SqliteConnection("Data Source=:memory:");
|
||||||
conn.Open();
|
conn.Open();
|
||||||
builder.UseSqlite(conn);
|
builder.UseSqlite(conn);
|
||||||
return new ServerDbSqlite(() => builder.Options, true);
|
return new ServerDbSqlite(() => builder.Options, true, IoCManager.Resolve<IConfigurationManager>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
Reference in New Issue
Block a user