2012-04-25 22 views
22

Cuando se prueban las unidades con RavenDb, a menudo ocurre que datos recién agregados se recuperan o procesan. Esto puede llevar a excepciones de "índice obsoleto", p.RavenDb: Fuerza los índices a esperar hasta que no estén obsoletos mientras se prueban las unidades

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

De acuerdo con una serie de respuestas

La forma de obligar a la base de datos (la IDocumentStore ejemplo) para esperar hasta que sus índices no estan rancios antes del procesamiento una operación de consulta o lote es utilizar DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites durante la inicialización IDocumentStore, así:

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(); 
    } 
} 

Por desgracia, el código anterior no funciona. Todavía recibo excepciones con respecto a los índices obsoletos. Estos pueden resolverse mediante consultas ficticias que incluyen .Customize(x => x.WaitForNonStaleResultsAsOfLastWrite()).

Esto está bien, siempre que estos puedan estar contenidos en la Prueba de Unidad, pero ¿qué pasa si no pueden? Estoy descubriendo que estas llamadas WaitForNonStaleResults* están entrando sigilosamente en el código de producción solo para poder pasar las pruebas unitarias.

Entonces, ¿hay una forma segura de utilizar la última versión de RavenDb para forzar el refrescamiento de los índices antes de permitir que se procesen los comandos, solo para fines de prueba unitaria?

Editar 1

Aquí es una solución basada en la respuesta dar a continuación que obliga a esperar hasta que el índice no está demasiado cargado. Lo he escrito como un método de extensión por el bien de la conveniencia de las pruebas unitarias;

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

Y aquí es una prueba de unidad que estaba utilizando la técnica de WaitForNonStaleResultsAsOfLastWrite prolijo pero ahora utiliza el método de extensión más ordenado.

[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

Esto ahora se lleva a cabo contra el DocumentStore en lugar de la DocumentSession, por lo que el método de extensión cambiaría a usar algo como db.Advanced.DocumentStore.DatabaseCommands. GetStatistics(). StaleIndexes.Any(), o simplemente entregue el DocumentStore directamente si puede – adrian

Respuesta

27

Si usted tiene un mapa/Reducir índice, DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites no funcionará. Necesitas usar un método alternativo.

En sus unidades de pruebas, código de llamada como ésta, inmediatamente después de que haya insertado ningún dato, esto obligará a los todos índices de actualizar antes de hacer cualquier otra cosa:

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

actualización Usted por supuesto puede ponerlo en un método de extensión si se quiere:

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

Entonces se puede decir:

db.ClearStaleIndexes(); 
+0

Voy a probar la idea 'Thread.Sleep (10)'. Sin embargo, tengo índices de mapa (tienen una función de mapa pero no una función de reducción) y son estos índices los que arrojan la excepción 'Operación masiva ... el índice está a punto '. ¿Se espera que el '' ConsistencyOptions.QueryYourWrites' funcione para índices Map-only? – biofractal

+0

Debería hacerlo, pero la Operación masiva podría ser el problema. Use el código de arriba para asegurarse de que todos los índices estén actualizados (después de insertar documentos y antes de realizar consultas) y debe estar bien. –

+1

Consulte Editar 1. Funciona de maravilla, aunque es posible que desee actualizar su respuesta a la sintaxis más reciente. Encapsular la espera como un método de extensión hace que todo sea agradable y ordenado :-) – biofractal

14

En realidad, puede agregar un detector de consultas en el Almacén de documentos para esperar resultados fuera de la señal.Esto se puede usar solo para pruebas unitarias, ya que está en el almacén de documentos y no en cada operación.

// 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 

(robada Desvergonzadamente de RavenDB how to flush?)

+2

Probé esto y funcionó para llamadas 'Query <>()' normales (sin índices obsoletos) pero falló para un índice de Map (no reduce) llamado mediante 'db .Advanced.DatabaseCommands.UpdateByIndex (..) '. Es este el comportamiento esperado? – biofractal

+0

Hrm, eso sería diferente de una consulta normal. Tendré que analizar esto (o sugerir algo para implementar) ya que debe haber una forma no invasiva de hacerlo. – Rangoric

+2

Usar un oyente sería una mejor solución de baja intervención que llamar manualmente un método de extensión según la respuesta aceptada, sin embargo, hasta ahora ninguno de los arreglos de nivel de DocumentStore parecen funcionar con una actualización masiva, es decir, cuando uso un índice a través de UpdateByIndex (..) '. ¡Buena suerte! – biofractal

1

Tenga en cuenta que StaleIndexes incluir también los índices de discapacidad y abondoned - que nunca ponerse al día.

Así que para evitar la espera utilizar esta propiedad en lugar indefinetely:

var staleIndices = store.DatabaseCommands.GetStatistics().CountOfStaleIndexesExcludingDisabledAndAbandoned; 
+0

Acabo de tropezar con un problema porque StaleIndexes también incluye a los abandonados. Tu enfoque parece resolverlo. – Fjut

Cuestiones relacionadas