| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.processors.igfs; |
| |
| import java.util.concurrent.ThreadLocalRandom; |
| import org.apache.ignite.igfs.IgfsGroupDataBlocksKeyMapper; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.lang.IgniteUuid; |
| |
| /** |
| * Tests for {@link org.apache.ignite.igfs.IgfsGroupDataBlocksKeyMapper} hash. |
| */ |
| public class IgfsGroupDataBlockKeyMapperHashSelfTest extends IgfsCommonAbstractTest { |
| /** |
| * @throws Exception If failed. |
| */ |
| public void testDistribution() throws Exception { |
| for (int i = 0; i < 100; i++) { |
| int grpSize = ThreadLocalRandom.current().nextInt(2, 100000); |
| int partCnt = ThreadLocalRandom.current().nextInt(1, grpSize); |
| |
| checkDistribution(grpSize, partCnt); |
| } |
| } |
| |
| /** |
| * @throws Exception If failed. |
| */ |
| public void testIntOverflowDistribution() throws Exception { |
| for (int i = 0; i < 100; i++) |
| checkIntOverflowDistribution(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE)); |
| } |
| |
| /** |
| * @param mapper IGFS blocks mapper. |
| * @param fileId IGFS file ID. |
| * @param blockId File block ID. |
| * @param partCnt Total partitions count. |
| * @return Partition index. |
| */ |
| private int partition(IgfsGroupDataBlocksKeyMapper mapper, IgniteUuid fileId, long blockId, int partCnt) { |
| return U.safeAbs((Integer) mapper.affinityKey(new IgfsBlockKey(fileId, null, false, blockId)) % partCnt); |
| } |
| |
| /** |
| * Check hash code generation for the given group size and partitions count. |
| * |
| * @throws Exception If failed. |
| */ |
| public void checkDistribution(int grpSize, int partCnt) throws Exception { |
| IgniteUuid fileId = IgniteUuid.randomUuid(); |
| |
| IgfsGroupDataBlocksKeyMapper mapper = new IgfsGroupDataBlocksKeyMapper(grpSize); |
| |
| int lastPart = 0; |
| |
| boolean first = true; |
| |
| for (int i = 0; i < 10; i++) { |
| // Ensure that all blocks within the group has the same hash codes. |
| boolean firstInGroup = true; |
| |
| for (int j = 0; j < grpSize; j++) { |
| int part = partition(mapper, fileId, i * grpSize + j, partCnt); |
| |
| if (firstInGroup) { |
| if (first) |
| first = false; |
| else |
| assert checkPartition(lastPart, part, partCnt) : |
| "[fileId = " + fileId + ", i=" + i + ", j=" + j + ", grpSize= " + grpSize + |
| ", partCnt=" + partCnt + ", lastPart=" + lastPart + ", part=" + part + ']'; |
| |
| firstInGroup = false; |
| } |
| else |
| assert part == lastPart; |
| |
| lastPart = part; |
| } |
| } |
| } |
| |
| /** |
| * Check distribution for integer overflow. |
| * |
| * @throws Exception If failed. |
| */ |
| @SuppressWarnings("NumericOverflow") |
| public void checkIntOverflowDistribution(int partCnt) throws Exception { |
| IgniteUuid fileId = IgniteUuid.randomUuid(); |
| |
| IgfsGroupDataBlocksKeyMapper mapper = new IgfsGroupDataBlocksKeyMapper(1); |
| |
| int part1 = partition(mapper, fileId, Integer.MAX_VALUE - 1, partCnt); |
| int part2 = partition(mapper, fileId, Integer.MAX_VALUE, partCnt); |
| int part3 = partition(mapper, fileId, (long)Integer.MAX_VALUE + 1, partCnt); |
| |
| assert checkPartition(part1, part2, partCnt) : |
| "[fileId = " + fileId + "part1=" + part1 + ", part2=" + part2 + ", partCnt=" + partCnt + ']'; |
| |
| assert checkPartition(part2, part3, partCnt) : |
| "[fileId = " + fileId + "part1=" + part2 + ", part3=" + part3 + ", partCnt=" + partCnt + ']'; |
| } |
| |
| /** |
| * Check correct partition shift. |
| * |
| * @param prevPart Previous partition. |
| * @param part Current partition. |
| * @param totalParts Total partitions. |
| * @return {@code true} if previous and current partitions have correct values. |
| */ |
| private boolean checkPartition(int prevPart, int part, int totalParts) { |
| return U.safeAbs(prevPart - part) == 1 || |
| (part == 0 && prevPart == totalParts - 1) || |
| (prevPart == 0 && part == totalParts - 1); |
| } |
| } |