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

com.hazelcast.jet.sql.impl.schema.MappingStorage Maven / Gradle / Ivy

/*
 * Copyright 2021 Hazelcast Inc.
 *
 * Licensed under the Hazelcast Community License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://hazelcast.com/hazelcast-community-license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.jet.sql.impl.schema;

import com.hazelcast.cluster.Address;
import com.hazelcast.cluster.Member;
import com.hazelcast.cluster.memberselector.MemberSelectors;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.logging.ILogger;
import com.hazelcast.map.MapEvent;
import com.hazelcast.replicatedmap.ReplicatedMap;
import com.hazelcast.replicatedmap.impl.ReplicatedMapService;
import com.hazelcast.replicatedmap.impl.operation.GetOperation;
import com.hazelcast.spi.impl.NodeEngine;
import com.hazelcast.spi.impl.operationservice.Operation;
import com.hazelcast.spi.impl.operationservice.OperationService;
import com.hazelcast.sql.impl.schema.Mapping;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

public class MappingStorage {

    private static final String CATALOG_MAP_NAME = "__sql.catalog";

    private static final int MAX_CHECK_ATTEMPTS = 5;
    private static final long SLEEP_MILLIS = 100;

    private final NodeEngine nodeEngine;
    private final ILogger logger;

    public MappingStorage(NodeEngine nodeEngine) {
        this.nodeEngine = nodeEngine;
        this.logger = nodeEngine.getLogger(getClass());
    }

    void put(String name, Mapping mapping) {
        storage().put(name, mapping);
        awaitMappingOnAllMembers(name, mapping);
    }

    boolean putIfAbsent(String name, Mapping mapping) {
        Object previous = storage().putIfAbsent(name, mapping);
        awaitMappingOnAllMembers(name, mapping);
        return previous == null;
    }

    /**
     * Temporary measure to ensure schema is propagated to all the members.
     */
    @SuppressWarnings("BusyWait")
    private void awaitMappingOnAllMembers(String name, Mapping mapping) {
        Data keyData = nodeEngine.getSerializationService().toData(name);
        int keyPartitionId = nodeEngine.getPartitionService().getPartitionId(keyData);
        OperationService operationService = nodeEngine.getOperationService();

        Collection
memberAddresses = getMemberAddresses(); for (int i = 0; i < MAX_CHECK_ATTEMPTS && !memberAddresses.isEmpty(); i++) { List> futures = memberAddresses.stream() .map(memberAddress -> { Operation operation = new GetOperation(CATALOG_MAP_NAME, keyData) .setPartitionId(keyPartitionId) .setValidateTarget(false); return operationService .createInvocationBuilder(ReplicatedMapService.SERVICE_NAME, operation, memberAddress) .setTryCount(1) .invoke() .toCompletableFuture() .thenApply(result -> Objects.equals(mapping, result) ? memberAddress : null); }).collect(toList()); for (CompletableFuture
future : futures) { try { memberAddresses.remove(future.join()); } catch (Exception e) { logger.warning("Error occurred while trying to fetch mapping: " + e.getMessage(), e); } } if (!memberAddresses.isEmpty()) { try { Thread.sleep(SLEEP_MILLIS); } catch (InterruptedException e) { break; } } } } private Collection
getMemberAddresses() { return nodeEngine.getClusterService().getMembers(MemberSelectors.DATA_MEMBER_SELECTOR).stream() .filter(member -> !member.localMember() && !member.isLiteMember()) .map(Member::getAddress) .collect(toSet()); } Collection values() { return storage().values(); } Mapping remove(String name) { return storage().remove(name); } void registerListener(EntryListenerAdapter listener) { // do not try to implicitly create ReplicatedMap // TODO: perform this check in a single place i.e. SqlService ? if (!nodeEngine.getLocalMember().isLiteMember()) { storage().addEntryListener(listener); } } private ReplicatedMap storage() { return nodeEngine.getHazelcastInstance().getReplicatedMap(CATALOG_MAP_NAME); } abstract static class EntryListenerAdapter implements EntryListener { @Override public final void entryAdded(EntryEvent event) { } @Override public abstract void entryUpdated(EntryEvent event); @Override public abstract void entryRemoved(EntryEvent event); @Override public final void entryEvicted(EntryEvent event) { throw new UnsupportedOperationException("SQL catalog entries must never be evicted - " + event); } @Override public void entryExpired(EntryEvent event) { throw new UnsupportedOperationException("SQL catalog entries must never be expired - " + event); } @Override public final void mapCleared(MapEvent event) { throw new UnsupportedOperationException("SQL catalog must never be cleared - " + event); } @Override public final void mapEvicted(MapEvent event) { throw new UnsupportedOperationException("SQL catalog must never be evicted - " + event); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy