2121import org .junit .After ;
2222import org .junit .AfterClass ;
2323import org .junit .Before ;
24- import org .junit .Ignore ;
2524import org .junit .Test ;
2625
2726import io .grpc .stub .StreamObserver ;
@@ -40,10 +39,14 @@ public class BatchContextTest {
4039 Optional .of (ConsistencyLevel .ONE ), Optional .of ("john_doe" ));
4140
4241 /**
43- * Dedicated executor for emitting events
44- * while the context is shutting down.
42+ * Dedicated executor for occasional asynchrony.
4543 *
46- * @see BatchContext#close()
44+ * <p>
45+ * Most of the time the "server", the "user", the "test" processes
46+ * will run on the main thread, one at a time, when the logic permits.
47+ * In some cases it is useful to put at least one of those processes
48+ * onto a separate thread. E.g. when the batch is being drained, but
49+ * the main thread is blocked by {@link BatchContext#close}.
4750 */
4851 private static final ExecutorService EXEC = Executors .newSingleThreadExecutor ();
4952
@@ -141,7 +144,7 @@ public void test_drainOnClose() throws Exception {
141144 // Contrary the test above, we expect the objects to be sent
142145 // only after context.close(), as the half-empty batch will
143146 // be drained. Similarly, we want to ack everything as it arrives.
144- Future <?> mockServer = EXEC .submit (() -> {
147+ Future <?> backgroundAcks = EXEC .submit (() -> {
145148 try {
146149 List <String > received = ack ();
147150 Assertions .assertThat (tasks )
@@ -156,7 +159,7 @@ public void test_drainOnClose() throws Exception {
156159 });
157160
158161 context .close ();
159- mockServer .get (); // Wait for the "mock server" to process the data message.
162+ backgroundAcks .get (); // Wait for the "mock server" to process the data message.
160163
161164 Assertions .assertThat (tasks ).extracting (TaskHandle ::result )
162165 .allMatch (CompletableFuture ::isDone )
@@ -172,8 +175,7 @@ public void test_backoff() throws Exception {
172175 server .emitEvent (new Event .Backoff (BATCH_SIZE / 2 ));
173176
174177 List <TaskHandle > tasks = new ArrayList <>();
175- ExecutorService exec = Executors .newSingleThreadExecutor ();
176- Future <?> testUser = exec .submit (() -> {
178+ Future <?> backgroundAdd = EXEC .submit (() -> {
177179 try {
178180 for (int i = 0 ; i < BATCH_SIZE ; i ++) {
179181 tasks .add (context .add (WeaviateObject .of ()));
@@ -189,7 +191,7 @@ public void test_backoff() throws Exception {
189191 Assertions .assertThat (received ).hasSize (BATCH_SIZE / 2 );
190192 server .emitEvent (new Event .Results (received , Collections .emptyMap ()));
191193
192- testUser .get (); // Finish populating batch context.
194+ backgroundAdd .get (); // Finish populating batch context.
193195
194196 // Since testUser will try and add BATCH_SIZE no. objects,
195197 // we should expect there to be exactly 2 batches.
@@ -222,7 +224,7 @@ public void test_backoffBacklog() throws Exception {
222224 server .emitEvent (new Event .Backoff (batchSizeNew ));
223225
224226 // The next item will go on the backlog and the trigger a flush,
225- // which will continue to send batches and re-populate the from
227+ // which will continue to send batches and re-populate from
226228 // the backlog as long as the batch is full, so we should expect
227229 // to see 2 batches of size BATCH_SIZE / 2 each.
228230 List <String > received ;
@@ -320,25 +322,65 @@ public void test_reconnect_onStreamHangup() throws Exception {
320322 ack ();
321323 }
322324
323- @ Ignore
324325 @ Test
325326 public void test_reconnect_DrainAfterStreamHangup () throws Exception {
326327 server .expectMessage (WeaviateProtoBatch .BatchStreamRequest .MessageCase .START );
327328 server .emitEvent (Event .STARTED );
328329
330+ List <TaskHandle > tasks = new ArrayList <>();
331+
329332 // Trigger a flush.
330333 for (int i = 0 ; i < BATCH_SIZE ; i ++) {
331- context .add (WeaviateObject .of ());
334+ tasks . add ( context .add (WeaviateObject .of () ));
332335 }
333336
334- // Expect a new batch to arrive. Hangup the stream before sending the Acks.
335- server .expectMessage (WeaviateProtoBatch .BatchStreamRequest .MessageCase .DATA );
337+ // Expect a new batch to arrive. Ack the batch, and trigger another flush.
338+ ack ();
339+ for (int i = 0 ; i < BATCH_SIZE ; i ++) {
340+ tasks .add (context .add (WeaviateObject .of ()));
341+ }
342+
343+ // Ack the latest batch, but now Before sending back the results
344+ // for either one, hang up the stream.
345+ ack ();
336346 server .hangup ();
337347
348+ // On hangup, client should re-populate the batch from the WIP buffer.
349+ // Add one more item to trigger the flush.
350+ tasks .add (context .add (WeaviateObject .of ()));
351+
338352 // The client should try to reconnect, because the context is still open.
353+ // Once the server starts accepting connections again, the client will
354+ // flush the 2 full batches and pause, waiting for the next event.
339355 server .expectMessage (WeaviateProtoBatch .BatchStreamRequest .MessageCase .START );
340356 server .emitEvent (Event .STARTED );
341357
358+ List <String > ids = ack ();
359+ server .emitEvent (new Event .Results (ids , Collections .emptyMap ()));
360+
361+ ids = ack ();
362+ server .emitEvent (new Event .Results (ids , Collections .emptyMap ()));
363+
364+ // There is now 1 item remaining in the batch.
365+ // On context close, the client will drain it.
366+ Future <?> backgroundAcks = EXEC .submit (() -> {
367+ try {
368+ List <String > id = ack ();
369+ Assertions .assertThat (id ).as ("drained item" ).hasSize (1 );
370+ Future <?> applied = server .emitEvent (new Event .Results (id , Collections .emptyMap ()));
371+ // applied.get();
372+ } catch (Exception e ) {
373+ throw new RuntimeException (e );
374+ }
375+ });
376+
377+ context .close ();
378+ backgroundAcks .get ();
379+
380+ Assertions .assertThat (tasks ).extracting (TaskHandle ::result )
381+ .allMatch (CompletableFuture ::isDone )
382+ .extracting (CompletableFuture ::get ).extracting (TaskHandle .Result ::error )
383+ .allMatch (Optional ::isEmpty );
342384 }
343385
344386 @ Test
@@ -429,12 +471,12 @@ WeaviateProtoBatch.BatchStreamRequest recv() throws InterruptedException {
429471 return requestQueue .take ();
430472 }
431473
432- void emitEvent (Event event ) {
474+ Future <?> emitEvent (Event event ) {
433475 if (event == Event .EOF ) {
434476 assert Thread .currentThread () != TEST_THREAD : "test MUST NOT close/terminate the the stream" ;
435477 }
436478
437- eventExecutor .execute (() -> {
479+ return eventExecutor .submit (() -> {
438480 System .out .println ("emit " + event );
439481 if (event == Event .EOF ) {
440482 eventStream .onCompleted ();
0 commit comments