Introduce artificial delay into SQLite on DEBUG.

This makes SQLite DB ops properly asynchronous (instead of synchronously completing tasks). This makes them more consistent with postgres and means that any deadlock bugs introduced the next time somebody does .Result will be caught on SQLite too.
This commit is contained in:
Pieter-Jan Briers
2022-03-04 23:55:35 +01:00
parent 2c721226d6
commit 950ead9b47
2 changed files with 28 additions and 2 deletions

View File

@@ -31,11 +31,14 @@ namespace Content.Server.Database
private readonly Task _dbReadyTask; private readonly Task _dbReadyTask;
private readonly SqliteServerDbContext _prefsCtx; private readonly SqliteServerDbContext _prefsCtx;
private int _msDelay;
public ServerDbSqlite(DbContextOptions<SqliteServerDbContext> options) public ServerDbSqlite(DbContextOptions<SqliteServerDbContext> options)
{ {
_prefsCtx = new SqliteServerDbContext(options); _prefsCtx = new SqliteServerDbContext(options);
if (IoCManager.Resolve<IConfigurationManager>().GetCVar(CCVars.DatabaseSynchronous)) var cfg = IoCManager.Resolve<IConfigurationManager>();
if (cfg.GetCVar(CCVars.DatabaseSynchronous))
{ {
_prefsCtx.Database.Migrate(); _prefsCtx.Database.Migrate();
_dbReadyTask = Task.CompletedTask; _dbReadyTask = Task.CompletedTask;
@@ -44,6 +47,8 @@ namespace Content.Server.Database
{ {
_dbReadyTask = Task.Run(() => _prefsCtx.Database.Migrate()); _dbReadyTask = Task.Run(() => _prefsCtx.Database.Migrate());
} }
cfg.OnValueChanged(CCVars.DatabaseSqliteDelay, v => _msDelay = v, true);
} }
#region Ban #region Ban
@@ -488,6 +493,9 @@ namespace Content.Server.Database
private async Task<DbGuardImpl> GetDbImpl() private async Task<DbGuardImpl> GetDbImpl()
{ {
await _dbReadyTask; await _dbReadyTask;
if (_msDelay > 0)
await Task.Delay(_msDelay);
await _prefsSemaphore.WaitAsync(); await _prefsSemaphore.WaitAsync();
return new DbGuardImpl(this); return new DbGuardImpl(this);

View File

@@ -242,6 +242,24 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<string> DatabaseSqliteDbPath = public static readonly CVarDef<string> DatabaseSqliteDbPath =
CVarDef.Create("database.sqlite_dbpath", "preferences.db", CVar.SERVERONLY); CVarDef.Create("database.sqlite_dbpath", "preferences.db", CVar.SERVERONLY);
/// <summary>
/// Milliseconds to asynchronously delay all SQLite database acquisitions with.
/// </summary>
/// <remarks>
/// Defaults to 1 on DEBUG, 0 on RELEASE.
/// This is intended to help catch .Result deadlock bugs that only happen on postgres
/// (because SQLite is not actually asynchronous normally)
/// </remarks>
public static readonly CVarDef<int> DatabaseSqliteDelay =
CVarDef.Create("database.sqlite_delay", DefaultSqliteDelay, CVar.SERVERONLY);
#if DEBUG
private const int DefaultSqliteDelay = 1;
#else
private const int DefaultSqliteDelay = 0;
#endif
public static readonly CVarDef<string> DatabasePgHost = public static readonly CVarDef<string> DatabasePgHost =
CVarDef.Create("database.pg_host", "localhost", CVar.SERVERONLY); CVarDef.Create("database.pg_host", "localhost", CVar.SERVERONLY);