|
1 | 1 | package io.tarantool.driver.integration;
|
2 | 2 |
|
| 3 | +import io.tarantool.driver.api.TarantoolClient; |
3 | 4 | import io.tarantool.driver.api.TarantoolResult;
|
4 | 5 | import io.tarantool.driver.api.conditions.Conditions;
|
5 | 6 | import io.tarantool.driver.api.space.TarantoolSpaceOperations;
|
6 | 7 | import io.tarantool.driver.api.tuple.TarantoolTuple;
|
7 |
| -import io.tarantool.driver.exceptions.TarantoolClientException; |
| 8 | +import io.tarantool.driver.protocol.Packable; |
8 | 9 |
|
| 10 | +import java.io.ByteArrayOutputStream; |
| 11 | +import java.io.IOException; |
| 12 | +import java.util.BitSet; |
| 13 | +import java.util.Collection; |
9 | 14 | import java.util.List;
|
| 15 | +import java.util.Optional; |
| 16 | +import java.util.concurrent.ExecutionException; |
10 | 17 |
|
11 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals;
|
12 |
| -import static org.junit.jupiter.api.Assertions.fail; |
13 | 19 |
|
14 | 20 | /**
|
15 | 21 | * @author Ivan Dneprov
|
| 22 | + * @author Artyom Dubinin |
16 | 23 | */
|
17 | 24 | public final class Utils {
|
| 25 | + private static Optional<Integer> bucketCount = Optional.empty(); |
| 26 | + |
18 | 27 | private Utils() {
|
19 | 28 | }
|
20 | 29 |
|
21 | 30 | /**
|
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) { |
28 | 36 | assertEquals(0, testSpace.select(Conditions.any()).thenApply(List::size).join());
|
29 | 37 | }
|
| 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 | + } |
30 | 97 | }
|
0 commit comments