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

net.spy.memcached.BulkService Maven / Gradle / Ivy

The newest version!
/*
 * arcus-java-client : Arcus Java client
 * Copyright 2010-2014 NAVER Corp.
 * Copyright 2014-2020 JaM2in Co., Ltd.
 *
 * 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 net.spy.memcached;

import net.spy.memcached.collection.CollectionResponse;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.internal.BasicThreadFactory;
import net.spy.memcached.ops.CollectionOperationStatus;
import net.spy.memcached.ops.StoreType;
import net.spy.memcached.transcoders.Transcoder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

class BulkService extends SpyObject {

  private static int DEFAULT_LOOP_LIMIT;
  private final ExecutorService executor;
  private final long singleOpTimeout;

  BulkService(int loopLimit, int threadCount, long singleOpTimeout) {
    this.executor = new ThreadPoolExecutor(threadCount, threadCount, 60L,
            TimeUnit.MILLISECONDS, new LinkedBlockingQueue(),
            new BasicThreadFactory("bulk-service", true),
            new ThreadPoolExecutor.AbortPolicy());
    BulkService.DEFAULT_LOOP_LIMIT = loopLimit;
    this.singleOpTimeout = singleOpTimeout;
  }

   Future> setBulk(
          List keys, int exp, T value, Transcoder transcoder,
          ArcusClient[] client) {
    assert !executor.isShutdown() : "Pool has already shut down.";
    BulkSetWorker w = new BulkSetWorker(keys, exp, value, transcoder,
            singleOpTimeout, client);
    BulkService.Task> task =
            new BulkService.Task>(w);
    executor.submit(task);
    return task;
  }

   Future> setBulk(
          Map o, int exp, Transcoder transcoder,
          ArcusClient[] client) {
    assert !executor.isShutdown() : "Pool has already shut down.";
    BulkSetWorker w = new BulkSetWorker(o, exp, transcoder,
            singleOpTimeout, client);
    BulkService.Task> task =
            new BulkService.Task>(w);
    executor.submit(task);
    return task;
  }

   Future> deleteBulk(
          List keys, ArcusClient[] client) {
    assert !executor.isShutdown() : "Pool has already shut down.";
    BulkDeleteWorker w = new BulkDeleteWorker(keys, singleOpTimeout, client);
    BulkService.Task> task =
            new BulkService.Task>(w);
    executor.submit(task);
    return task;
  }

  void shutdown() {
    try {
      executor.shutdown();
    } catch (Exception e) {
      getLogger().warn("exception while shutting down bulk set service.",
              e);
    }
  }

  private static class Task extends FutureTask {
    private final BulkWorker worker;

    public Task(Callable callable) {
      super(callable);
      this.worker = (BulkWorker) callable;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
      return worker.cancel() && super.cancel(mayInterruptIfRunning);
    }
  }

  /**
   * Bulk operation worker
   */
  private abstract static class BulkWorker extends SpyObject implements
          Callable {

    protected final ArcusClient[] clientList;
    protected final ArrayList> future;
    protected final long operationTimeout;
    protected final AtomicBoolean isRunnable = new AtomicBoolean(true);
    protected T errorList = null;

    protected final int keyCount;

    public BulkWorker(Collection keys, long timeout, ArcusClient[] clientList) {
      this.future = new ArrayList>(keys.size());
      this.operationTimeout = timeout;
      this.clientList = clientList;

      keyCount = keys.size();
    }

    public boolean cancel() {
      if (!isRunnable()) {
        return false;
      }

      isRunnable.set(false);

      boolean ret = true;

      for (Future f : future) {
        if (f == null) {
          continue;
        }
        if (f.isCancelled() || f.isDone()) {
          continue;
        }
        ret &= f.cancel(true);

        if (getLogger().isDebugEnabled()) {
          getLogger().debug("Cancel the future. " + f);
        }
      }
      return ret;
    }

    protected boolean isRunnable() {
      return isRunnable.get() && !Thread.currentThread().isInterrupted();
    }

    protected abstract Future processItem(int index);

    protected abstract void awaitProcessResult(int index);

    public T call() throws Exception {
      int numActiveOperations = 0;
      int posResponseReceived = 0;

      for (int pos = 0; isRunnable() && pos < keyCount; pos++) {
        try {
          if (isRunnable()) {
            future.add(pos, processItem(pos));
          }
        } catch (IllegalStateException e) {
          if (Thread.currentThread().isInterrupted()) {
            break;
          } else {
            throw e;
          }
        }
        numActiveOperations++;

        if (numActiveOperations >= DEFAULT_LOOP_LIMIT) {
          awaitProcessResult(posResponseReceived);
          posResponseReceived++;
          numActiveOperations--;
        }
      }

      while (numActiveOperations > 0) {
        awaitProcessResult(posResponseReceived);
        posResponseReceived++;
        numActiveOperations--;
      }
      return errorList;
    }
  }

  /**
   * Bulk set operation worker
   */
  private static class BulkSetWorker extends BulkWorker> {
    private final List keys;
    private final int exp;
    private final int cntCos;
    private List cos;

    public BulkSetWorker(List keys, int exp, T value,
                         Transcoder transcoder, long timeout, ArcusClient[] clientList) {
      super(keys, timeout, clientList);
      this.keys = keys;
      this.exp = exp;
      this.cos = new ArrayList();
      this.cos.add(transcoder.encode(value));
      this.cntCos = 1;
      this.errorList = new HashMap();
    }

    public BulkSetWorker(Map o, int exp,
                         Transcoder transcoder, long timeout, ArcusClient[] clientList) {

      super(o.keySet(), timeout, clientList);

      this.keys = new ArrayList(o.keySet());
      this.exp = exp;

      this.cos = new ArrayList();
      for (String key : keys) {
        this.cos.add(transcoder.encode(o.get(key)));
      }
      this.cntCos = this.cos.size();
      this.errorList = new HashMap();
    }

    @Override
    public Future processItem(int index) {
      return clientList[index % clientList.length].asyncStore(
              StoreType.set, keys.get(index), exp,
              (this.cntCos > 1 ? cos.get(index) : cos.get(0)));
    }

    @Override
    public void awaitProcessResult(int index) {
      try {
        boolean success = future.get(index).get(operationTimeout,
                TimeUnit.MILLISECONDS);
        if (!success) {
          errorList.put(
                  keys.get(index),
                  new CollectionOperationStatus(false, String
                          .valueOf(success), CollectionResponse.END));
        }
      } catch (Exception e) {
        future.get(index).cancel(true);
        errorList.put(keys.get(index), new CollectionOperationStatus(
                false, e.getMessage(), CollectionResponse.EXCEPTION));
      }
    }
  }

  /**
   * Bulk delete operation worker
   */
  private static class BulkDeleteWorker
      extends BulkWorker> {
    private final List keys;

    public BulkDeleteWorker(List keys, long timeout, ArcusClient[] clientList) {
      super(keys, timeout, clientList);
      this.keys = keys;
      this.errorList = new HashMap();
    }

    @Override
    public Future processItem(int index) {
      return clientList[index % clientList.length].delete(keys.get(index));
    }

    @Override
    public void awaitProcessResult(int index) {
      try {
        boolean success = future.get(index).get(operationTimeout,
                TimeUnit.MILLISECONDS);
        if (!success) {
          errorList.put(
                  keys.get(index),
                  new CollectionOperationStatus(false, String
                          .valueOf(success), CollectionResponse.NOT_FOUND));
        }
      } catch (Exception e) {
        future.get(index).cancel(true);
        errorList.put(keys.get(index), new CollectionOperationStatus(
                false, e.getMessage(), CollectionResponse.EXCEPTION));
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy