/* * 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); } }