在内存 SQLite 数据库上使用 NHibernate 进行测试时出现随机错误

发布于 2024-10-31 22:24:25 字数 1425 浏览 0 评论 0 原文

我有一个系统,在收到消息后将其排队(写入表),另一个进程轮询数据库并将其出队进行处理。在我的自动测试中,我已将操作合并到同一进程中,但无法(概念上)合并两个操作中的 NH 会话。

自然地,问题就会出现。

我已经阅读了有关让 SQLite-InMemory-NHibernate 组合在测试领域工作的所有内容,但由于“没有这样的表”错误,我现在遇到了随机失败的测试。明确地说 - “随机”意味着具有相同精确配置和代码的相同测试有时会失败。

我有以下 SQLite 配置:

return SQLiteConfiguration
 .Standard
 .ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
 .Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");

在测试开始时(每次测试),我获取“静态”会话提供程序,并请它刷新现有数据库,并重新创建架构:

public void PurgeDatabaseOrCreateNew()
{
    using (var session = GetNewSession())
    using (var tx = session.BeginTransaction())
    {
            PurgeDatabaseOrCreateNew(session);
            tx.Commit();
    }
}

private void PurgeDatabaseOrCreateNew(ISession session)
{
    //http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
    new SchemaExport(_Configuration)
        .Execute(false, true, false, session.Connection, null);
}

所以是的,它位于不同的会话上,但连接集中在 SQLite 上,因此我创建的下一个会话将看到生成的架构。然而,虽然大多数时候它都有效,但有时稍后的“入队”操作会失败,因为它看不到我的传入消息的表。 另外,每次测试套件运行时,这种情况似乎最多发生一到两次;并非所有测试都失败,只有第一个测试失败(有时还有另一个测试。不太确定是否是第二个测试)。

当然,最糟糕的部分是随机性。我告诉自己我已经多次修复了这个问题,只是因为它“不再失败”。随机的。

这种情况发生在 FW4.0、System.Data.SQLite x86 版本、Win7 64b 和 2008R2(总共三台不同的机器)、NH2.1.2、配置了 FNH、TestDriven.NET 32b 进程和 NUnit 控制台 32b 进程上。

帮助?

I have a system which after getting a message - enqueues it (write to a table), and another process polls the DB and dequeues it for processing. In my automatic tests I've merged the operations in the same process, but cannot (conceptually) merge the NH sessions from the two operations.

Naturally - problems arise.

I've read everything I could about getting the SQLite-InMemory-NHibernate combination to work in the testing world, but I've now ran into RANDOMLY failing tests, due to "no such table" errors. To make it clear - "random" means that the same test with the same exact configuration and code will sometimes fail.

I have the following SQLite configuration:

return SQLiteConfiguration
 .Standard
 .ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
 .Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");

At the beginning of my test (every test) I fetch the "static" session provider, and kindly ask it to flush the existing DB clean, and recreate the schema:

public void PurgeDatabaseOrCreateNew()
{
    using (var session = GetNewSession())
    using (var tx = session.BeginTransaction())
    {
            PurgeDatabaseOrCreateNew(session);
            tx.Commit();
    }
}

private void PurgeDatabaseOrCreateNew(ISession session)
{
    //http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
    new SchemaExport(_Configuration)
        .Execute(false, true, false, session.Connection, null);
}

So yes, it's on a different session, but the connection is pooled on SQLite, so the next session I create will see the generated schema. Yet, while most of the times it works - sometimes the later "enqueue" operation will fail because it cannot see a table for my incoming messages.
Also - that seems to happen at max one or twice per test suite run; not all the tests are failing, just the first one (and sometimes another one. Not quite sure if it's the second or not).

The worst part is the randomness, naturally. I've told myself I've fixed this several times now, just because it simply "stopped failing". At random.

This happens on FW4.0, System.Data.SQLite x86 version, Win7 64b and 2008R2 (three differen machine in total), NH2.1.2, configured with FNH, on TestDriven.NET 32b precesses and NUnit console 32b processes.

Help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情深如许 2024-11-07 22:24:25

嗨,我很确定我和你有完全相同的问题。我在每个集成测试中打开和关闭多个会话。在深入研究 SQLite 连接池和我自己的一些实验之后,我得出以下结论:

SQLite 池代码使用 Wea​​kReferences 缓存连接,这不是最好的 缓存选项,因为当没有对连接的正常(强)引用时,对连接的引用将被清除连接并且 GC 运行。由于您无法预测 GC 何时运行,这解释了“随机性”。尝试在关闭一个会话和打开另一个会话之间添加一个 GC.Collect(); ,您的测试将始终失败。

我的解决方案是自己缓存打开会话之间的连接,如下所示:

public class BaseIntegrationTest
{
    private static ISessionFactory _sessionFactory;
    private static Configuration _configuration;
    private static SchemaExport _schemaExport;

    // I cache the whole session because I don't want it and the
    // underlying connection to get closed.
    // The "Connection" property of the ISession is what we actually want.
    // Using the NHibernate SQLite Driver to get the connection would probably
    // work too.
    private static ISession _keepConnectionAlive;

    static BaseIntegrationTest()
    {
        _configuration = new Configuration();
        _configuration.Configure();
        _configuration.AddAssembly(typeof(Product).Assembly);
        _sessionFactory = _configuration.BuildSessionFactory();
        _schemaExport = new SchemaExport(_configuration);

        _keepConnectionAlive = _sessionFactory.OpenSession();
    }

    [SetUp]
    protected void RecreateDB()
    {
        _schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
    }

    protected ISession OpenSession()
    {
        return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
    }
}

我的每个集成测试都继承自此类,并调用 OpenSession() 来获取会话。由于 [SetUp] 属性,NUnit 在每次测试之前都会调用 RecreateDB。

我希望这对您或其他遇到此错误的人有所帮助。

Hi I'm pretty sure I have the exact same problem as you. I open and close multiple sessions per integration test. After digging through the SQLite connection pooling and some experimenting of my own, I've come to the following conclusion:

The SQLite pooling code caches the connection using WeakReferences, which isn't the best option for caching, since the reference to the connection(s) will be cleared when there is no normal (strong) reference to the connection and the GC runs. Since you can't predict when the GC runs, this explains the "randomness". Try and add a GC.Collect(); between closing one and opening another session, your test will always fail.

My solution was to cache the connection myself between opening sessions, like this:

public class BaseIntegrationTest
{
    private static ISessionFactory _sessionFactory;
    private static Configuration _configuration;
    private static SchemaExport _schemaExport;

    // I cache the whole session because I don't want it and the
    // underlying connection to get closed.
    // The "Connection" property of the ISession is what we actually want.
    // Using the NHibernate SQLite Driver to get the connection would probably
    // work too.
    private static ISession _keepConnectionAlive;

    static BaseIntegrationTest()
    {
        _configuration = new Configuration();
        _configuration.Configure();
        _configuration.AddAssembly(typeof(Product).Assembly);
        _sessionFactory = _configuration.BuildSessionFactory();
        _schemaExport = new SchemaExport(_configuration);

        _keepConnectionAlive = _sessionFactory.OpenSession();
    }

    [SetUp]
    protected void RecreateDB()
    {
        _schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
    }

    protected ISession OpenSession()
    {
        return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
    }
}

Each of my integrationtests inherits from this class, and calls OpenSession() to get a session. RecreateDB is called by NUnit before each test because of the [SetUp] attribute.

I hope this helps you or anyone else who gets this error.

诠释孤独 2024-11-07 22:24:25

唯一想到的是您在测试后随机保持会话打开。在打开另一个 ISession 之前,您必须确保任何现有的 ISession 已关闭。如果您不使用 using() 语句或手动调用 Dispose(),会话可能仍处于活动状态,导致这些随机异常。

Only thing that comes into mind that you are randomly leaving session open after the test. You must make sure any existing ISession is closed before you open another one. If you are not using the using() statement or calling Dispose() manually the session might still be alive somewhere causing those random exceptions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文