All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.simulator.tests.helpers.KeyUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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 com.hazelcast.simulator.tests.helpers;

import com.hazelcast.cluster.Member;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.partition.Partition;
import com.hazelcast.partition.PartitionService;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import static com.hazelcast.simulator.utils.CommonUtils.sleepSeconds;
import static com.hazelcast.simulator.utils.GeneratorUtils.generateAsciiString;
import static java.lang.String.format;

public final class KeyUtils {

    private KeyUtils() {
    }

    /**
     * Checks if a key is located on a Hazelcast instance.
     *
     * @param instance the HazelcastInstance the key should belong to
     * @param key      the key to check
     * @return true if the key belongs to the Hazelcast instance, false otherwise
     */
    public static boolean isLocalKey(HazelcastInstance instance, Object key) {
        PartitionService partitionService = instance.getPartitionService();
        Partition partition = partitionService.getPartition(key);
        Member owner;
        while (true) {
            owner = partition.getOwner();
            if (owner != null) {
                break;
            }
            sleepSeconds(1);
        }
        return owner.equals(instance.getLocalEndpoint());
    }

    private static KeyGenerator newIntKeyGenerator(HazelcastInstance hz, KeyLocality keyLocality, int keyCount) {
        switch (keyLocality) {
            case LOCAL:
                return new BalancedIntKeyGenerator(hz, keyLocality, keyCount);
            case REMOTE:
                return new BalancedIntKeyGenerator(hz, keyLocality, keyCount);
            case RANDOM:
                return new BalancedIntKeyGenerator(hz, keyLocality, keyCount);
            case SINGLE_PARTITION:
                return new SinglePartitionIntKeyGenerator();
            default:
                return new SharedIntKeyGenerator();
        }
    }

    private static KeyGenerator newStringKeyGenerator(
            HazelcastInstance hz, KeyLocality keyLocality, int keyCount, int keyLength, String prefix) {
        switch (keyLocality) {
            case LOCAL:
                return new BalancedStringKeyGenerator(hz, keyLocality, keyCount, keyLength, prefix);
            case REMOTE:
                return new BalancedStringKeyGenerator(hz, keyLocality, keyCount, keyLength, prefix);
            case RANDOM:
                return new BalancedStringKeyGenerator(hz, keyLocality, keyCount, keyLength, prefix);
            case SINGLE_PARTITION:
                return new SinglePartitionStringKeyGenerator(keyLength, prefix);
            default:
                return new SharedStringKeyGenerator(keyLength, prefix);
        }
    }

    /**
     * Generates an array of int keys with a configurable keyLocality.
     *
     * If the instance is a client, keyLocality is ignored.
     *
     * @param keyCount    the number of keys in the array
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created array of keys
     */
    public static int[] generateIntKeys(int keyCount, KeyLocality keyLocality, HazelcastInstance hz) {
        KeyGenerator keyGenerator = newIntKeyGenerator(hz, keyLocality, keyCount);

        int[] keys = new int[keyCount];
        for (int i = 0; i < keys.length; i++) {
            keys[i] = keyGenerator.next();
        }
        return keys;
    }

    /**
     * Generates an array of int keys with a configurable keyLocality.
     *
     * If the instance is a client, keyLocality is ignored.
     *
     * @param keyCount    the number of keys in the array
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created array of keys
     */
    public static Integer[] generateIntegerKeys(int keyCount, KeyLocality keyLocality, HazelcastInstance hz) {
        KeyGenerator keyGenerator = newIntKeyGenerator(hz, keyLocality, keyCount);

        Integer[] keys = new Integer[keyCount];
        for (int i = 0; i < keys.length; i++) {
            keys[i] = keyGenerator.next();
        }
        return keys;
    }

    /**
     * Generates a string key with a configurable keyLocality.
     *
     * @param keyLength   the length of each string key
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created key
     */
    public static String generateStringKey(int keyLength, KeyLocality keyLocality, HazelcastInstance hz) {
        KeyGenerator keyGenerator = newStringKeyGenerator(hz, keyLocality, Integer.MAX_VALUE, keyLength, "");
        return keyGenerator.next();
    }

    /**
     * Generates an array of string keys with a configurable keyLocality.
     *
     * If the instance is a client, keyLocality is ignored.
     *
     * @param keyCount    the number of keys in the array
     * @param keyLength   the length of each string key
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created array of keys
     */
    public static String[] generateStringKeys(int keyCount, int keyLength, KeyLocality keyLocality, HazelcastInstance hz) {
        return generateStringKeys("", keyCount, keyLength, keyLocality, hz);
    }

    /**
     * Generates an array of string keys with a configurable keyLocality.
     *
     * If the hz is a client, keyLocality is ignored.
     *
     * @param prefix      prefix for the generated keys
     * @param keyCount    the number of keys in the array
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created array of keys
     */
    public static String[] generateStringKeys(String prefix, int keyCount, KeyLocality keyLocality, HazelcastInstance hz) {
        int keyLength = (int) (prefix.length() + Math.ceil(Math.log10(keyCount))) + 2;
        return generateStringKeys(prefix, keyCount, keyLength, keyLocality, hz);
    }

    /**
     * Generates an array of string keys with a configurable keyLocality.
     *
     * If the hz is a client, keyLocality is ignored.
     *
     * @param prefix      prefix for the generated keys
     * @param keyCount    the number of keys in the array
     * @param keyLength   the length of each string key
     * @param keyLocality if the key is local/remote/random
     * @param hz          the HazelcastInstance that is used for keyLocality
     * @return the created array of keys
     */
    public static String[] generateStringKeys(String prefix, int keyCount, int keyLength, KeyLocality keyLocality,
                                              HazelcastInstance hz) {


        String[] keys = new String[keyCount];
        KeyGenerator keyGenerator = newStringKeyGenerator(hz, keyLocality, keyCount, keyLength, prefix);
        for (int i = 0; i < keys.length; i++) {
            keys[i] = keyGenerator.next();
        }

        return keys;
    }

    interface KeyGenerator {
        K next();
    }

    abstract static class BalancedKeyGenerator implements KeyGenerator {

        protected final Random random = new Random();
        protected final HazelcastInstance hz;
        protected final int keyCount;

        private final Set[] keysPerPartition;
        private final PartitionService partitionService;
        private final int maxKeysPerPartition;
        private final KeyLocality keyLocality;

        @SuppressWarnings("unchecked")
        BalancedKeyGenerator(HazelcastInstance hz, KeyLocality keyLocality, int keyCount) {
            this.hz = hz;
            this.keyLocality = keyLocality;
            this.keyCount = keyCount;

            this.partitionService = hz.getPartitionService();

            Set targetPartitions = getTargetPartitions();
            this.maxKeysPerPartition = (int) Math.ceil(keyCount / (float) targetPartitions.size());

            int partitionCount = partitionService.getPartitions().size();
            this.keysPerPartition = new Set[partitionCount];
            for (Integer partitionId : targetPartitions) {
                keysPerPartition[partitionId] = new HashSet<>();
            }
        }

        @Override
        public final K next() {
            for (; ; ) {
                K key = generateKey();

                Partition partition = partitionService.getPartition(key);

                Set keys = keysPerPartition[partition.getPartitionId()];
                if (keys == null) {
                    continue;
                }

                if (keys.contains(key)) {
                    continue;
                }

                if (keys.size() == maxKeysPerPartition) {
                    continue;
                }

                keys.add(key);
                return key;
            }
        }

        protected abstract K generateKey();

        private Set getTargetPartitions() {
            Set targetPartitions = new HashSet<>();
            Member localMember = getLocalMember(hz);

            switch (keyLocality) {
                case LOCAL:
                    for (Partition partition : partitionService.getPartitions()) {
                        if (localMember == null || localMember.equals(partition.getOwner())) {
                            targetPartitions.add(partition.getPartitionId());
                        }
                    }
                    break;
                case REMOTE:
                    for (Partition partition : partitionService.getPartitions()) {
                        if (localMember == null || !localMember.equals(partition.getOwner())) {
                            targetPartitions.add(partition.getPartitionId());
                        }
                    }
                    break;
                case RANDOM:
                    for (Partition partition : partitionService.getPartitions()) {
                        targetPartitions.add(partition.getPartitionId());
                    }
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported keyLocality: " + keyLocality);
            }

            verifyHasPartitions(targetPartitions);

            return targetPartitions;
        }

        private void verifyHasPartitions(Set targetPartitions) {
            if (targetPartitions.isEmpty()) {
                Map partitionsPerMember = new HashMap<>();
                for (Partition partition : partitionService.getPartitions()) {
                    Member owner = partition.getOwner();
                    if (owner == null) {
                        throw new IllegalStateException("Owner is null for partition: " + partition);
                    }
                    Integer value = partitionsPerMember.get(owner);
                    Integer result = value == null ? 1 : value + 1;
                    partitionsPerMember.put(owner, result);
                }
                throw new IllegalStateException("No partitions found, partitionsPerMember: " + partitionsPerMember);
            }
        }

        private Member getLocalMember(HazelcastInstance hz) {
            try {
                return hz.getCluster().getLocalMember();
            } catch (UnsupportedOperationException ignore) {
                // clients throw UnsupportedOperationExceptions
                return null;
            }
        }
    }

    private static final class SharedIntKeyGenerator implements KeyGenerator {

        private int current;

        @Override
        public Integer next() {
            int value = current;
            current++;
            return value;
        }
    }

    private static final class SinglePartitionIntKeyGenerator implements KeyGenerator {

        @Override
        public Integer next() {
            return 0;
        }
    }

    private static final class BalancedIntKeyGenerator extends BalancedKeyGenerator {

        private BalancedIntKeyGenerator(HazelcastInstance hz, KeyLocality keyLocality, int keyCount) {
            super(hz, keyLocality, keyCount);
        }

        @Override
        protected Integer generateKey() {
            return random.nextInt(Integer.MAX_VALUE);
        }
    }

    private static final class BalancedStringKeyGenerator extends BalancedKeyGenerator {

        private final int keyLength;
        private final String prefix;

        private BalancedStringKeyGenerator(
                HazelcastInstance hz, KeyLocality keyLocality, int keyCount, int keyLength, String prefix) {
            super(hz, keyLocality, keyCount);
            this.keyLength = keyLength;
            this.prefix = prefix;
        }

        @Override
        protected String generateKey() {
            if (prefix.length() == 0) {
                return generateAsciiString(keyLength);
            } else {
                return prefix + generateAsciiString(keyLength - prefix.length());
            }
        }
    }

    private static final class SinglePartitionStringKeyGenerator implements KeyGenerator {

        private final String key;

        SinglePartitionStringKeyGenerator(int keyLength, String prefix) {
            key = padWithZero(new StringBuilder(prefix), keyLength).toString();
        }

        @Override
        public String next() {
            return key;
        }
    }

    private static StringBuilder padWithZero(StringBuilder sb, int length) {
        int count = length - sb.length();

        for (int i = 0; i < count; i++) {
            sb.append('0');
        }
        return sb;
    }

    private static final class SharedStringKeyGenerator implements KeyGenerator {

        private int current;
        private final int keyLength;
        private final String prefix;

        SharedStringKeyGenerator(int keyLength, String prefix) {
            this.keyLength = keyLength;
            this.prefix = prefix;
        }

        @Override
        public String next() {
            int id = current;
            current++;

            int x = keyLength - prefix.length();
            return prefix + format("%0" + x + "d", id);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy