【问题标题】:What could cause Realm to lock up when attempting a write?什么可能导致 Realm 在尝试写入时锁定?
【发布时间】:2021-01-05 23:28:50
【问题描述】:

我的团队目前在跨所有平台(Android、iOS 和 UWP)的 Xamarin.Forms 应用程序中遇到问题。 Realm 经常会变得无响应,再次使用它的唯一方法是关闭应用程序。在过去的几个月里,它变得更加频繁和容易重现,但我们无法确定原因或解决方法。

我们已经确定了一些可能有助于确定正在发生的事情的模式。我们注意到,每当需要数据库中的信息时,我们都会看到工作线程卡在Realm.Write() 调用上。这种行为似乎几乎就像在 Realm 系统中发生了死锁一样。关于它被卡在哪个Write() 调用上并不一致,这似乎是基于领域何时失败的随机性。此时,任何其他通过任何方法访问此领域的尝试,例如Find()All()Remove() 等也会卡住。我们还确认 Write() 中的代码此时从未运行过,因为我们可以在第一行放置一个独立于领域的日志记录调用,并且永远不会在我们的日志中看到它。

一旦出现此问题,除此之外可能还会出现其他一些问题。我们的应用程序中还有另外两个领域,它们处理完全独立的数据,因此没有重叠的代码。这些 Realm 从来都不是这个问题的原因,但是当问题 Realm 卡住时,有时会导致其他 Realm 在下一次调用时也卡住。这个问题有时也会在应用程序的使用之间持续存在,导致第一次调用 Realm 时卡住,需要完全重新安装才能修复。

由于我们的应用程序使用基于反应式的编程,我们不得不以稍微不同的方式来构建我们处理数据库的方式。对于 Realm 问题,我们有一个服务,它可以在可观察的流中保持单个实例处于活动状态,然后可以订阅该实例以观察变化。我在这篇文章的末尾添加了一些这种架构的例子。我们还通过此流路由所有其他不可观察的操作,但是在调试期间,我们能够将这些调用移动到它们自己的独立领域实例,而几乎没有问题/功能没有变化。

目前,我们认为这很可能与我们如何将 Realm 转换为可观察系统有关,或者与我们的 Realms 以某种方式崩溃/损坏有关。

RealmStream 声明:

_realmStream = Observable
    .Start(() => Realm.GetInstance(_dbConfig), _scheduler)
    .Do(_ => logger.LogTrace("Realm created"), () => logger.LogTrace("Realm stream completed"))
    .Replay()
    .AutoConnect();

RealmStream 使用示例:

public IObservable<IChangeSet<TResult>> GetChangeSetStream<TSource, TResult>(Func<Realm, IQueryable<TSource>> selector, Func<TSource, TResult> transformFactory) where TSource : RealmObject
{           
    return _realmStream
        .Select(realm =>
            selector(realm)
                .AsRealmCollection()
                .ToObservableChangeSet<IRealmCollection<TSource>, TSource>()
                .SubscribeOn(_scheduler)
                .Transform(transformFactory)
                .DisposeMany())
        .Switch()
        .Catch<IChangeSet<TResult>, Exception>(ex =>
        {
            _logger.LogError(ex, "Error getting property change stream");
            return Observable.Return<IChangeSet<TResult>>(default);
        })
        .SubscribeOn(_scheduler);
}

Non-Observable 领域方法:

public async Task Run(Action<Realm> action)
{
    await _realmStream
        .Do(action)
        .SubscribeOn(_scheduler);
}

public async Task<TResult> Run<TResult>(Func<Realm, TResult> action)
{
    return await _realmStream
        .Select(action)
        .SubscribeOn(_scheduler);
}

到目前为止,我们已经尝试了以下方法:

  • 确保 Realm 和 Xamarin 都在最新版本上
  • 减少Realm.Write()s 的数量(小改进)
  • 将每个 Realm 函数移动到我们的可观察系统中(没有明显变化,我们的大多数函数已经这样做了)
  • 尝试将不需要可观察对象的所有内容移动到使用独立领域实例(增加锁定频率)
  • 试图将所有内容从我们的单个 Realm 实例中移走。我们无法做到这一点,因为我们无法确定如何正确处理一些可观察的事件,例如 RealmObject 被删除,而不会导致严重的性能问题

【问题讨论】:

    标签: xamarin xamarin.forms realm reactive-programming


    【解决方案1】:

    realm.Write 需要获取写锁,根据您的描述,您似乎确实遇到了死锁,其中具有打开写事务的线程等待另一个线程卡在realm.Write 调用上。如果您能够在附加调试器的情况下重现手,您可以检查线程窗口并尝试查明有问题的代码。

    This article 提供了一些有关调试死锁的提示。不幸的是,如果没有整个项目和重现案例,就很难查明原因。

    【讨论】:

    • 那么问题可能是传递给写入的操作有问题,并且在领域写入中失败以防止它释放其内部锁?如果是这种情况,有没有办法查看哪个线程/函数当前持有锁?我已经使用线程窗口查看了哪个写入调用无法访问锁,但没有关于哪个线程持有它的信息。
    • 写入错误有点模棱两可——如果它抛出一个异常,就会被处理,事务将被回滚,从而释放锁。更可能的情况是领域内的操作。写入调用到另一个线程上的某些东西,该线程也尝试执行领域。写入。在线程窗口中,您可以看到每个线程暂停的语句 - 如果我的理论是正确的,一个应该在领域暂停。写入,另一个 - 在领域内的某事上暂停。写入。
    • 经过几天的努力,我们认为这不是嵌套领域写入的问题。我的团队已经完成了我们代码中的每一次写入,虽然我们确实找到了删除其中的一小部分,但我们仍然遇到了这个问题。在我们的线程窗口中,我们仍然看不到与领域写入相关的任何其他线程。即使没有嵌套写入,这是否可能是由写入中的线程工作引起的?我们的应用中有几个写入使用诸如 async-await 或 observables 之类的东西,这可能会导致问题吗?
    • 如果您的意思是这样,您可能不应该在事务中使用await。如果您的意思是在打开或提交事务之前使用 async-await,那应该没问题,只要您注意确保不跨线程使用领域。如果您可以隔离一个重现该问题或愿意分享您的代码的小项目,请随时打开 Github 问题:github.com/realm/realm-dotnet/issues - SO 更倾向于答案而不是讨论。
    猜你喜欢
    • 2018-07-04
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 2018-10-25
    • 2016-01-14
    • 2010-10-22
    相关资源
    最近更新 更多