1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 20:13:20 +08:00

Fix multiple issues causing database reset to fail

This commit is contained in:
Dean Herbert 2018-06-04 02:07:02 +09:00
parent 68910745d8
commit 3a823d6c25
3 changed files with 44 additions and 18 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
@ -46,7 +47,10 @@ namespace osu.Game.Database
public DatabaseWriteUsage GetForWrite(bool withTransaction = true) public DatabaseWriteUsage GetForWrite(bool withTransaction = true)
{ {
Monitor.Enter(writeLock); Monitor.Enter(writeLock);
OsuDbContext context;
try
{
if (currentWriteTransaction == null && withTransaction) if (currentWriteTransaction == null && withTransaction)
{ {
// this mitigates the fact that changes on tracked entities will not be rolled back with the transaction by ensuring write operations are always executed in isolated contexts. // this mitigates the fact that changes on tracked entities will not be rolled back with the transaction by ensuring write operations are always executed in isolated contexts.
@ -54,12 +58,24 @@ namespace osu.Game.Database
if (threadContexts.IsValueCreated) if (threadContexts.IsValueCreated)
recycleThreadContexts(); recycleThreadContexts();
currentWriteTransaction = threadContexts.Value.Database.BeginTransaction(); context = threadContexts.Value;
currentWriteTransaction = context.Database.BeginTransaction();
}
else
{
context = threadContexts.Value;
}
}
catch (Exception e)
{
// retrieval of a context could trigger a fatal error.
Monitor.Exit(writeLock);
throw;
} }
Interlocked.Increment(ref currentWriteUsages); Interlocked.Increment(ref currentWriteUsages);
return new DatabaseWriteUsage(threadContexts.Value, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 }; return new DatabaseWriteUsage(context, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 };
} }
private void usageCompleted(DatabaseWriteUsage usage) private void usageCompleted(DatabaseWriteUsage usage)
@ -100,19 +116,18 @@ namespace osu.Game.Database
private void recycleThreadContexts() => threadContexts = new ThreadLocal<OsuDbContext>(CreateContext); private void recycleThreadContexts() => threadContexts = new ThreadLocal<OsuDbContext>(CreateContext);
protected virtual OsuDbContext CreateContext() protected virtual OsuDbContext CreateContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name))
{ {
var ctx = new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name)); Database = { AutoTransactionsEnabled = false }
ctx.Database.AutoTransactionsEnabled = false; };
return ctx;
}
public void ResetDatabase() public void ResetDatabase()
{ {
lock (writeLock) lock (writeLock)
{ {
recycleThreadContexts(); recycleThreadContexts();
GC.Collect();
GC.WaitForPendingFinalizers();
host.Storage.DeleteDatabase(database_name); host.Storage.DeleteDatabase(database_name);
} }
} }

View File

@ -58,13 +58,22 @@ namespace osu.Game.Database
this.connectionString = connectionString; this.connectionString = connectionString;
var connection = Database.GetDbConnection(); var connection = Database.GetDbConnection();
try
{
connection.Open(); connection.Open();
using (var cmd = connection.CreateCommand()) using (var cmd = connection.CreateCommand())
{ {
cmd.CommandText = "PRAGMA journal_mode=WAL;"; cmd.CommandText = "PRAGMA journal_mode=WAL;";
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
} }
catch (Exception e)
{
connection.Close();
throw;
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {

View File

@ -211,15 +211,17 @@ namespace osu.Game
using (var db = contextFactory.GetForWrite(false)) using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate(); db.Context.Migrate();
} }
catch (MigrationFailedException e) catch (Exception e)
{ {
Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database); Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database);
// if we failed, let's delete the database and start fresh. // if we failed, let's delete the database and start fresh.
// todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this.
contextFactory.ResetDatabase(); contextFactory.ResetDatabase();
Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important); Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important);
// only run once more, then hard bail.
using (var db = contextFactory.GetForWrite(false)) using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate(); db.Context.Migrate();
} }