2021-01-07 13:07:36 +08:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
2021-01-14 15:13:10 +08:00
|
|
|
using System;
|
2021-01-07 13:07:36 +08:00
|
|
|
using System.Threading;
|
2021-01-13 16:34:44 +08:00
|
|
|
using osu.Framework.Graphics;
|
2021-01-12 13:36:35 +08:00
|
|
|
using osu.Framework.Logging;
|
2021-01-07 13:07:36 +08:00
|
|
|
using osu.Framework.Platform;
|
|
|
|
using osu.Framework.Statistics;
|
|
|
|
using Realms;
|
|
|
|
|
|
|
|
namespace osu.Game.Database
|
|
|
|
{
|
2021-01-13 16:34:44 +08:00
|
|
|
public class RealmContextFactory : Component, IRealmFactory
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
|
|
|
private readonly Storage storage;
|
2021-01-13 17:24:19 +08:00
|
|
|
|
2021-01-07 13:07:36 +08:00
|
|
|
private const string database_name = @"client";
|
|
|
|
|
2021-01-12 13:25:07 +08:00
|
|
|
private const int schema_version = 5;
|
|
|
|
|
2021-01-12 13:36:35 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Lock object which is held for the duration of a write operation (via <see cref="GetForWrite"/>).
|
|
|
|
/// </summary>
|
2021-01-07 13:07:36 +08:00
|
|
|
private readonly object writeLock = new object();
|
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
private static readonly GlobalStatistic<int> reads = GlobalStatistics.Get<int>("Realm", "Get (Read)");
|
|
|
|
private static readonly GlobalStatistic<int> writes = GlobalStatistics.Get<int>("Realm", "Get (Write)");
|
2021-01-13 17:24:19 +08:00
|
|
|
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>("Realm", "Dirty Refreshes");
|
2021-01-13 16:34:44 +08:00
|
|
|
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>("Realm", "Contexts (Created)");
|
2021-01-14 14:51:19 +08:00
|
|
|
private static readonly GlobalStatistic<int> pending_writes = GlobalStatistics.Get<int>("Realm", "Pending writes");
|
2021-01-13 16:34:44 +08:00
|
|
|
|
|
|
|
private Realm context;
|
|
|
|
|
|
|
|
public Realm Context
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
2021-01-13 16:34:44 +08:00
|
|
|
get
|
|
|
|
{
|
|
|
|
if (context == null)
|
|
|
|
{
|
|
|
|
context = createContext();
|
|
|
|
Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}");
|
|
|
|
}
|
2021-01-11 17:57:56 +08:00
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
// creating a context will ensure our schema is up-to-date and migrated.
|
2021-01-07 14:41:29 +08:00
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
return context;
|
|
|
|
}
|
2021-01-07 14:41:29 +08:00
|
|
|
}
|
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
public RealmContextFactory(Storage storage)
|
2021-01-07 14:41:29 +08:00
|
|
|
{
|
2021-01-13 16:34:44 +08:00
|
|
|
this.storage = storage;
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
public Realm GetForRead()
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
|
|
|
reads.Value++;
|
2021-01-13 16:34:44 +08:00
|
|
|
return createContext();
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public RealmWriteUsage GetForWrite()
|
|
|
|
{
|
|
|
|
writes.Value++;
|
2021-01-14 14:51:19 +08:00
|
|
|
pending_writes.Value++;
|
|
|
|
|
2021-01-07 13:07:36 +08:00
|
|
|
Monitor.Enter(writeLock);
|
2021-01-13 16:34:44 +08:00
|
|
|
|
2021-01-13 17:24:19 +08:00
|
|
|
return new RealmWriteUsage(this);
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
protected override void Update()
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
2021-01-13 16:34:44 +08:00
|
|
|
base.Update();
|
2021-01-07 13:07:36 +08:00
|
|
|
|
2021-01-13 16:34:44 +08:00
|
|
|
if (Context.Refresh())
|
|
|
|
refreshes.Value++;
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-12 13:36:35 +08:00
|
|
|
private Realm createContext()
|
|
|
|
{
|
|
|
|
contexts_created.Value++;
|
|
|
|
|
|
|
|
return Realm.GetInstance(new RealmConfiguration(storage.GetFullPath($"{database_name}.realm", true))
|
|
|
|
{
|
|
|
|
SchemaVersion = schema_version,
|
|
|
|
MigrationCallback = onMigration,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-13 17:24:19 +08:00
|
|
|
private void onMigration(Migration migration, ulong lastSchemaVersion)
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
2021-01-13 17:24:19 +08:00
|
|
|
}
|
2021-01-07 13:07:36 +08:00
|
|
|
|
2021-01-14 15:13:10 +08:00
|
|
|
/// <summary>
|
|
|
|
/// A transaction used for making changes to realm data.
|
|
|
|
/// </summary>
|
|
|
|
public class RealmWriteUsage : IDisposable
|
2021-01-13 17:24:19 +08:00
|
|
|
{
|
2021-01-14 15:13:10 +08:00
|
|
|
public readonly Realm Realm;
|
2021-01-07 13:07:36 +08:00
|
|
|
|
2021-01-14 15:13:10 +08:00
|
|
|
private readonly RealmContextFactory factory;
|
|
|
|
private readonly Transaction transaction;
|
|
|
|
|
|
|
|
internal RealmWriteUsage(RealmContextFactory factory)
|
2021-01-07 13:07:36 +08:00
|
|
|
{
|
2021-01-14 15:13:10 +08:00
|
|
|
this.factory = factory;
|
|
|
|
|
|
|
|
Realm = factory.createContext();
|
|
|
|
transaction = Realm.BeginWrite();
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
2021-01-13 16:34:44 +08:00
|
|
|
|
2021-01-14 15:13:10 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Commit all changes made in this transaction.
|
|
|
|
/// </summary>
|
|
|
|
public void Commit() => transaction.Commit();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Revert all changes made in this transaction.
|
|
|
|
/// </summary>
|
|
|
|
public void Rollback() => transaction.Rollback();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Disposes this instance, calling the initially captured action.
|
|
|
|
/// </summary>
|
|
|
|
public virtual void Dispose()
|
2021-01-13 17:24:19 +08:00
|
|
|
{
|
2021-01-14 15:13:10 +08:00
|
|
|
// rollback if not explicitly committed.
|
|
|
|
transaction?.Dispose();
|
|
|
|
|
2021-01-13 17:24:19 +08:00
|
|
|
Monitor.Exit(factory.writeLock);
|
2021-01-14 14:51:19 +08:00
|
|
|
pending_writes.Value--;
|
2021-01-13 17:24:19 +08:00
|
|
|
}
|
2021-01-13 16:34:44 +08:00
|
|
|
}
|
2021-01-07 13:07:36 +08:00
|
|
|
}
|
|
|
|
}
|