25

使用 RavenDb 进行单元测试时,通常会检索或处理新添加的数据。这可能导致“过时索引”异常,例如

Bulk operation cancelled because the index is stale and allowStale is false

根据许多答案

在处理查询或批处理操作之前强制数据库(IDocumentStore实例)等到其索引不陈旧的方法是DefaultQueryingConsistency = ConsistencyOptions.QueryYourWritesIDocumentStore初始化期间使用,如下所示:

public class InMemoryRavenSessionProvider : IRavenSessionProvider
{
    private static IDocumentStore documentStore;

    public static IDocumentStore DocumentStore
    {
        get { return (documentStore ?? (documentStore = CreateDocumentStore())); }
    }

    private static IDocumentStore CreateDocumentStore()
    {
        var store = new EmbeddableDocumentStore
        {
            RunInMemory = true,
            Conventions = new DocumentConvention
            {
                DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites,
                IdentityPartsSeparator = "-"
            }
        };
        store.Initialize();
        IndexCreation.CreateIndexes(typeof (RavenIndexes).Assembly, store);
        return store;
    }

    public IDocumentSession GetSession()
    {
        return DocumentStore.OpenSession();
    }
}

不幸的是,上面的代码不起作用。我仍然收到有关过时索引的例外情况。这些可以通过放入包含.Customize(x => x.WaitForNonStaleResultsAsOfLastWrite()).

这很好,只要这些可以包含在单元测试中,但如果不能呢?我发现这些WaitForNonStaleResults*调用正在潜入生产代码,只是为了让单元测试通过。

那么,是否有一种可靠的方法,使用最新版本的 RavenDb,在允许处理命令之前强制索引更新 - 仅用于单元测试的目的?

编辑 1

这是基于下面给出的答案的解决方案,该解决方案强制等待索引不陈旧。为了单元测试的方便,我把它写成一个扩展方法;

public static class IDocumentSessionExt
{
    public static void ClearStaleIndexes(this IDocumentSession db)
    {
        while (db.Advanced.DatabaseCommands.GetStatistics().StaleIndexes.Length != 0)
        {
            Thread.Sleep(10);
        }
    }
}

这是一个使用详细WaitForNonStaleResultsAsOfLastWrite技术但现在使用更整洁的扩展方法的单元测试。

[Fact]
public void Should_return_list_of_Relationships_for_given_mentor()
{
    using (var db = Fake.Db())
    {
        var mentorId = Fake.Mentor(db).Id;
        Fake.Relationship(db, mentorId, Fake.Mentee(db).Id);
        Fake.Relationship(db, mentorId, Fake.Mentee(db).Id);
        Fake.Relationship(db, Fake.Mentor(db).Id, Fake.Mentee(db).Id);

        //db.Query<Relationship>()
        //  .Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
        //  .Count()
        //  .ShouldBe(3);

        db.ClearStaleIndexes();
        db.Query<Relationship>().Count().ShouldBe(3);
        MentorService.GetRelationships(db, mentorId).Count.ShouldBe(2);
    }
}
4

3 回答 3

30

如果您有 Map/Reduce 索引,DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites则无法使用。您需要使用另一种方法。

在您的单元测试中,在您插入任何数据之后立即调用这样的代码,这将强制所有索引在您执行任何其他操作之前更新:

while (documentStore.DatabaseCommands.GetStatistics().StaleIndexes.Length != 0)
{
    Thread.Sleep(10);
}

更新如果您愿意,您当然可以将其放入扩展方法中:

public static class IDocumentSessionExt
{
    public static void ClearStaleIndexes(this IDocumentSession db)
    {
        while (db.Advanced.DatabaseCommands.GetStatistics().StaleIndexes.Length != 0)
        {
            Thread.Sleep(10);
        }
    }
}

然后你可以说:

db.ClearStaleIndexes();
于 2012-04-25T13:57:07.837 回答
14

您实际上可以在 DocumentStore 上添加一个查询侦听器来等待非陈旧的结果。这可以仅用于单元测试,因为它在文档存储中,而不是每个操作。

// Initialise the Store.
var documentStore = new EmbeddableDocumentStore
                {
                    RunInMemory = true
                };
documentStore.Initialize();

// Force queries to wait for indexes to catch up. Unit Testing only :P
documentStore.RegisterListener(new NoStaleQueriesListener());

....


#region Nested type: NoStaleQueriesListener

public class NoStaleQueriesListener : IDocumentQueryListener
{
    #region Implementation of IDocumentQueryListener

    public void BeforeQueryExecuted(IDocumentQueryCustomization queryCustomization)
    {
        queryCustomization.WaitForNonStaleResults();
    }

    #endregion
}

#endregion

(无耻从RavenDB偷来的怎么刷机?

于 2012-04-25T14:27:57.063 回答
2

请注意,StaleIndexes 还包括废弃和禁用的索引——它们永远不会更新。

因此,为避免无限等待,请改用此属性:

 var staleIndices = store.DatabaseCommands.GetStatistics().CountOfStaleIndexesExcludingDisabledAndAbandoned;
于 2017-07-31T11:02:00.883 回答