blob: 7e975da7fc11e074aeff180a0ee00a69f800cde2 [file]
/*
* Copyright 2023 CeresDB Project Authors. Licensed under Apache-2.0.
*/
package io.ceresdb;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.ceresdb.models.Point;
import io.ceresdb.models.RequestContext;
import io.ceresdb.models.Value;
import io.ceresdb.models.WriteRequest;
import io.ceresdb.proto.internal.Common;
import io.ceresdb.proto.internal.Storage;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import io.ceresdb.common.Endpoint;
import io.ceresdb.common.util.Clock;
import io.ceresdb.models.Err;
import io.ceresdb.models.Result;
import io.ceresdb.models.WriteOk;
import io.ceresdb.options.WriteOptions;
import io.ceresdb.rpc.Context;
import io.ceresdb.rpc.Observer;
import io.ceresdb.util.StreamWriteBuf;
import io.ceresdb.util.TestUtil;
import io.ceresdb.util.Utils;
@RunWith(value = MockitoJUnitRunner.class)
public class WriteClientTest {
private WriteClient writeClient;
@Mock
private RouterClient routerClient;
@Before
public void before() {
final WriteOptions writeOpts = new WriteOptions();
writeOpts.setAsyncPool(ForkJoinPool.commonPool());
writeOpts.setRoutedClient(this.routerClient);
writeOpts.setDatabase("public");
this.writeClient = new WriteClient();
this.writeClient.init(writeOpts);
}
@After
public void after() {
this.writeClient.shutdownGracefully();
this.routerClient.shutdownGracefully();
}
@Test
public void writeAllSuccessTest() throws ExecutionException, InterruptedException {
final List<Point> data = TestUtil.newMultiTablePoints("write_client_test_table1", //
"write_client_test_table2", //
"write_client_test_table3");
final Endpoint ep1 = Endpoint.of("127.0.0.1", 8081);
final Endpoint ep2 = Endpoint.of("127.0.0.2", 8081);
final Endpoint ep3 = Endpoint.of("127.0.0.3", 8081);
final Common.ResponseHeader header = Common.ResponseHeader.newBuilder() //
.setCode(Result.SUCCESS) //
.build();
final Storage.WriteResponse resp = Storage.WriteResponse.newBuilder() //
.setHeader(header) //
.setSuccess(2) //
.build();
Mockito.when(this.routerClient.invoke(Mockito.eq(ep1), Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(resp));
Mockito.when(this.routerClient.invoke(Mockito.eq(ep2), Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(resp));
Mockito.when(this.routerClient.invoke(Mockito.eq(ep3), Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(resp));
Mockito.when(this.routerClient.routeFor(Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(new HashMap<String, Route>() {
private static final long serialVersionUID = 4694599978937545735L;
{
put("write_client_test_table1", Route.of("write_client_test_table1", ep1));
put("write_client_test_table2", Route.of("write_client_test_table2", ep2));
put("write_client_test_table3", Route.of("write_client_test_table3", ep3));
}
}));
final CompletableFuture<Result<WriteOk, Err>> f = this.writeClient.write(new WriteRequest(data),
Context.newDefault());
final Result<WriteOk, Err> ret = f.get();
Assert.assertTrue(ret.isOk());
Assert.assertEquals(new Integer(6), ret.mapOr(0, WriteOk::getSuccess));
Assert.assertEquals(new Integer(0), ret.mapOr(-1, WriteOk::getFailed));
}
@Test
public void writeSplitTest() throws ExecutionException, InterruptedException {
writeSplit(1, 1);
writeSplit(2, 2);
writeSplit(4, 4);
writeSplit(8, 8);
}
private void writeSplit(final int maxWriteSize, final int partOfSuccess)
throws ExecutionException, InterruptedException {
// re-init
this.writeClient.shutdownGracefully();
final WriteOptions writeOpts = new WriteOptions();
writeOpts.setDatabase("public");
writeOpts.setAsyncPool(ForkJoinPool.commonPool());
writeOpts.setRoutedClient(this.routerClient);
writeOpts.setMaxWriteSize(maxWriteSize);
this.writeClient = new WriteClient();
this.writeClient.init(writeOpts);
final List<Point> data = TestUtil.newMultiTablePoints("write_client_test_table1", //
"write_client_test_table2", //
"write_client_test_table3", //
"write_client_test_table4", //
"write_client_test_table5", //
"write_client_test_table6", //
"write_client_test_table7", //
"write_client_test_table8");
final Endpoint ep = Endpoint.of("127.0.0.1", 8081);
final Common.ResponseHeader header = Common.ResponseHeader.newBuilder() //
.setCode(Result.SUCCESS) //
.build();
final Storage.WriteResponse resp = Storage.WriteResponse.newBuilder() //
.setHeader(header) //
.setSuccess(partOfSuccess) //
.build();
Mockito.when(this.routerClient.invoke(Mockito.eq(ep), Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(resp));
Mockito.when(this.routerClient.routeFor(Mockito.any(), Mockito.any())) //
.thenReturn(Utils.completedCf(new HashMap<String, Route>() {
private static final long serialVersionUID = 4694599978937545735L;
{
put("write_client_test_table1", Route.of("write_client_test_table1", ep));
put("write_client_test_table2", Route.of("write_client_test_table2", ep));
put("write_client_test_table3", Route.of("write_client_test_table3", ep));
put("write_client_test_table4", Route.of("write_client_test_table4", ep));
put("write_client_test_table5", Route.of("write_client_test_table5", ep));
put("write_client_test_table6", Route.of("write_client_test_table6", ep));
put("write_client_test_table7", Route.of("write_client_test_table7", ep));
put("write_client_test_table8", Route.of("write_client_test_table8", ep));
}
}));
final CompletableFuture<Result<WriteOk, Err>> f = this.writeClient.write(new WriteRequest(data),
Context.newDefault());
final Result<WriteOk, Err> ret = f.get();
Assert.assertTrue(ret.isOk());
Assert.assertEquals(new Integer(16), ret.mapOr(0, WriteOk::getSuccess));
Assert.assertEquals(new Integer(0), ret.mapOr(-1, WriteOk::getFailed));
}
@Test
public void streamWriteTest() {
final String testTable = "stream_table_test";
final Endpoint ep = Endpoint.of("127.0.0.1", 8081);
Mockito.when(this.routerClient.routeFor(Mockito.any(), Mockito.eq(Collections.singleton(testTable)))) //
.thenReturn(Utils.completedCf(new HashMap<String, Route>() {
private static final long serialVersionUID = 8473563130528272901L;
{
put(testTable, Route.of(testTable, ep));
}
}));
final CompletableFuture<WriteOk> f = new CompletableFuture<>();
final AtomicInteger dataCount = new AtomicInteger();
Mockito.when(this.routerClient.invokeClientStreaming(Mockito.any(), Mockito.any(Storage.WriteRequest.class),
Mockito.any(), Mockito.any())).thenReturn(new Observer<Storage.WriteRequest>() {
@Override
public void onNext(final Storage.WriteRequest value) {
final int c = value.getTableRequestsList().stream() //
.flatMap(writeTableRequest -> writeTableRequest.getEntriesList().stream()) //
.map(Storage.WriteSeriesEntry::getFieldGroupsCount).reduce(0, Integer::sum);
dataCount.addAndGet(c);
}
@Override
public void onError(final Throwable err) {
// ignored
}
@Override
public void onCompleted() {
f.complete(WriteOk.ok(dataCount.get(), 0, null));
}
});
final StreamWriteBuf<Point, WriteOk> writer = this.writeClient.streamWrite(testTable);
final CompletableFuture<WriteOk> ret = writer //
.write(TestUtil.newTableTwoPoints(testTable)) //
.write(TestUtil.newTableTwoPoints(testTable)) //
.write(TestUtil.newTableTwoPoints(testTable)) //
.flush() //
.write(TestUtil.newTableTwoPoints(testTable)) //
.flush() //
.writeAndFlush(TestUtil.newMultiTablePoints(testTable, testTable)) //
.completed();
f.whenComplete((r, e) -> {
if (e != null) {
ret.completeExceptionally(e);
} else {
ret.complete(r);
}
});
Assert.assertEquals(12, ret.join().getSuccess());
}
@Test
public void rowsToWriteProtoTest() {
List<Point> table1 = new ArrayList<>();
table1.add(Point.newPointBuilder("table1") //
.setTimestamp(Clock.defaultClock().getTick()).addTag("t1_tag1", Value.withString("v1")) //
.addTag("t1_tag2", Value.withString("v2")) //
.addTag("t1_tag3", Value.withString("v3")) //
.addField("t1_field1", Value.withDouble(0.1)).addField("t1_field2", Value.withString("he"))
.addField("t1_field3", Value.withStringOrNull(null)).build());
table1.add(Point.newPointBuilder("table1") //
.setTimestamp(Clock.defaultClock().getTick()).addTag("t1_tag1", Value.withString("v1")) //
.addTag("t1_tag2", Value.withString("v2")) //
.addTag("t1_tag3", Value.withString("v3")) //
.addField("t1_field1", Value.withDouble(0.1)).addField("t1_field2", Value.withString("he"))
.addField("t1_field3", Value.withStringOrNull("surprise!!!")).build());
List<Point> table2 = new ArrayList<>();
table2.add(Point.newPointBuilder("table2") //
.setTimestamp(Clock.defaultClock().getTick()).addTag("t2_tag1", Value.withString("v1")) //
.addTag("t2_tag2", Value.withString("v2")) //
.addTag("t2_tag3", Value.withString("v3")) //
.addField("t2_field1", Value.withDouble(0.1)).addField("t2_field2", Value.withString("he")).build());
table2.add(Point.newPointBuilder("table2").setTimestamp(Clock.defaultClock().getTick() + 10)
.addTag("t2_tag1", Value.withString("v1")) //
.addTag("t2_tag2", Value.withString("v2")) //
.addTag("t2_tag3", Value.withString("v3")) //
.addField("t2_field1", Value.withDouble(0.1)).addField("t2_field2", Value.withString("he"))
.addField("t2_field3", Value.withStringOrNull("surprise!!!")).build());
final List<Point> table3 = Collections.singletonList(Point.newPointBuilder("table1")
.setTimestamp(System.currentTimeMillis()).addTag("t1_tag1", Value.withString("vv1")) //
.addTag("t1_tag2", Value.withString("vv2")) //
.addField("t1_field1", Value.withDouble(0.1)).addField("t1_field2", Value.withString("he")).build());
RequestContext reqCtx = new RequestContext();
reqCtx.setDatabase("public");
final Storage.WriteRequest writeReq = this.writeClient.toWriteRequestObj(reqCtx,
Stream.of(table1, table2, table3).flatMap(List::stream).collect(Collectors.toList()).stream());
Assert.assertNotNull(writeReq);
Assert.assertEquals(2, writeReq.getTableRequestsCount());
final Set<String> tables = writeReq.getTableRequestsList() //
.stream() //
.map(Storage.WriteTableRequest::getTable) //
.collect(Collectors.toSet());
Assert.assertEquals(new HashSet<>(Arrays.asList("table1", "table2")), tables);
{
final Optional<Storage.WriteTableRequest> opl = writeReq.getTableRequestsList().stream()
.filter(wm -> wm.getTable().equals("table1")) //
.findFirst(); //
Assert.assertTrue(opl.isPresent());
final Storage.WriteTableRequest tableRequest = opl.get();
Assert.assertEquals(3, tableRequest.getTagNamesCount());
Assert.assertEquals(3, tableRequest.getFieldNamesCount());
Assert.assertEquals(2, tableRequest.getEntriesCount());
{
final Optional<Storage.WriteSeriesEntry> opl2 = tableRequest.getEntriesList().stream().filter(w -> {
final Set<String> tagVSet = w.getTagsList().stream().map(tag -> tag.getValue().getStringValue())
.collect(Collectors.toSet());
return tagVSet.contains("v1") && tagVSet.contains("v2") && tagVSet.contains("v3");
}).findFirst();
Assert.assertTrue(opl2.isPresent());
Assert.assertEquals(0, opl2.get().getTags(0).getNameIndex());
Assert.assertEquals(1, opl2.get().getTags(1).getNameIndex());
Assert.assertEquals(2, opl2.get().getTags(2).getNameIndex());
Assert.assertEquals(2, opl2.get().getFieldGroupsCount());
Assert.assertEquals(2, opl2.get().getFieldGroups(0).getFieldsCount());
Assert.assertEquals(3, opl2.get().getFieldGroups(1).getFieldsCount());
}
}
{
final Optional<Storage.WriteTableRequest> opl = writeReq.getTableRequestsList().stream()
.filter(wm -> wm.getTable().equals("table2")) //
.findFirst(); //
Assert.assertTrue(opl.isPresent());
final Storage.WriteTableRequest tableRequest = opl.get();
Assert.assertEquals(3, tableRequest.getTagNamesCount());
Assert.assertEquals(3, tableRequest.getFieldNamesCount());
Assert.assertEquals(1, tableRequest.getEntriesCount());
{
final Optional<Storage.WriteSeriesEntry> opl2 = tableRequest.getEntriesList().stream().filter(w -> {
final Set<String> tagVSet = w.getTagsList().stream().map(tag -> tag.getValue().getStringValue())
.collect(Collectors.toSet());
return tagVSet.contains("v1") && tagVSet.contains("v2") && tagVSet.contains("v3");
}).findFirst();
Assert.assertTrue(opl2.isPresent());
Assert.assertEquals(0, opl2.get().getTags(0).getNameIndex());
Assert.assertEquals(1, opl2.get().getTags(1).getNameIndex());
Assert.assertEquals(2, opl2.get().getTags(2).getNameIndex());
Assert.assertEquals(2, opl2.get().getFieldGroupsCount());
Assert.assertEquals(2, opl2.get().getFieldGroups(0).getFieldsCount());
Assert.assertEquals(3, opl2.get().getFieldGroups(1).getFieldsCount());
}
}
}
}