RavenDb: форсировать индексы до тех пор, пока не будет устаревать при модульном тестировании
При модульном тестировании с RavenDb часто бывает, что вновь добавленные данные извлекаются или обрабатываются иным образом. Это может привести к исключениям "устаревших индексов", например,
Bulk operation cancelled because the index is stale and allowStale is false
В соответствии с рядом ответов
Чтобы заставить базу данных (экземпляр IDocumentStore
) дождаться, пока ее индексы не будут устаревшими до обработки запроса или пакетной операции, следует использовать DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites
во время инициализации IDocumentStore
, например:
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())
.
Это нормально, если они могут содержаться в Unit Test, но что делать, если они не могут? Я нахожу, что эти вызовы 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);
}
}
}
И вот Unit Test, который использовал метод verbose WaitForNonStaleResultsAsOfLastWrite
, но теперь использует метод расширения neater.
[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);
}
}
Ответы
Ответ 1
Если у вас есть индекс Map/Reduce, DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites
не будет работать. Вам нужно использовать альтернативный метод.
В ваших модулях тесты вызывают код, подобный этому, сразу после того, как вы вставили какие-либо данные, это заставит индексы all обновиться, прежде чем вы сделаете что-нибудь еще:
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();
Ответ 2
Фактически вы можете добавить прослушиватель запросов в 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 как очистить?)
Ответ 3
Имейте в виду, что StaleIndexes также включает индексированные и отключенные индексы, которые никогда не будут обновляться.
Итак, чтобы избежать ожидания неопределенно использовать это свойство вместо:
var staleIndices = store.DatabaseCommands.GetStatistics().CountOfStaleIndexesExcludingDisabledAndAbandoned;