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

com.hazelcast.internal.partition.impl.InternalPartitionImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2024, 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.internal.partition.impl;

import com.hazelcast.internal.partition.AbstractInternalPartition;
import com.hazelcast.internal.partition.InternalPartition;
import com.hazelcast.internal.partition.PartitionReplica;
import com.hazelcast.internal.partition.PartitionReplicaInterceptor;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import static java.util.Arrays.copyOf;

public class InternalPartitionImpl extends AbstractInternalPartition implements InternalPartition {

    @SuppressFBWarnings(value = "VO_VOLATILE_REFERENCE_TO_ARRAY", justification =
            "The contents of this array will never be updated, so it can be safely read using a volatile read."
                    + " Writing to `replicas` is done under InternalPartitionServiceImpl.lock,"
                    + " so there's no need to guard `replicas` field or to use a CAS.")
    private volatile PartitionReplica[] replicas = new PartitionReplica[MAX_REPLICA_COUNT];
    private final PartitionReplicaInterceptor interceptor;
    private volatile int version;
    private volatile PartitionReplica localReplica;
    private volatile boolean isMigrating;

    InternalPartitionImpl(int partitionId, PartitionReplica localReplica, PartitionReplicaInterceptor interceptor) {
        super(partitionId);
        this.localReplica = localReplica;
        this.interceptor = interceptor;
    }

    @SuppressFBWarnings("EI_EXPOSE_REP")
    public InternalPartitionImpl(int partitionId, PartitionReplica localReplica, PartitionReplica[] replicas, int version,
            PartitionReplicaInterceptor interceptor) {
        this(partitionId, localReplica, interceptor);
        this.replicas = replicas;
        this.version = version;
    }

    @Override
    public boolean isMigrating() {
        return isMigrating;
    }

    /**
     * Sets migrating flag if it's not set already.
     * @return true if migrating flag is updated, false otherwise
     */
    public boolean setMigrating() {
        if (isMigrating) {
            return false;
        }
        isMigrating = true;
        return true;
    }

    /**
     * Resets migrating flag.
     */
    public void resetMigrating() {
        isMigrating = false;
    }

    @Override
    public boolean isLocal() {
        PartitionReplica local = localReplica;
        return local != null && local.equals(getOwnerReplicaOrNull());
    }

    @Override
    public int version() {
        return version;
    }

    @Override
    public PartitionReplica getReplica(int replicaIndex) {
        return replicas[replicaIndex];
    }

    /** Swaps the replicas for {@code index1} and {@code index2} and call the partition listeners */
    void swapReplicas(int index1, int index2) {
        PartitionReplica[] newReplicas = copyOf(replicas, MAX_REPLICA_COUNT);

        PartitionReplica a1 = newReplicas[index1];
        PartitionReplica a2 = newReplicas[index2];
        newReplicas[index1] = a2;
        newReplicas[index2] = a1;

        replicas = newReplicas;
        onReplicaChange(index1, a1, a2);
        onReplicaChange(index2, a2, a1);
    }

    /**
     * This method is always called from batch partition update situations, so it does
     * not invoke interceptors individually per partition.
     * (apply partition assignments from master while joining, apply partition table recovered
     * from hot restart)
     * @return {@code true} if partition owner was changed, otherwise {@code false}.
     */
    boolean setReplicasAndVersion(InternalPartition partition) {
        boolean ownerChanged = setReplicas(partition.getReplicasCopy(), false);
        version = partition.version();
        return ownerChanged;
    }

    void setVersion(int version) {
        this.version = version;
    }

    // Not doing a defensive copy of given Address[]
    // This method is called under InternalPartitionServiceImpl.lock,
    // so there's no need to guard `addresses` field or to use a CAS.
    void setReplicas(PartitionReplica[] newReplicas) {
        PartitionReplica[] oldReplicas = replicas;
        replicas = newReplicas;
        onReplicasChange(newReplicas, oldReplicas);
    }

    /**
     * Variant of {@link #setReplicas(PartitionReplica[])} that's suitable for batch partition replica updates.
     *
     * @return {@code true} if partition owner was changed, otherwise {@code false}.
     */
    boolean setReplicas(PartitionReplica[] newReplicas, boolean invokeInterceptor) {
        PartitionReplica[] oldReplicas = replicas;
        replicas = newReplicas;
        return onReplicasChange(newReplicas, oldReplicas, invokeInterceptor);
    }

    void setReplica(int replicaIndex, PartitionReplica newReplica) {
        PartitionReplica[] newReplicas = copyOf(replicas, MAX_REPLICA_COUNT);
        PartitionReplica oldReplica = newReplicas[replicaIndex];
        newReplicas[replicaIndex] = newReplica;
        replicas = newReplicas;
        onReplicaChange(replicaIndex, oldReplica, newReplica);
    }

    /**
     * Calls the partition replica change interceptor for all changed replicas.
     * @return {@code true} if partition owner change was detected, otherwise {@code false}.
     */
    private boolean onReplicasChange(PartitionReplica[] newReplicas, PartitionReplica[] oldReplicas) {
        return onReplicasChange(newReplicas, oldReplicas, true);
    }

    private boolean onReplicasChange(PartitionReplica[] newReplicas, PartitionReplica[] oldReplicas, boolean invokeInterceptor) {
        PartitionReplica oldReplicasOwner = oldReplicas[0];
        PartitionReplica newReplicasOwner = newReplicas[0];
        boolean partitionOwnerChanged = onReplicaChange(0, oldReplicasOwner, newReplicasOwner, invokeInterceptor);

        for (int replicaIndex = 1; replicaIndex < MAX_REPLICA_COUNT; replicaIndex++) {
            PartitionReplica oldReplicasId = oldReplicas[replicaIndex];
            PartitionReplica newReplicasId = newReplicas[replicaIndex];
            onReplicaChange(replicaIndex, oldReplicasId, newReplicasId, invokeInterceptor);
        }
        return partitionOwnerChanged;
    }

    /**
     * If a replica change is detected, then increments partition version and calls the partition replica change interceptor
     * for the changed replica.
     * @return {@code true} if a replica change was detected, otherwise {@code false}.
     */
    @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT",
            justification = "This method is called under InternalPartitionServiceImpl.lock")
    private boolean onReplicaChange(int replicaIndex, PartitionReplica oldReplica, PartitionReplica newReplica) {
        return onReplicaChange(replicaIndex, oldReplica, newReplica, true);
    }

    /**
     * Calls the partition replica change interceptor for the changed replica.
     * @return {@code true} if a replica change was detected, otherwise {@code false}.
     */
    @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT",
            justification = "This method is called under InternalPartitionServiceImpl.lock")
    private boolean onReplicaChange(int replicaIndex, PartitionReplica oldReplica, PartitionReplica newReplica,
                                 boolean invokeInterceptor) {
        boolean changed;
        if (oldReplica == null) {
            changed = newReplica != null;
        } else {
            changed = !oldReplica.equals(newReplica);
        }
        if (!changed) {
            return false;
        }
        version++;
        if (interceptor != null && invokeInterceptor) {
            interceptor.replicaChanged(partitionId, replicaIndex, oldReplica, newReplica);
        }
        return true;
    }

    InternalPartitionImpl copy(PartitionReplicaInterceptor interceptor) {
        return new InternalPartitionImpl(partitionId, localReplica, copyOf(replicas, MAX_REPLICA_COUNT), version, interceptor);
    }

    @Override
    protected PartitionReplica[] replicas() {
        return replicas;
    }

    int replaceReplica(PartitionReplica oldReplica, PartitionReplica newReplica) {
        for (int i = 0; i < MAX_REPLICA_COUNT; i++) {
            PartitionReplica currentReplica = replicas[i];
            if (currentReplica == null) {
                break;
            }

            if (currentReplica.equals(oldReplica)) {
                PartitionReplica[] newReplicas = copyOf(replicas, MAX_REPLICA_COUNT);
                newReplicas[i] = newReplica;
                replicas = newReplicas;
                onReplicaChange(i, oldReplica, newReplica);
                return i;
            }
        }
        return -1;
    }

    void reset(PartitionReplica localReplica) {
        assert localReplica != null;
        this.replicas = new PartitionReplica[MAX_REPLICA_COUNT];
        this.localReplica = localReplica;
        version = 0;
        resetMigrating();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy