Skip to content

Commit 2aa7b20

Browse files
committed
test(it): stop cluster after ClusterITest
1 parent 455decc commit 2aa7b20

3 files changed

Lines changed: 89 additions & 58 deletions

File tree

src/it/java/io/weaviate/integration/ClusterITest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import java.io.IOException;
44

55
import org.assertj.core.api.Assertions;
6+
import org.junit.AfterClass;
7+
import org.junit.ClassRule;
68
import org.junit.Test;
9+
import org.junit.rules.TestRule;
710

811
import io.weaviate.ConcurrentTest;
912
import io.weaviate.client6.v1.api.WeaviateClient;
@@ -13,11 +16,18 @@
1316
import io.weaviate.client6.v1.api.cluster.replication.Replication;
1417
import io.weaviate.client6.v1.api.cluster.replication.ReplicationState;
1518
import io.weaviate.client6.v1.api.cluster.replication.ReplicationType;
19+
import io.weaviate.containers.Container;
1620
import io.weaviate.containers.Weaviate;
1721
import io.weaviate.containers.Weaviate.Version;
1822

1923
public class ClusterITest extends ConcurrentTest {
20-
private static final WeaviateClient client = Weaviate.cluster(3).getClient();
24+
private static final Weaviate cluster = Weaviate.cluster(3);
25+
private static final WeaviateClient client = cluster.getClient();
26+
27+
@AfterClass
28+
public static void tearDown() {
29+
cluster.stop();
30+
}
2131

2232
@Test
2333
public void test_shardingState() throws IOException {
Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.weaviate.integration;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
4+
35
import java.io.IOException;
46
import java.util.Map;
57

@@ -17,41 +19,36 @@
1719
import io.weaviate.containers.Model2Vec;
1820
import io.weaviate.containers.Weaviate;
1921

20-
import static org.assertj.core.api.Assertions.assertThat;
21-
2222
public class VectorizersITest extends ConcurrentTest {
2323
private static final Container.ContainerGroup compose = Container.compose(
24-
Weaviate.custom()
25-
.withModel2VecUrl(Model2Vec.URL)
26-
.build(),
27-
Container.MODEL2VEC);
28-
@ClassRule // Bind containers to the lifetime of the test
29-
public static final TestRule _rule = compose.asTestRule();
24+
Weaviate.custom()
25+
.withModel2VecUrl(Model2Vec.URL)
26+
.build(),
27+
Container.MODEL2VEC);
3028
private static final WeaviateClient client = compose.getClient();
3129

30+
@ClassRule // Bind containers to the lifetime of the test
31+
public static final TestRule __ = compose.asTestRule();
32+
3233
@Test
3334
public void testVectorizerModel2VecPropeties() throws IOException {
3435
var collectionName = ns("Model2Vec2NamedVectors");
3536
client.collections.create(collectionName,
36-
col -> col
37-
.properties(Property.text("name"), Property.text("author"))
38-
.vectorConfig(
39-
VectorConfig.text2vecModel2Vec("name", v -> v.sourceProperties("name")),
40-
VectorConfig.text2vecModel2Vec("author", v -> v.sourceProperties("author"))
41-
)
42-
);
37+
col -> col
38+
.properties(Property.text("name"), Property.text("author"))
39+
.vectorConfig(
40+
VectorConfig.text2vecModel2Vec("name", v -> v.sourceProperties("name")),
41+
VectorConfig.text2vecModel2Vec("author", v -> v.sourceProperties("author"))));
4342

4443
var model2vec = client.collections.use(collectionName);
4544
assertThat(model2vec).isNotNull();
4645

4746
String uuid1 = "00000000-0000-0000-0000-000000000001";
48-
WeaviateObject<Map<String, Object>> obj1 = WeaviateObject.of(o ->
49-
o.properties(Map.of("name", "Dune", "author", "Frank Herbert")).uuid(uuid1)
50-
);
47+
WeaviateObject<Map<String, Object>> obj1 = WeaviateObject
48+
.of(o -> o.properties(Map.of("name", "Dune", "author", "Frank Herbert")).uuid(uuid1));
5149
String uuid2 = "00000000-0000-0000-0000-000000000002";
52-
WeaviateObject<Map<String, Object>> obj2 = WeaviateObject.of(o ->
53-
o.properties(Map.of("name", "same content", "author", "same content")).uuid(uuid2)
54-
);
50+
WeaviateObject<Map<String, Object>> obj2 = WeaviateObject
51+
.of(o -> o.properties(Map.of("name", "same content", "author", "same content")).uuid(uuid2));
5552

5653
var resp = model2vec.data.insertMany(obj1, obj2);
5754
assertThat(resp).isNotNull().satisfies(s -> {
@@ -61,21 +58,21 @@ public void testVectorizerModel2VecPropeties() throws IOException {
6158
var o1 = model2vec.query.fetchObjectById(uuid1, FetchObjectById.Builder::includeVector);
6259
// Assert that for object1 we have generated 2 different vectors
6360
assertThat(o1).get()
64-
.extracting(WeaviateObject::vectors)
65-
.satisfies(v -> {
66-
assertThat(v.getSingle("name")).isNotEmpty();
67-
assertThat(v.getSingle("author")).isNotEmpty();
68-
assertThat(v.getSingle("name")).isNotEqualTo(v.getSingle("author"));
69-
});
61+
.extracting(WeaviateObject::vectors)
62+
.satisfies(v -> {
63+
assertThat(v.getSingle("name")).isNotEmpty();
64+
assertThat(v.getSingle("author")).isNotEmpty();
65+
assertThat(v.getSingle("name")).isNotEqualTo(v.getSingle("author"));
66+
});
7067

7168
var o2 = model2vec.query.fetchObjectById(uuid2, FetchObjectById.Builder::includeVector);
7269
// Assert that for object2 we have generated same vectors
7370
assertThat(o2).get()
74-
.extracting(WeaviateObject::vectors)
75-
.satisfies(v -> {
76-
assertThat(v.getSingle("name")).isNotEmpty();
77-
assertThat(v.getSingle("author")).isNotEmpty();
78-
assertThat(v.getSingle("name")).isEqualTo(v.getSingle("author"));
79-
});
71+
.extracting(WeaviateObject::vectors)
72+
.satisfies(v -> {
73+
assertThat(v.getSingle("name")).isNotEmpty();
74+
assertThat(v.getSingle("author")).isNotEmpty();
75+
assertThat(v.getSingle("name")).isEqualTo(v.getSingle("author"));
76+
});
8077
}
8178
}

src/main/java/io/weaviate/client6/v1/api/collections/batch/BatchContext.java

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,36 @@
4444
* BatchContext stores the state of an active batch process
4545
* and controls its lifecycle.
4646
*
47-
* <h2>State</h2>
48-
*
4947
* <h2>Lifecycle</h2>
5048
*
49+
* The SSB implementation is based on gRPC bidi-streams, which are modeled as a
50+
* pair of "observers" that define callbacks for inbound and outbound messages.
51+
* We'll refer to them as "sender" (sending messages to the server) and "recv"
52+
* (receiving and handing server-side events).
53+
*
54+
* <p>
55+
* When the context is started, the client exchanges a "recv" for a "sender",
56+
* then stores in {@link #messages}. A {@link Send} process is started in the
57+
* {@link #sendService} -- it will continue to run until the context is closed
58+
* either gracefully via {@link #close} or abruptly via {@link #shutdownNow}.
59+
*
60+
* <p>
61+
* A "recv" always runs on some internal gRPC thread. The "recv" process is
62+
* expected to exit whenever server closes its half of the stream, and will
63+
* be re-created if the stream is re-opened. {@link Recv} delegates most
64+
* of the operations to its parent BatchContext.
65+
*
66+
* <p>
67+
* Collectively, "sender" and "recv" are referred to as "workers".
68+
* After both "workers" exit the {@link #workers} count is expected to be 0.
69+
*
70+
* <h2>State</h2>
71+
*
5172
* <h2>Cancellation policy</h2>
5273
*
5374
* @param <PropertiesT> the shape of properties for inserted objects.
75+
*
76+
* @see StreamObserver
5477
*/
5578
public final class BatchContext<PropertiesT> implements Closeable {
5679
private final int maxReconnectRetries;
@@ -69,15 +92,15 @@ public final class BatchContext<PropertiesT> implements Closeable {
6992
* thread; the latter may be blocked on {@link Send#awaitCanSend} or
7093
* {@link Send#awaitCanPrepareNext}.
7194
*/
72-
private final ExecutorService sendExec = Executors.newSingleThreadExecutor();
95+
private final ExecutorService sendService = Executors.newSingleThreadExecutor();
7396

7497
/**
7598
* Scheduled thread pool for delayed tasks.
7699
*
77100
* @see Oom
78101
* @see Reconnecting
79102
*/
80-
private final ScheduledExecutorService scheduledExec = Executors.newScheduledThreadPool(1);
103+
private final ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(1);
81104

82105
/** The thread that created the context. */
83106
private final Thread parent = Thread.currentThread();
@@ -99,7 +122,7 @@ public final class BatchContext<PropertiesT> implements Closeable {
99122
/**
100123
* Work-in-progress items.
101124
*
102-
* An item is added to the wip map after the Sender successfully
125+
* An item is added to the wip map after the "sender" successfully
103126
* adds it to the {@link #batch} and is removed once the server reports
104127
* back the result (whether success of failure).
105128
*/
@@ -109,7 +132,7 @@ public final class BatchContext<PropertiesT> implements Closeable {
109132
* Current batch.
110133
*
111134
* <p>
112-
* An item is added to the batch after the Sender pulls it
135+
* An item is added to the batch after the "sender" pulls it
113136
* from the queue and remains there until it's Ack'ed.
114137
*/
115138
private final Batch batch;
@@ -144,8 +167,8 @@ public final class BatchContext<PropertiesT> implements Closeable {
144167
/** closing completes the stream. */
145168
private final CompletableFuture<Void> closing = new CompletableFuture<>();
146169

147-
/** Executor for performing the shutdown sequence. */
148-
private final ExecutorService shutdownExec = Executors.newSingleThreadExecutor();
170+
/** Executor for performing graceful shutdown sequence. */
171+
private final ExecutorService shutdownService = Executors.newSingleThreadExecutor();
149172

150173
/** Lightway check to ensure users cannot send on a closed context. */
151174
private volatile boolean closed;
@@ -202,7 +225,7 @@ void start() {
202225

203226
// "send" routine must start after the nextState has been set.
204227
setState(AWAIT_STARTED);
205-
send = sendExec.submit(new Send());
228+
send = sendService.submit(new Send());
206229
}
207230

208231
/**
@@ -261,27 +284,27 @@ public void close() throws IOException {
261284
}
262285
throw new IOException(e.getCause());
263286
} finally {
264-
shutdownExecutors();
287+
shutdownExecutionServices();
265288
}
266289
}
267290

291+
/** Start a graceful context shutdown. */
268292
private void shutdown() {
269-
CompletableFuture.runAsync(() -> {
293+
shutdownService.execute(() -> {
270294
try {
271295
// Poison the queue -- this will signal "send" to drain the remaining
272296
// items in the batch and in the backlog and exit.
273297
//
274298
// If shutdownNow has been called previously and the "send" routine
275299
// has been interrupted, this would block indefinitely.
276300
// Luckily, shutdownNow resolves the `closing` future as well.
277-
System.out.println("POISON THE QUEUE");
278301
queue.put(TaskHandle.POISON);
279302

280303
// Wait for the send to exit before closing our end of the stream.
281304
try {
282305
send.get();
283306
} catch (CancellationException ignored) {
284-
// Send task can be cancelled due to a reconnect or an internal error.
307+
// "sender" can be cancelled due to a reconnect or an internal error.
285308
}
286309

287310
// Wait for both "send" and "recv" to exit.
@@ -290,9 +313,10 @@ private void shutdown() {
290313
} catch (Exception e) {
291314
closing.completeExceptionally(e);
292315
}
293-
}, shutdownExec);
316+
});
294317
}
295318

319+
/** Terminate context abruptly. */
296320
private void shutdownNow(Exception ex) {
297321
// Now report this error to the server and terminate the stream.
298322
closing.completeExceptionally(ex);
@@ -315,21 +339,21 @@ private void shutdownNow(Exception ex) {
315339
}
316340
}
317341

318-
private void shutdownExecutors() {
342+
private void shutdownExecutionServices() {
319343
BiConsumer<String, List<Runnable>> assertEmpty = (name, pending) -> {
320344
assert pending.isEmpty() : "'%s' service had %d tasks awaiting execution"
321345
.formatted(pending.size(), name);
322346
};
323347

324348
List<Runnable> pending;
325349

326-
pending = sendExec.shutdownNow();
350+
pending = sendService.shutdownNow();
327351
assertEmpty.accept("send", pending);
328352

329-
pending = scheduledExec.shutdownNow();
353+
pending = scheduledService.shutdownNow();
330354
assertEmpty.accept("oom", pending);
331355

332-
pending = shutdownExec.shutdownNow();
356+
pending = shutdownService.shutdownNow();
333357
assertEmpty.accept("shutdown", pending);
334358
}
335359

@@ -377,7 +401,7 @@ boolean canPrepareNext() {
377401
* on a gRPC thread. {@link State} implementations SHOULD offload any
378402
* blocking operations to one of the provided executors.
379403
*
380-
* @see #scheduledExec
404+
* @see #scheduledService
381405
*/
382406
private void onEvent(Event event) {
383407
lock.lock();
@@ -719,7 +743,7 @@ private Oom(long delaySeconds) {
719743

720744
@Override
721745
public void onEnter(State prev) {
722-
shutdown = scheduledExec.schedule(this::initiateShutdown, delaySeconds, TimeUnit.SECONDS);
746+
shutdown = scheduledService.schedule(this::initiateShutdown, delaySeconds, TimeUnit.SECONDS);
723747
}
724748

725749
/** Imitate server shutdown sequence. */
@@ -748,7 +772,7 @@ public void onEvent(Event event) {
748772
shutdown.get();
749773
} catch (CancellationException ignored) {
750774
} catch (InterruptedException ignored) {
751-
// Recv is running on a thread from gRPC's internal thread pool,
775+
// "recv" is running on a thread from gRPC's internal thread pool,
752776
// so, while onEvent allows InterruptedException to stay responsive,
753777
// in practice this thread will only be interrupted by the thread pool,
754778
// which already knows it's being shut down.
@@ -763,7 +787,7 @@ public void onEvent(Event event) {
763787
/**
764788
* ServerShuttingDown allows preparing the next batch
765789
* unless the server's OOM'ed on the previous one.
766-
* Once set, the state will shutdown {@link BatchContext#sendExec}
790+
* Once set, the state will shutdown {@link BatchContext#sendService}
767791
* to instruct the "send" thread to close our part of the stream.
768792
*/
769793
private final class ServerShuttingDown extends BaseState {
@@ -863,7 +887,7 @@ private void reconnectNow() {
863887
* Schedule a task to {@link #reconnect} after a delay.
864888
*
865889
* <p>
866-
* The task is scheduled on {@link #scheduledExec} even if
890+
* The task is scheduled on {@link #scheduledService} even if
867891
* {@code delaySeconds == 0} to avoid blocking gRPC worker
868892
* thread,
869893
* where the {@link BatchContext#onEvent} callback runs.
@@ -873,7 +897,7 @@ private void reconnectNow() {
873897
private void reconnectAfter(long delaySeconds) {
874898
retries++;
875899

876-
scheduledExec.schedule(() -> {
900+
scheduledService.schedule(() -> {
877901
try {
878902
reconnect();
879903
} catch (InterruptedException e) {

0 commit comments

Comments
 (0)