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

com.hazelcast.executor.impl.ExecutorServiceProxy Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show 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.executor.impl;

import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.ICompletableFuture;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberSelector;
import com.hazelcast.core.MultiExecutionCallback;
import com.hazelcast.core.PartitionAware;
import com.hazelcast.executor.impl.operations.CallableTaskOperation;
import com.hazelcast.executor.impl.operations.MemberCallableTaskOperation;
import com.hazelcast.executor.impl.operations.ShutdownOperation;
import com.hazelcast.instance.MemberImpl;
import com.hazelcast.logging.ILogger;
import com.hazelcast.monitor.LocalExecutorStats;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.AbstractDistributedObject;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.InternalCompletableFuture;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.OperationService;
import com.hazelcast.util.Clock;
import com.hazelcast.util.executor.CompletedFuture;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.logging.Level;

import static com.hazelcast.util.FutureUtil.ExceptionHandler;
import static com.hazelcast.util.FutureUtil.logAllExceptions;
import static com.hazelcast.util.FutureUtil.waitWithDeadline;
import static com.hazelcast.util.Preconditions.checkNotNull;
import static com.hazelcast.util.UuidUtil.newUnsecureUuidString;

public class ExecutorServiceProxy
        extends AbstractDistributedObject
        implements IExecutorService {

    public static final int SYNC_FREQUENCY = 100;
    public static final int SYNC_DELAY_MS = 10;

    private static final AtomicIntegerFieldUpdater CONSECUTIVE_SUBMITS = AtomicIntegerFieldUpdater
            .newUpdater(ExecutorServiceProxy.class, "consecutiveSubmits");

    private static final ExceptionHandler WHILE_SHUTDOWN_EXCEPTION_HANDLER =
            logAllExceptions("Exception while ExecutorService shutdown", Level.FINEST);

    private final String name;
    private final Random random = new Random(-System.currentTimeMillis());
    private final int partitionCount;
    private final ILogger logger;

    // This field is never accessed directly but by the CONSECUTIVE_SUBMITS above
    private volatile int consecutiveSubmits;

    private volatile long lastSubmitTime;

    public ExecutorServiceProxy(String name, NodeEngine nodeEngine, DistributedExecutorService service) {
        super(nodeEngine, service);
        this.name = name;
        this.partitionCount = nodeEngine.getPartitionService().getPartitionCount();
        this.logger = nodeEngine.getLogger(ExecutorServiceProxy.class);
        getLocalExecutorStats();
    }

    @Override
    public void execute(Runnable command, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        executeOnMember(command, members.get(selectedMember));
    }

    @Override
    public void executeOnMembers(Runnable command, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        executeOnMembers(command, members);
    }

    @Override
    public  Future submit(Callable task, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        return submitToMember(task, members.get(selectedMember));
    }

    @Override
    public  Map> submitToMembers(Callable task, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        return submitToMembers(task, members);
    }

    @Override
    public void submit(Runnable task, MemberSelector memberSelector, ExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        submitToMember(task, members.get(selectedMember), callback);
    }

    @Override
    public void submitToMembers(Runnable task, MemberSelector memberSelector, MultiExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        submitToMembers(task, members, callback);
    }

    @Override
    public  void submit(Callable task, MemberSelector memberSelector, ExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        submitToMember(task, members.get(selectedMember), callback);
    }

    @Override
    public  void submitToMembers(Callable task, MemberSelector memberSelector, MultiExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        submitToMembers(task, members, callback);
    }

    @Override
    public void execute(Runnable command) {
        Callable callable = createRunnableAdapter(command);
        submit(callable);
    }

    private  RunnableAdapter createRunnableAdapter(Runnable command) {
        checkNotNull(command, "Command can't be null");

        return new RunnableAdapter(command);
    }

    @Override
    public void executeOnKeyOwner(Runnable command, Object key) {
        Callable callable = createRunnableAdapter(command);
        submitToKeyOwner(callable, key);
    }

    @Override
    public void executeOnMember(Runnable command, Member member) {
        Callable callable = createRunnableAdapter(command);
        submitToMember(callable, member);
    }

    @Override
    public void executeOnMembers(Runnable command, Collection members) {
        Callable callable = createRunnableAdapter(command);
        submitToMembers(callable, members);
    }

    @Override
    public void executeOnAllMembers(Runnable command) {
        Callable callable = createRunnableAdapter(command);
        submitToAllMembers(callable);
    }

    @Override
    public Future submit(Runnable task) {
        Callable callable = createRunnableAdapter(task);
        return submit(callable);
    }

    @Override
    public  Future submit(Runnable task, T result) {
        checkNotNull(task, "task can't be null");
        checkNotShutdown();

        NodeEngine nodeEngine = getNodeEngine();
        Callable callable = createRunnableAdapter(task);
        Data callableData = nodeEngine.toData(callable);
        String uuid = newUnsecureUuidString();
        int partitionId = getTaskPartitionId(callable);

        CallableTaskOperation op = new CallableTaskOperation(name, uuid, callableData);
        ICompletableFuture future = invoke(partitionId, op);
        boolean sync = checkSync();
        if (sync) {
            try {
                future.get();
            } catch (Exception exception) {
                logger.warning(exception);
            }
            return new CompletedFuture(nodeEngine.getSerializationService(), result, getAsyncExecutor());
        }
        return new CancellableDelegatingFuture(future, result, nodeEngine, uuid, partitionId);
    }

    private void checkNotShutdown() {
        if (isShutdown()) {
            throw new RejectedExecutionException(getRejectionMessage());
        }
    }

    private InternalCompletableFuture invoke(int partitionId, CallableTaskOperation op) {
        NodeEngine nodeEngine = getNodeEngine();
        OperationService operationService = nodeEngine.getOperationService();
        return operationService.invokeOnPartition(DistributedExecutorService.SERVICE_NAME, op, partitionId);
    }

    @Override
    public  Future submit(Callable task) {
        final int partitionId = getTaskPartitionId(task);
        return submitToPartitionOwner(task, partitionId, false);
    }

    private  Future submitToPartitionOwner(Callable task, int partitionId, boolean preventSync) {
        checkNotNull(task, "task can't be null");
        checkNotShutdown();

        NodeEngine nodeEngine = getNodeEngine();
        Data taskData = nodeEngine.toData(task);
        String uuid = newUnsecureUuidString();

        boolean sync = !preventSync && checkSync();
        CallableTaskOperation op = new CallableTaskOperation(name, uuid, taskData);
        ICompletableFuture future = invoke(partitionId, op);
        if (sync) {
            Object response;
            try {
                response = future.get();
            } catch (Exception e) {
                response = e;
            }
            return new CompletedFuture(nodeEngine.getSerializationService(), response, getAsyncExecutor());
        }
        return new CancellableDelegatingFuture(future, nodeEngine, uuid, partitionId);
    }

    /**
     * This is a hack to prevent overloading the system with unprocessed tasks. Once backpressure is added, this can
     * be removed.
     */
    private boolean checkSync() {
        boolean sync = false;
        long last = lastSubmitTime;
        long now = Clock.currentTimeMillis();
        if (last + SYNC_DELAY_MS < now) {
            CONSECUTIVE_SUBMITS.set(this, 0);
        } else if (CONSECUTIVE_SUBMITS.incrementAndGet(this) % SYNC_FREQUENCY == 0) {
            sync = true;
        }
        lastSubmitTime = now;
        return sync;
    }

    private  int getTaskPartitionId(Callable task) {
        if (task instanceof PartitionAware) {
            Object partitionKey = ((PartitionAware) task).getPartitionKey();
            if (partitionKey != null) {
                return getNodeEngine().getPartitionService().getPartitionId(partitionKey);
            }
        }
        return random.nextInt(partitionCount);
    }

    @Override
    public  Future submitToKeyOwner(Callable task, Object key) {
        NodeEngine nodeEngine = getNodeEngine();
        return submitToPartitionOwner(task, nodeEngine.getPartitionService().getPartitionId(key), false);
    }

    @Override
    public  Future submitToMember(Callable task, Member member) {
        checkNotNull(task, "task can't be null");
        checkNotShutdown();

        NodeEngine nodeEngine = getNodeEngine();
        Data taskData = nodeEngine.toData(task);
        String uuid = newUnsecureUuidString();
        Address target = ((MemberImpl) member).getAddress();

        boolean sync = checkSync();
        MemberCallableTaskOperation op = new MemberCallableTaskOperation(name, uuid, taskData);
        InternalCompletableFuture future = nodeEngine.getOperationService()
                .invokeOnTarget(DistributedExecutorService.SERVICE_NAME, op, target);
        if (sync) {
            Object response;
            try {
                response = future.get();
            } catch (Exception e) {
                response = e;
            }
            return new CompletedFuture(nodeEngine.getSerializationService(), response, getAsyncExecutor());
        }
        return new CancellableDelegatingFuture(future, nodeEngine, uuid, target);
    }

    @Override
    public  Map> submitToMembers(Callable task, Collection members) {
        Map> futures = new HashMap>(members.size());
        for (Member member : members) {
            futures.put(member, submitToMember(task, member));
        }
        return futures;
    }

    @Override
    public  Map> submitToAllMembers(Callable task) {
        NodeEngine nodeEngine = getNodeEngine();
        return submitToMembers(task, nodeEngine.getClusterService().getMembers());
    }

    @Override
    public void submit(Runnable task, ExecutionCallback callback) {
        Callable callable = createRunnableAdapter(task);
        submit(callable, callback);
    }

    @Override
    public void submitToKeyOwner(Runnable task, Object key, ExecutionCallback callback) {
        Callable callable = createRunnableAdapter(task);
        submitToKeyOwner(callable, key, callback);
    }

    @Override
    public void submitToMember(Runnable task, Member member, ExecutionCallback callback) {
        Callable callable = createRunnableAdapter(task);
        submitToMember(callable, member, callback);
    }

    @Override
    public void submitToMembers(Runnable task, Collection members, MultiExecutionCallback callback) {
        Callable callable = createRunnableAdapter(task);
        submitToMembers(callable, members, callback);
    }

    @Override
    public void submitToAllMembers(Runnable task, MultiExecutionCallback callback) {
        Callable callable = createRunnableAdapter(task);
        submitToAllMembers(callable, callback);
    }

    private  void submitToPartitionOwner(Callable task, ExecutionCallback callback, int partitionId) {
        checkNotShutdown();

        NodeEngine nodeEngine = getNodeEngine();
        Data taskData = nodeEngine.toData(task);
        CallableTaskOperation op = new CallableTaskOperation(name, null, taskData);
        OperationService operationService = nodeEngine.getOperationService();
        operationService.createInvocationBuilder(DistributedExecutorService.SERVICE_NAME, op, partitionId)
                .setExecutionCallback((ExecutionCallback) callback).invoke();
    }

    @Override
    public  void submit(Callable task, ExecutionCallback callback) {
        int partitionId = getTaskPartitionId(task);
        submitToPartitionOwner(task, callback, partitionId);
    }

    @Override
    public  void submitToKeyOwner(Callable task, Object key, ExecutionCallback callback) {
        NodeEngine nodeEngine = getNodeEngine();
        submitToPartitionOwner(task, callback, nodeEngine.getPartitionService().getPartitionId(key));
    }

    @Override
    public  void submitToMember(Callable task, Member member, ExecutionCallback callback) {
        checkNotShutdown();

        NodeEngine nodeEngine = getNodeEngine();
        Data taskData = nodeEngine.toData(task);
        String uuid = newUnsecureUuidString();
        MemberCallableTaskOperation op = new MemberCallableTaskOperation(name, uuid, taskData);
        OperationService operationService = nodeEngine.getOperationService();
        Address address = ((MemberImpl) member).getAddress();
        operationService
                .createInvocationBuilder(DistributedExecutorService.SERVICE_NAME, op, address)
                .setExecutionCallback((ExecutionCallback) callback).invoke();
    }

    private String getRejectionMessage() {
        return "ExecutorService[" + name + "] is shutdown! In order to create a new ExecutorService with name '" + name
                + "', you need to destroy current ExecutorService first!";
    }

    @Override
    public  void submitToMembers(Callable task, Collection members, MultiExecutionCallback callback) {
        NodeEngine nodeEngine = getNodeEngine();
        ExecutionCallbackAdapterFactory executionCallbackFactory = new ExecutionCallbackAdapterFactory(
                nodeEngine.getLogger(ExecutionCallbackAdapterFactory.class), members, callback);

        for (Member member : members) {
            submitToMember(task, member, executionCallbackFactory.callbackFor(member));
        }
    }

    @Override
    public  void submitToAllMembers(Callable task, MultiExecutionCallback callback) {
        NodeEngine nodeEngine = getNodeEngine();
        submitToMembers(task, nodeEngine.getClusterService().getMembers(), callback);
    }

    @Override
    public  List> invokeAll(Collection> tasks)
            throws InterruptedException {
        List> futures = new ArrayList>(tasks.size());
        List> result = new ArrayList>(tasks.size());
        for (Callable task : tasks) {
            futures.add(submit(task));
        }
        for (Future future : futures) {
            Object value;
            try {
                value = future.get();
            } catch (ExecutionException e) {
                value = e;
            }
            result.add(new CompletedFuture(getNodeEngine().getSerializationService(), value, getAsyncExecutor()));
        }
        return result;
    }

    @Override
    public  List> invokeAll(Collection> tasks, long timeout, TimeUnit unit)
            throws InterruptedException {
        checkNotNull(unit, "unit must not be null");
        checkNotNull(tasks, "tasks must not be null");

        long timeoutNanos = unit.toNanos(timeout);
        List> futures = new ArrayList>(tasks.size());
        boolean done = false;
        try {
            for (Callable task : tasks) {
                long start = System.nanoTime();
                int partitionId = getTaskPartitionId(task);
                futures.add(submitToPartitionOwner(task, partitionId, true));
                timeoutNanos -= System.nanoTime() - start;
            }
            if (timeoutNanos <= 0L) {
                return futures;
            }

            done = wait(timeoutNanos, futures);
            return futures;
        } catch (Throwable t) {
            logger.severe(t);
            // todo: should an exception not be thrown?
            return futures;
        } finally {
            if (!done) {
                cancelAll(futures);
            }
        }
    }

    private  boolean wait(long timeoutNanos, List> futures) throws InterruptedException {
        boolean done = true;
        for (int i = 0, size = futures.size(); i < size; i++) {
            long start = System.nanoTime();
            Object value;
            try {
                Future future = futures.get(i);
                value = future.get(timeoutNanos, TimeUnit.NANOSECONDS);
            } catch (ExecutionException e) {
                value = e;
            } catch (TimeoutException e) {
                done = false;
                for (int l = i; l < size; l++) {
                    Future f = futures.get(i);
                    if (f.isDone()) {
                        Object v;
                        try {
                            v = f.get();
                        } catch (ExecutionException ex) {
                            v = ex;
                        }
                        futures.set(l, new CompletedFuture(getNodeEngine().getSerializationService(), v, getAsyncExecutor()));
                    }
                }
                break;
            }

            futures.set(i, new CompletedFuture(getNodeEngine().getSerializationService(), value, getAsyncExecutor()));
            timeoutNanos -= System.nanoTime() - start;
        }
        return done;
    }

    private static  void cancelAll(List> result) {
        for (Future aResult : result) {
            aResult.cancel(true);
        }
    }

    @Override
    public  T invokeAny(Collection> tasks)
            throws InterruptedException, ExecutionException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  T invokeAny(Collection> tasks, long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        throw new UnsupportedOperationException();
    }

    @Override
    protected void throwNotActiveException() {
        throw new RejectedExecutionException();
    }

    @Override
    public boolean isShutdown() {
        try {
            return getService().isShutdown(name);
        } catch (HazelcastInstanceNotActiveException e) {
            return true;
        }
    }

    @Override
    public boolean isTerminated() {
        return isShutdown();
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException {
        return false;
    }

    @Override
    public void shutdown() {
        NodeEngine nodeEngine = getNodeEngine();
        Collection members = nodeEngine.getClusterService().getMembers();
        OperationService operationService = nodeEngine.getOperationService();
        Collection calls = new LinkedList();

        for (Member member : members) {
            if (member.localMember()) {
                getService().shutdownExecutor(name);
            } else {
                Future f = submitShutdownOperation(operationService, member);
                calls.add(f);
            }
        }

        waitWithDeadline(calls, 1, TimeUnit.SECONDS, WHILE_SHUTDOWN_EXCEPTION_HANDLER);
    }

    private InternalCompletableFuture submitShutdownOperation(OperationService operationService, Member member) {
        ShutdownOperation op = new ShutdownOperation(name);
        return operationService.invokeOnTarget(getServiceName(), op, member.getAddress());
    }

    @Override
    public List shutdownNow() {
        shutdown();
        return Collections.emptyList();
    }

    @Override
    public LocalExecutorStats getLocalExecutorStats() {
        return getService().getLocalExecutorStats(name);
    }

    @Override
    public String getServiceName() {
        return DistributedExecutorService.SERVICE_NAME;
    }

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

    private ExecutorService getAsyncExecutor() {
        return getNodeEngine().getExecutionService().getExecutor(ExecutionService.ASYNC_EXECUTOR);
    }

    private List selectMembers(MemberSelector memberSelector) {
        if (memberSelector == null) {
            throw new IllegalArgumentException("memberSelector must not be null");
        }
        List selected = new ArrayList();
        Collection members = getNodeEngine().getClusterService().getMembers();
        for (Member member : members) {
            if (memberSelector.select(member)) {
                selected.add(member);
            }
        }
        if (selected.isEmpty()) {
            throw new RejectedExecutionException("No member selected with memberSelector[" + memberSelector + "]");
        }
        return selected;
    }

    @Override
    public String toString() {
        return "IExecutorService{" + "name='" + name + '\'' + '}';
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy