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

com.hazelcast.replicatedmap.impl.iterator.ReplicatedMapIterationService 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.replicatedmap.impl.iterator;

import com.hazelcast.internal.iteration.IterationResult;
import com.hazelcast.internal.iteration.IteratorWithCursor;
import com.hazelcast.internal.iteration.IteratorWithCursorManager;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.internal.serialization.SerializationService;
import com.hazelcast.replicatedmap.impl.ReplicatedMapService;
import com.hazelcast.replicatedmap.impl.record.ReplicatedMapEntryViewHolder;
import com.hazelcast.replicatedmap.impl.record.ReplicatedRecord;
import com.hazelcast.replicatedmap.impl.record.ReplicatedRecordStore;
import com.hazelcast.spi.impl.NodeEngine;
import com.hazelcast.spi.properties.HazelcastProperties;
import com.hazelcast.spi.properties.HazelcastProperty;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class ReplicatedMapIterationService {
    /**
     * ReplicatedMap EntryView iteration creates resources in the server via creating snapshots. These snapshots are kept until
     * the timeout expires or the iteration is complete. This property configures the period of the task that checks for stale
     * iterators. The timeout for a iterator to be considered stale is managed via another property:
     * {@link ReplicatedMapIterationService#ITERATOR_CLEANUP_TIMEOUT_MILLIS}.
     */
    public static final HazelcastProperty ITERATOR_CLEANUP_PERIOD_SECONDS = new HazelcastProperty(
            "hazelcast.replicatedmap.iterator.cleanup.period.seconds", 30, TimeUnit.SECONDS);
    /**
     * The timeout in milliseconds for cleaning up stale iterators. The task that checks for stale iterators runs every
     * {@link ReplicatedMapIterationService#ITERATOR_CLEANUP_PERIOD_SECONDS} seconds.
     */
    public static final HazelcastProperty ITERATOR_CLEANUP_TIMEOUT_MILLIS = new HazelcastProperty(
            "hazelcast.replicatedmap.iterator.cleanup.timeout.millis", 300_000, TimeUnit.MILLISECONDS);
    private final ReplicatedMapService replicatedMapService;
    private final SerializationService serializationService;
    private final IteratorWithCursorManager iteratorManager;
    private final HazelcastProperties properties;
    private final NodeEngine nodeEngine;
    private final AtomicBoolean noIteratorCreated = new AtomicBoolean(true);
    private ScheduledFuture iteratorCleanupFuture;
    public ReplicatedMapIterationService(ReplicatedMapService replicatedMapService, SerializationService serializationService,
                                         NodeEngine nodeEngine) {
        this.replicatedMapService = replicatedMapService;
        this.serializationService = serializationService;
        this.properties = nodeEngine.getProperties();
        this.iteratorManager = new IteratorWithCursorManager<>(replicatedMapService.getNodeEngine());
        this.nodeEngine = nodeEngine;
    }

    /**
     * If the partition owner changes during iteration, the iteration will fail
     * with a {@link IllegalStateException} stating that there is no iteration with
     * the provided cursor id because this method will be called on a different member.
     */
    public void createIterator(String name, int partitionId, UUID cursorId) {
        if (noIteratorCreated.getAndSet(false)) {
            // little optimization to create the future upon first iterator creation.
            this.iteratorCleanupFuture = nodeEngine.getExecutionService().getGlobalTaskScheduler().scheduleWithRepetition(
                    this::removeStaleIterators, 0,
                    nodeEngine.getProperties().getInteger(ITERATOR_CLEANUP_PERIOD_SECONDS), TimeUnit.SECONDS);
        }
        ReplicatedRecordStore store = this.replicatedMapService.getReplicatedRecordStore(name, false, partitionId);
        if (store == null) {
            throw new IllegalStateException("There is no ReplicatedRecordStore for " + name + " on partitionId "
                    + partitionId + " on member " + replicatedMapService.getNodeEngine().getThisAddress() + ".");
        }
        iteratorManager.createIterator(store.recordIterator(), cursorId);
    }

    public IterationResult iterate(UUID cursorId, int maxCount) {
        IterationResult result = iteratorManager.iterate(cursorId, maxCount);
        List page = result.getPage();
        return new IterationResult<>(convertToHolder(page), result.getCursorId(), result.getCursorIdToForget());
    }

    private List convertToHolder(List page) {
        if (page.isEmpty()) {
            return Collections.emptyList();
        }
        List result = new ArrayList<>(page.size());
        for (ReplicatedRecord record : page) {
            result.add(toEntryViewHolder(record));
        }
        return result;
    }

    public void removeStaleIterators() {
        ConcurrentHashMap.KeySetView> keySetView = iteratorManager.getKeySet();
        for (UUID iteratorId : keySetView) {
            IteratorWithCursor paginator = iteratorManager.getIterator(iteratorId);
            if (paginator.getLastAccessTime()
                    < System.currentTimeMillis() - properties.getLong(ITERATOR_CLEANUP_TIMEOUT_MILLIS)) {
                keySetView.remove(iteratorId);
            }
        }
    }

    public IteratorWithCursorManager getIteratorManager() {
        return iteratorManager;
    }

    private ReplicatedMapEntryViewHolder toEntryViewHolder(ReplicatedRecord record) {
        return new ReplicatedMapEntryViewHolder(toData(record.getKey()), toData(record.getValue()), record.getCreationTime(),
                record.getHits(), record.getLastAccessTime(), record.getUpdateTime(), record.getTtlMillis());
    }

    private Data toData(Object object) {
        return serializationService.toData(object);
    }

    public void shutdown() {
        if (iteratorCleanupFuture != null) {
            iteratorCleanupFuture.cancel(true);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy