(Probably) fix random integration test failures. (#18270)

This commit is contained in:
Pieter-Jan Briers
2023-07-25 03:10:50 +02:00
committed by GitHub
parent 5fa1849948
commit 978887bf03
5 changed files with 164 additions and 36 deletions

View File

@@ -20,16 +20,15 @@ namespace Content.Server.Database
{
private readonly Func<DbContextOptions<SqliteServerDbContext>> _options;
private readonly SemaphoreSlim _prefsSemaphore;
private readonly ConcurrencySemaphore _prefsSemaphore;
private readonly Task _dbReadyTask;
private int _msDelay;
public ServerDbSqlite(
Func<DbContextOptions<SqliteServerDbContext>> options,
public ServerDbSqlite(Func<DbContextOptions<SqliteServerDbContext>> options,
bool inMemory,
IConfigurationManager cfg)
IConfigurationManager cfg, bool synchronous)
{
_options = options;
@@ -37,7 +36,7 @@ namespace Content.Server.Database
// When inMemory we re-use the same connection, so we can't have any concurrency.
var concurrency = inMemory ? 1 : cfg.GetCVar(CCVars.DatabaseSqliteConcurrency);
_prefsSemaphore = new SemaphoreSlim(concurrency, concurrency);
_prefsSemaphore = new ConcurrencySemaphore(concurrency, synchronous);
if (cfg.GetCVar(CCVars.DatabaseSynchronous))
{
@@ -564,5 +563,68 @@ namespace Content.Server.Database
_db._prefsSemaphore.Release();
}
}
private sealed class ConcurrencySemaphore
{
private readonly bool _synchronous;
private readonly SemaphoreSlim _semaphore;
private Thread? _holdingThread;
public ConcurrencySemaphore(int maxCount, bool synchronous)
{
if (synchronous && maxCount != 1)
throw new ArgumentException("If synchronous, max concurrency must be 1");
_synchronous = synchronous;
_semaphore = new SemaphoreSlim(maxCount, maxCount);
}
public Task WaitAsync()
{
var task = _semaphore.WaitAsync();
if (_synchronous)
{
if (!task.IsCompleted)
{
if (Thread.CurrentThread == _holdingThread)
{
throw new InvalidOperationException(
"Multiple database requests from same thread on synchronous database!");
}
throw new InvalidOperationException(
$"Different threads trying to access the database at once! " +
$"Holding thread: {DiagThread(_holdingThread)}, " +
$"current thread: {DiagThread(Thread.CurrentThread)}");
}
_holdingThread = Thread.CurrentThread;
}
return task;
}
public void Release()
{
if (_synchronous)
{
if (Thread.CurrentThread != _holdingThread)
throw new InvalidOperationException("Released on different thread than took lock???");
_holdingThread = null;
}
_semaphore.Release();
}
private static string DiagThread(Thread? thread)
{
if (thread != null)
return $"{thread.Name} ({thread.ManagedThreadId})";
return "<null thread>";
}
}
}
}