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

com.hazelcast.crdt.pncounter.PNCounterService Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2018, 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.crdt.pncounter;

import com.hazelcast.config.Config;
import com.hazelcast.config.PNCounterConfig;
import com.hazelcast.internal.util.Memoizer;
import com.hazelcast.crdt.CRDTReplicationAwareService;
import com.hazelcast.crdt.CRDTReplicationContainer;
import com.hazelcast.crdt.MutationDisallowedException;
import com.hazelcast.cluster.impl.VectorClock;
import com.hazelcast.monitor.LocalPNCounterStats;
import com.hazelcast.monitor.impl.LocalPNCounterStatsImpl;
import com.hazelcast.spi.ManagedService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.QuorumAwareService;
import com.hazelcast.spi.RemoteService;
import com.hazelcast.spi.StatisticsAwareService;
import com.hazelcast.util.ConstructorFunction;
import com.hazelcast.util.UuidUtil;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.hazelcast.util.ConcurrencyUtil.getOrPutSynchronized;

/**
 * Service responsible for {@link PNCounter} proxies and replication operation.
 */
public class PNCounterService implements
        ManagedService,
        RemoteService,
        CRDTReplicationAwareService,
        QuorumAwareService,
        StatisticsAwareService {
    /** The name under which this service is registered */
    public static final String SERVICE_NAME = "hz:impl:PNCounterService";

    /** Map from counter name to counter implementations */
    private final ConcurrentMap counters = new ConcurrentHashMap();

    /** Constructor function for counter implementations */
    private final ConstructorFunction counterConstructorFn =
            new ConstructorFunction() {
                @Override
                public PNCounterImpl createNew(String name) {
                    if (isShuttingDown) {
                        throw new MutationDisallowedException("Cannot create a new PN counter named " + name
                                + " because this instance is shutting down!");
                    }
                    return new PNCounterImpl(UuidUtil.newUnsecureUuidString(), name);
                }
            };

    /** Cache for quorum config names */
    private final Memoizer quorumConfigCache = new Memoizer(
            new ConstructorFunction() {
                @Override
                public Object createNew(String name) {
                    final PNCounterConfig counterConfig = nodeEngine.getConfig().findPNCounterConfig(name);
                    final String quorumName = counterConfig.getQuorumName();
                    return quorumName == null ? Memoizer.NULL_OBJECT : quorumName;
                }
            });

    /** Map from PN counter name to counter statistics */
    private final ConcurrentMap statsMap
            = new ConcurrentHashMap();
    /** Unmodifiable statistics map to return from {@link #getStats()} */
    private Map unmodifiableStatsMap = Collections.unmodifiableMap(statsMap);

    /** Constructor function for PN counter statistics */
    private final ConstructorFunction statsConstructorFunction =
            new ConstructorFunction() {
                public LocalPNCounterStatsImpl createNew(String name) {
                    return new LocalPNCounterStatsImpl();
                }
            };

    /** Mutex for creating new PN counters and for marking the service as shutting down */
    private final Object newCounterCreationMutex = new Object();

    /**
     * Flag marking this service as shutting down. New PN counters must not be
     * added if this is {@code true}
     */
    private volatile boolean isShuttingDown;

    private NodeEngine nodeEngine;

    /**
     * Returns the counter with the given {@code name}.
     */
    public PNCounterImpl getCounter(String name) {
        return getOrPutSynchronized(counters, name, newCounterCreationMutex, counterConstructorFn);
    }

    /**
     * Returns {@code true} if the CRDT state for the PN counter with the
     * given {@code name} is present on this node.
     *
     * @param name the PN counter name
     */
    public boolean containsCounter(String name) {
        return counters.containsKey(name);
    }

    /** Returns the PN counter statistics for the counter with the given {@code name} */
    public LocalPNCounterStatsImpl getLocalPNCounterStats(String name) {
        return getOrPutSynchronized(statsMap, name, statsMap, statsConstructorFunction);
    }

    @Override
    public void init(NodeEngine nodeEngine, Properties properties) {
        this.nodeEngine = nodeEngine;
    }

    @Override
    public void reset() {
    }

    @Override
    public void shutdown(boolean terminate) {
        counters.clear();
        statsMap.clear();
    }

    @Override
    public PNCounterProxy createDistributedObject(String objectName) {
        return new PNCounterProxy(objectName, nodeEngine, this);
    }

    @Override
    public void destroyDistributedObject(String objectName) {
        counters.remove(objectName);
        statsMap.remove(objectName);
        quorumConfigCache.remove(objectName);
    }

    @Override
    public CRDTReplicationContainer prepareReplicationOperation(
            Map previouslyReplicatedVectorClocks, int targetIndex) {
        final HashMap currentVectorClocks = new HashMap();
        final HashMap counters = new HashMap();
        final Config config = nodeEngine.getConfig();

        for (Entry counterEntry : this.counters.entrySet()) {
            final String counterName = counterEntry.getKey();
            final PNCounterImpl counter = counterEntry.getValue();
            if (targetIndex >= config.findPNCounterConfig(counterName).getReplicaCount()) {
                continue;
            }
            final VectorClock counterCurrentVectorClock = counter.getCurrentVectorClock();
            final VectorClock counterPreviousVectorClock = previouslyReplicatedVectorClocks.get(counterName);

            if (counterPreviousVectorClock == null || counterCurrentVectorClock.isAfter(counterPreviousVectorClock)) {
                counters.put(counterName, counter);
            }
            currentVectorClocks.put(counterName, counterCurrentVectorClock);
        }

        return counters.isEmpty()
                ? null
                : new CRDTReplicationContainer(new PNCounterReplicationOperation(counters), currentVectorClocks);
    }

    @Override
    public String getName() {
        return SERVICE_NAME;
    }

    @Override
    public void merge(String name, PNCounterImpl value) {
        getCounter(name).merge(value);
    }

    @Override
    public CRDTReplicationContainer prepareMigrationOperation(int maxConfiguredReplicaCount) {
        final HashMap currentVectorClocks = new HashMap();
        final HashMap counters = new HashMap();
        final Config config = nodeEngine.getConfig();

        for (Entry counterEntry : this.counters.entrySet()) {
            final String counterName = counterEntry.getKey();
            final PNCounterImpl counter = counterEntry.getValue();
            if (config.findPNCounterConfig(counterName).getReplicaCount() >= maxConfiguredReplicaCount) {
                continue;
            }
            counters.put(counterName, counter);
            currentVectorClocks.put(counterName, counter.getCurrentVectorClock());
        }

        return counters.isEmpty()
                ? null
                : new CRDTReplicationContainer(new PNCounterReplicationOperation(counters), currentVectorClocks);
    }

    @Override
    public boolean clearCRDTState(Map vectorClocks) {
        boolean allCleared = true;
        for (Entry vectorClockEntry : vectorClocks.entrySet()) {
            final String counterName = vectorClockEntry.getKey();
            final VectorClock vectorClock = vectorClockEntry.getValue();
            final PNCounterImpl counter = counters.get(counterName);
            if (counter == null) {
                continue;
            }
            if (counter.markMigrated(vectorClock)) {
                counters.remove(counterName);
            } else {
                allCleared = false;
            }
        }
        return allCleared;
    }

    @Override
    public void prepareToSafeShutdown() {
        synchronized (newCounterCreationMutex) {
            isShuttingDown = true;
        }
        for (PNCounterImpl counter : this.counters.values()) {
            counter.markMigrated();
        }
    }

    @Override
    public String getQuorumName(String name) {
        return (String) quorumConfigCache.getOrCalculate(name);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map getStats() {
        return unmodifiableStatsMap;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy