Skip to content

Commit f4d15d4

Browse files
committed
Add tests how to calculate bucketId on client
1 parent 5941b8a commit f4d15d4

File tree

3 files changed

+109
-12
lines changed

3 files changed

+109
-12
lines changed

src/main/java/io/tarantool/driver/api/metadata/TarantoolMetadataOperations.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,32 @@ public interface TarantoolMetadataOperations {
1919

2020
/**
2121
* Refresh metadata cache
22+
*
2223
* @return future with empty value for tracking the refresh progress
2324
* @throws TarantoolClientException if fetching data failed with error
2425
*/
2526
CompletableFuture<Void> refresh() throws TarantoolClientException;
2627

2728
/**
2829
* Get metadata for the space specified by name
30+
*
2931
* @param spaceName the space name, must not be null or empty
3032
* @return nullable space metadata wrapped in {@link Optional}
3133
*/
3234
Optional<TarantoolSpaceMetadata> getSpaceByName(String spaceName);
3335

3436
/**
3537
* Get metadata for index from the specified space by name
36-
* @param spaceId the space ID, must be greater than 0
38+
*
39+
* @param spaceId the space ID, must be greater than 0
3740
* @param indexName index name, must not be null or empty
3841
* @return nullable index metadata wrapped in {@link Optional}
3942
*/
4043
Optional<TarantoolIndexMetadata> getIndexByName(int spaceId, String indexName);
4144

4245
/**
4346
* Get metadata for index from the specified space by name
47+
*
4448
* @param spaceName the space name, must not be null or empty
4549
* @param indexName index name, must not be null or empty
4650
* @return nullable index metadata wrapped in {@link Optional}
@@ -49,14 +53,16 @@ public interface TarantoolMetadataOperations {
4953

5054
/**
5155
* Get metadata for index from the specified space by index ID
56+
*
5257
* @param spaceName the space name, must not be null or empty
53-
* @param indexId index ID, must not be must be greater or equal than 0
58+
* @param indexId index ID, must not be must be greater or equal than 0
5459
* @return nullable index metadata wrapped in {@link Optional}
5560
*/
5661
Optional<TarantoolIndexMetadata> getIndexById(String spaceName, int indexId);
5762

5863
/**
5964
* Get metadata for index from the specified space by index ID
65+
*
6066
* @param spaceId the space ID, must be greater than 0
6167
* @param indexId index ID, must not be must be greater or equal than 0
6268
* @return nullable index metadata wrapped in {@link Optional}
@@ -65,20 +71,23 @@ public interface TarantoolMetadataOperations {
6571

6672
/**
6773
* Get metadata for the space specified by id
74+
*
6875
* @param spaceId the space ID, must be greater than 0
6976
* @return nullable space metadata wrapped in {@link Optional}
7077
*/
7178
Optional<TarantoolSpaceMetadata> getSpaceById(int spaceId);
7279

7380
/**
7481
* Get metadata for all indexes for space specified by id
82+
*
7583
* @param spaceId the space ID, must be greater than 0
7684
* @return nullable map of index names to index metadata wrapped in {@link Optional}
7785
*/
7886
Optional<Map<String, TarantoolIndexMetadata>> getSpaceIndexes(int spaceId);
7987

8088
/**
8189
* Get metadata for all indexes for space specified by name
90+
*
8291
* @param spaceName the space name, must not be null or empty
8392
* @return nullable map of index names to index metadata wrapped in {@link Optional}
8493
*/
Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,97 @@
11
package io.tarantool.driver.integration;
22

3+
import io.tarantool.driver.api.TarantoolClient;
34
import io.tarantool.driver.api.TarantoolResult;
45
import io.tarantool.driver.api.conditions.Conditions;
56
import io.tarantool.driver.api.space.TarantoolSpaceOperations;
67
import io.tarantool.driver.api.tuple.TarantoolTuple;
7-
import io.tarantool.driver.exceptions.TarantoolClientException;
8+
import io.tarantool.driver.protocol.Packable;
89

10+
import java.io.ByteArrayOutputStream;
11+
import java.io.IOException;
12+
import java.util.BitSet;
13+
import java.util.Collection;
914
import java.util.List;
15+
import java.util.Optional;
16+
import java.util.concurrent.ExecutionException;
1017

1118
import static org.junit.jupiter.api.Assertions.assertEquals;
12-
import static org.junit.jupiter.api.Assertions.fail;
1319

1420
/**
1521
* @author Ivan Dneprov
22+
* @author Artyom Dubinin
1623
*/
1724
public final class Utils {
25+
private static Optional<Integer> bucketCount = Optional.empty();
26+
1827
private Utils() {
1928
}
2029

2130
/**
22-
* Checks if the space is empty.
23-
*
24-
* @param testSpace space to check
25-
*/
26-
static void checkSpaceIsEmpty(TarantoolSpaceOperations<TarantoolTuple,
27-
TarantoolResult<TarantoolTuple>> testSpace) {
31+
* Checks if the space is empty.
32+
*
33+
* @param testSpace space to check
34+
*/
35+
static void checkSpaceIsEmpty(TarantoolSpaceOperations<TarantoolTuple, TarantoolResult<TarantoolTuple>> testSpace) {
2836
assertEquals(0, testSpace.select(Conditions.any()).thenApply(List::size).join());
2937
}
38+
39+
/**
40+
* Get number of buckets in vshard cluster.
41+
*
42+
* @param client Tarantool client for with access to vshard router
43+
* @param <T> target tuple type
44+
* @param <R> target tuple collection type
45+
* @return number of buckets
46+
*/
47+
public static <T extends Packable, R extends Collection<T>> Integer getBucketCount(
48+
TarantoolClient<T, R> client) throws ExecutionException, InterruptedException {
49+
if (!bucketCount.isPresent()) {
50+
bucketCount = Optional.ofNullable(
51+
client.callForSingleResult("vshard.router.bucket_count", Integer.class).get()
52+
);
53+
}
54+
return bucketCount.get();
55+
}
56+
57+
/**
58+
* Get bucket_id via crc32 hash function.
59+
* You can't use null, because null is packed to box.NULL((void *) 0) and java doesn't have equivalent.
60+
*
61+
* @param client Tarantool client for with access to vshard router
62+
* @param key key that will be used to calculate bucketId
63+
* @param <T> target tuple type
64+
* @param <R> target tuple collection type
65+
* @return bucketId number determining the location in the cluster
66+
*/
67+
public static <T extends Packable, R extends Collection<T>> Integer getBucketIdStrCRC32(
68+
TarantoolClient<T, R> client, List<Object> key) throws ExecutionException, InterruptedException {
69+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
70+
for (Object part : key) {
71+
try {
72+
if (part != null) {
73+
outputStream.write(part.toString().getBytes());
74+
}
75+
} catch (IOException e) {
76+
throw new RuntimeException(e);
77+
}
78+
}
79+
return Math.toIntExact(
80+
(crc32(outputStream.toByteArray()) % getBucketCount(client)) + 1
81+
);
82+
}
83+
84+
private static long crc32(byte[] data) {
85+
BitSet bitSet = BitSet.valueOf(data);
86+
int crc32 = 0xFFFFFFFF; // initial value
87+
for (int i = 0; i < data.length * 8; i++) {
88+
if (((crc32 >>> 31) & 1) != (bitSet.get(i) ? 1 : 0)) {
89+
crc32 = (crc32 << 1) ^ 0x1EDC6F41; // xor with polynomial
90+
} else {
91+
crc32 = (crc32 << 1);
92+
}
93+
}
94+
crc32 = Integer.reverse(crc32); // result reflect
95+
return crc32 & 0x00000000ffffffffL; // the unsigned java problem
96+
}
3097
}

src/test/java/io/tarantool/driver/integration/proxy/options/ProxySpaceInsertOptionsIT.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
import io.tarantool.driver.core.ClusterTarantoolTupleClient;
1515
import io.tarantool.driver.core.ProxyTarantoolTupleClient;
1616
import io.tarantool.driver.api.space.options.proxy.ProxyInsertOptions;
17-
import io.tarantool.driver.exceptions.TarantoolConnectionException;
1817
import io.tarantool.driver.exceptions.TarantoolInternalException;
1918
import io.tarantool.driver.integration.SharedCartridgeContainer;
19+
import io.tarantool.driver.integration.Utils;
2020
import io.tarantool.driver.mappers.DefaultMessagePackMapperFactory;
2121
import org.junit.jupiter.api.BeforeAll;
2222
import org.junit.jupiter.api.BeforeEach;
2323
import org.junit.jupiter.api.Test;
2424

25+
import java.util.Arrays;
2526
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.List;
@@ -134,13 +135,33 @@ public void withBucketIdTest() throws ExecutionException, InterruptedException {
134135
assertEquals(1, selectResult.size());
135136
}
136137

138+
private Integer getBucketIdFromTarantool(List<Object> key) throws ExecutionException, InterruptedException {
139+
return client.callForSingleResult(
140+
"vshard.router.bucket_id_strcrc32",
141+
Collections.singletonList(key),
142+
Integer.class
143+
).get();
144+
}
145+
146+
@Test
147+
public void withBucketIdClientComputationTest() throws ExecutionException, InterruptedException {
148+
List<List<Object>> keys = Arrays.asList(
149+
Collections.singletonList(1),
150+
Arrays.asList(1, "FIO"),
151+
Arrays.asList(1, true, "FIO", 'm', 100.123)
152+
);
153+
154+
for (List<Object> key : keys) {
155+
assertEquals(Utils.getBucketIdStrCRC32(client, key), getBucketIdFromTarantool(key));
156+
}
157+
}
158+
137159
@Test
138160
public void withBucketIdMoreThanLimitTest() throws ExecutionException, InterruptedException {
139161
TarantoolSpaceOperations<TarantoolTuple, TarantoolResult<TarantoolTuple>> profileSpace =
140162
client.space(TEST_SPACE_NAME);
141163

142164
TarantoolTuple tarantoolTuple = tupleFactory.create(1, null, "FIO", 50, 100);
143-
Conditions condition = Conditions.equals(PK_FIELD_NAME, 1);
144165

145166
Integer bucketsCount = client.callForSingleResult("vshard.router.bucket_count", Integer.class).get();
146167
InsertOptions insertOptions = ProxyInsertOptions.create().withBucketId(bucketsCount * 2);

0 commit comments

Comments
 (0)