/**
   * Records deploy event.
   *
   * @param cls Deployed class.
   * @param clsLdr Class loader.
   * @param recordEvt Flag indicating whether to record events.
   */
  @SuppressWarnings({"unchecked"})
  private void recordDeployFailed(Class<?> cls, ClassLoader clsLdr, boolean recordEvt) {
    assert cls != null;
    assert clsLdr != null;

    boolean isTask = isTask(cls);

    String msg =
        "Failed to deploy "
            + (isTask ? "task" : "class")
            + " [cls="
            + cls
            + ", clsLdr="
            + clsLdr
            + ']';

    if (recordEvt
        && ctx.event().isRecordable(isTask ? EVT_CLASS_DEPLOY_FAILED : EVT_TASK_DEPLOY_FAILED)) {
      String taskName = isTask ? U.getTaskName((Class<? extends GridTask<?, ?>>) cls) : null;

      GridDeploymentEvent evt = new GridDeploymentEvent();

      evt.message(msg);
      evt.nodeId(ctx.localNodeId());
      evt.type(isTask(cls) ? EVT_CLASS_DEPLOY_FAILED : EVT_TASK_DEPLOY_FAILED);
      evt.alias(taskName);

      ctx.event().record(evt);
    }

    if (log.isInfoEnabled()) {
      log.info(msg);
    }
  }
  /**
   * Method cleans up all events that either outnumber queue size or exceeds time-to-live value. It
   * does none if someone else cleans up queue (lock is locked) or if there are queue readers
   * (readersNum > 0).
   */
  private void cleanupQueue() {
    long now = U.currentTimeMillis();

    long queueOversize = evts.sizex() - expireCnt;

    for (int i = 0; i < queueOversize && evts.sizex() > expireCnt; i++) {
      GridEvent expired = evts.poll();

      if (log.isDebugEnabled()) log.debug("Event expired by count: " + expired);
    }

    while (true) {
      ConcurrentLinkedDeque8.Node<GridEvent> node = evts.peekx();

      if (node == null) // Queue is empty.
      break;

      GridEvent evt = node.item();

      if (evt == null) // Competing with another thread.
      continue;

      if (now - evt.timestamp() < expireAgeMs) break;

      if (evts.unlinkx(node) && log.isDebugEnabled())
        log.debug("Event expired by age: " + node.item());
    }
  }
  /** {@inheritDoc} */
  @Override
  public void start() throws GridException {
    ctxLdr = U.detectClassLoader(getClass());

    spi.setListener(new LocalDeploymentListener());

    if (log.isDebugEnabled()) {
      log.debug(startInfo());
    }
  }
예제 #4
0
  /**
   * Flushes every internal buffer if buffer was flushed before passed in threshold.
   *
   * <p>Does not wait for result and does not fail on errors assuming that this method should be
   * called periodically.
   */
  @Override
  public void tryFlush() throws GridInterruptedException {
    if (!busyLock.enterBusy()) return;

    try {
      for (Buffer buf : bufMappings.values()) buf.flush();

      lastFlushTime = U.currentTimeMillis();
    } finally {
      leaveBusy();
    }
  }
  /**
   * Adds new participant to deployment.
   *
   * @param dep Shared deployment.
   * @param meta Request metadata.
   * @return {@code True} if participant was added.
   */
  private boolean addParticipant(SharedDeployment dep, GridDeploymentMetadata meta) {
    assert dep != null;
    assert meta != null;

    assert Thread.holdsLock(mux);

    if (!checkModeMatch(dep, meta)) return false;

    if (meta.participants() != null) {
      for (Map.Entry<UUID, GridTuple2<GridUuid, Long>> e : meta.participants().entrySet()) {
        dep.addParticipant(e.getKey(), e.getValue().get1(), e.getValue().get2());

        if (log.isDebugEnabled())
          log.debug(
              "Added new participant [nodeId="
                  + e.getKey()
                  + ", clsLdrId="
                  + e.getValue().get1()
                  + ", seqNum="
                  + e.getValue().get2()
                  + ']');
      }
    }

    if (dep.deployMode() == CONTINUOUS || meta.participants() == null) {
      if (!dep.addParticipant(meta.senderNodeId(), meta.classLoaderId(), meta.sequenceNumber())) {
        U.warn(
            log,
            "Failed to create shared mode deployment "
                + "(requested class loader was already undeployed, did sender node leave grid?) "
                + "[clsLdrId="
                + meta.classLoaderId()
                + ", senderNodeId="
                + meta.senderNodeId()
                + ']');

        return false;
      }

      if (log.isDebugEnabled())
        log.debug(
            "Added new participant [nodeId="
                + meta.senderNodeId()
                + ", clsLdrId="
                + meta.classLoaderId()
                + ", seqNum="
                + meta.sequenceNumber()
                + ']');
    }

    return true;
  }
예제 #6
0
    void cancelAll() {
      GridException err =
          new GridException("Data loader has been cancelled: " + GridDataLoaderImpl.this);

      for (GridFuture<?> f : locFuts) {
        try {
          f.cancel();
        } catch (GridException e) {
          U.error(log, "Failed to cancel mini-future.", e);
        }
      }

      for (GridFutureAdapter<?> f : reqs.values()) f.onDone(err);
    }
  /**
   * Records deploy event.
   *
   * @param cls Deployed class.
   * @param alias Class alias.
   * @param recordEvt Flag indicating whether to record events.
   */
  private void recordDeploy(Class<?> cls, String alias, boolean recordEvt) {
    assert cls != null;

    boolean isTask = isTask(cls);

    String msg = (isTask ? "Task" : "Class") + " locally deployed: " + cls;

    if (recordEvt && ctx.event().isRecordable(isTask ? EVT_TASK_DEPLOYED : EVT_CLASS_DEPLOYED)) {
      GridDeploymentEvent evt = new GridDeploymentEvent();

      evt.message(msg);
      evt.nodeId(ctx.localNodeId());
      evt.type(isTask ? EVT_TASK_DEPLOYED : EVT_CLASS_DEPLOYED);
      evt.alias(alias);

      ctx.event().record(evt);
    }

    // Don't record JDK or Grid classes.
    if (U.isGrid(cls) || U.isJdk(cls)) return;

    if (log.isInfoEnabled()) log.info(msg);
  }
예제 #8
0
    /** {@inheritDoc} */
    @Override
    public ClassLoader classLoader() {
      if (ldr == null) {
        ClassLoader ldr0 = deployClass().getClassLoader();

        // Safety.
        if (ldr0 == null) ldr0 = U.gridClassLoader();

        assert ldr0 != null : "Failed to detect classloader [objs=" + objs + ']';

        ldr = ldr0;
      }

      return ldr;
    }
예제 #9
0
    /** {@inheritDoc} */
    @Override
    public Class<?> deployClass() {
      if (cls == null) {
        Class<?> cls0 = null;

        if (depCls != null) cls0 = depCls;
        else {
          for (Iterator<Object> it = objs.iterator();
              (cls0 == null || U.isJdk(cls0)) && it.hasNext(); ) {
            Object o = it.next();

            if (o != null) cls0 = U.detectClass(o);
          }

          if (cls0 == null || U.isJdk(cls0)) cls0 = GridDataLoaderImpl.class;
        }

        assert cls0 != null : "Failed to detect deploy class [objs=" + objs + ']';

        cls = cls0;
      }

      return cls;
    }
  /**
   * Checks if deployment modes match.
   *
   * @param dep Shared deployment.
   * @param meta Request metadata.
   * @return {@code True} if shared deployment modes match.
   */
  private boolean checkModeMatch(GridDeploymentInfo dep, GridDeploymentMetadata meta) {
    if (dep.deployMode() != meta.deploymentMode()) {
      U.warn(
          log,
          "Received invalid deployment mode (will not deploy, make sure that all nodes "
              + "executing the same classes in shared mode have identical GridDeploymentMode parameter) [mode="
              + meta.deploymentMode()
              + ", expected="
              + dep.deployMode()
              + ']');

      return false;
    }

    return true;
  }
예제 #11
0
  /** {@inheritDoc} */
  @Override
  public void isolated(boolean isolated) throws GridException {
    if (isolated()) return;

    GridNode node = F.first(ctx.grid().forCache(cacheName).nodes());

    if (node == null) throw new GridException("Failed to get node for cache: " + cacheName);

    GridCacheAttributes a = U.cacheAttributes(node, cacheName);

    assert a != null;

    updater =
        a.atomicityMode() == GridCacheAtomicityMode.ATOMIC
            ? GridDataLoadCacheUpdaters.<K, V>batched()
            : GridDataLoadCacheUpdaters.<K, V>groupLocked();
  }
예제 #12
0
    /** @param res Response. */
    void onResponse(GridDataLoadResponse res) {
      if (log.isDebugEnabled()) log.debug("Received data load response: " + res);

      GridFutureAdapter<?> f = reqs.remove(res.requestId());

      if (f == null) {
        if (log.isDebugEnabled())
          log.debug("Future for request has not been found: " + res.requestId());

        return;
      }

      Throwable err = null;

      byte[] errBytes = res.errorBytes();

      if (errBytes != null) {
        try {
          GridPeerDeployAware jobPda0 = jobPda;

          err =
              ctx.config()
                  .getMarshaller()
                  .unmarshal(
                      errBytes, jobPda0 != null ? jobPda0.classLoader() : U.gridClassLoader());
        } catch (GridException e) {
          f.onDone(null, new GridException("Failed to unmarshal response.", e));

          return;
        }
      }

      f.onDone(null, err);

      if (log.isDebugEnabled())
        log.debug(
            "Finished future [fut=" + f + ", reqId=" + res.requestId() + ", err=" + err + ']');
    }
예제 #13
0
 /**
  * Increments active tasks count.
  *
  * @throws GridInterruptedException If thread has been interrupted.
  */
 private void incrementActiveTasks() throws GridInterruptedException {
   U.acquire(sem);
 }
예제 #14
0
 /** {@inheritDoc} */
 @Override
 public long getDelay(TimeUnit unit) {
   return unit.convert(nextFlushTime() - U.currentTimeMillis(), TimeUnit.MILLISECONDS);
 }
  /** {@inheritDoc} */
  @Override
  public void start() throws GridException {
    ctxLdr = U.detectClassLoader(getClass());

    if (log.isDebugEnabled()) log.debug(startInfo());
  }
예제 #16
0
/** Data loader implementation. */
public class GridDataLoaderImpl<K, V> implements GridDataLoader<K, V>, Delayed {
  /** Cache updater. */
  private GridDataLoadCacheUpdater<K, V> updater = GridDataLoadCacheUpdaters.individual();

  /** */
  private byte[] updaterBytes;

  /** Max remap count before issuing an error. */
  private static final int MAX_REMAP_CNT = 32;

  /** Log reference. */
  private static final AtomicReference<GridLogger> logRef = new AtomicReference<>();

  /** Cache name ({@code null} for default cache). */
  private final String cacheName;

  /** Per-node buffer size. */
  @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
  private int bufSize = DFLT_PER_NODE_BUFFER_SIZE;

  /** */
  private int parallelOps = DFLT_MAX_PARALLEL_OPS;

  /** */
  private long autoFlushFreq;

  /** Mapping. */
  @GridToStringInclude private ConcurrentMap<UUID, Buffer> bufMappings = new ConcurrentHashMap8<>();

  /** Logger. */
  private GridLogger log;

  /** Discovery listener. */
  private final GridLocalEventListener discoLsnr;

  /** Context. */
  private final GridKernalContext ctx;

  /** Communication topic for responses. */
  private final Object topic;

  /** */
  private byte[] topicBytes;

  /** {@code True} if data loader has been cancelled. */
  private volatile boolean cancelled;

  /** Active futures of this data loader. */
  @GridToStringInclude
  private final Collection<GridFuture<?>> activeFuts = new GridConcurrentHashSet<>();

  /** Closure to remove from active futures. */
  @GridToStringExclude
  private final GridInClosure<GridFuture<?>> rmvActiveFut =
      new GridInClosure<GridFuture<?>>() {
        @Override
        public void apply(GridFuture<?> t) {
          boolean rmv = activeFuts.remove(t);

          assert rmv;
        }
      };

  /** Job peer deploy aware. */
  private volatile GridPeerDeployAware jobPda;

  /** Deployment class. */
  private Class<?> depCls;

  /** Future to track loading finish. */
  private final GridFutureAdapter<?> fut;

  /** Busy lock. */
  private final GridSpinBusyLock busyLock = new GridSpinBusyLock();

  /** Closed flag. */
  private final AtomicBoolean closed = new AtomicBoolean();

  /** */
  private volatile long lastFlushTime = U.currentTimeMillis();

  /** */
  private final DelayQueue<GridDataLoaderImpl<K, V>> flushQ;

  /**
   * @param ctx Grid kernal context.
   * @param cacheName Cache name.
   * @param flushQ Flush queue.
   */
  public GridDataLoaderImpl(
      final GridKernalContext ctx,
      @Nullable final String cacheName,
      DelayQueue<GridDataLoaderImpl<K, V>> flushQ) {
    assert ctx != null;

    this.ctx = ctx;
    this.cacheName = cacheName;
    this.flushQ = flushQ;

    log = U.logger(ctx, logRef, GridDataLoaderImpl.class);

    discoLsnr =
        new GridLocalEventListener() {
          @Override
          public void onEvent(GridEvent evt) {
            assert evt.type() == EVT_NODE_FAILED || evt.type() == EVT_NODE_LEFT;

            GridDiscoveryEvent discoEvt = (GridDiscoveryEvent) evt;

            UUID id = discoEvt.eventNodeId();

            // Remap regular mappings.
            final Buffer buf = bufMappings.remove(id);

            if (buf != null) {
              // Only async notification is possible since
              // discovery thread may be trapped otherwise.
              ctx.closure()
                  .callLocalSafe(
                      new Callable<Object>() {
                        @Override
                        public Object call() throws Exception {
                          buf.onNodeLeft();

                          return null;
                        }
                      },
                      true /* system pool */);
            }
          }
        };

    ctx.event().addLocalEventListener(discoLsnr, EVT_NODE_FAILED, EVT_NODE_LEFT);

    // Generate unique topic for this loader.
    topic = TOPIC_DATALOAD.topic(GridUuid.fromUuid(ctx.localNodeId()));

    ctx.io()
        .addMessageListener(
            topic,
            new GridMessageListener() {
              @Override
              public void onMessage(UUID nodeId, Object msg) {
                assert msg instanceof GridDataLoadResponse;

                GridDataLoadResponse res = (GridDataLoadResponse) msg;

                if (log.isDebugEnabled()) log.debug("Received data load response: " + res);

                Buffer buf = bufMappings.get(nodeId);

                if (buf != null) buf.onResponse(res);
                else if (log.isDebugEnabled())
                  log.debug("Ignoring response since node has left [nodeId=" + nodeId + ", ");
              }
            });

    if (log.isDebugEnabled()) log.debug("Added response listener within topic: " + topic);

    fut = new GridDataLoaderFuture(ctx, this);
  }

  /** Enters busy lock. */
  private void enterBusy() {
    if (!busyLock.enterBusy()) throw new IllegalStateException("Data loader has been closed.");
  }

  /** Leaves busy lock. */
  private void leaveBusy() {
    busyLock.leaveBusy();
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> future() {
    return fut;
  }

  /** {@inheritDoc} */
  @Override
  public void deployClass(Class<?> depCls) {
    this.depCls = depCls;
  }

  /** {@inheritDoc} */
  @Override
  public void updater(GridDataLoadCacheUpdater<K, V> updater) {
    A.notNull(updater, "updater");

    this.updater = updater;
  }

  /** {@inheritDoc} */
  @Override
  public boolean isolated() {
    return updater != GridDataLoadCacheUpdaters.individual();
  }

  /** {@inheritDoc} */
  @Override
  public void isolated(boolean isolated) throws GridException {
    if (isolated()) return;

    GridNode node = F.first(ctx.grid().forCache(cacheName).nodes());

    if (node == null) throw new GridException("Failed to get node for cache: " + cacheName);

    GridCacheAttributes a = U.cacheAttributes(node, cacheName);

    assert a != null;

    updater =
        a.atomicityMode() == GridCacheAtomicityMode.ATOMIC
            ? GridDataLoadCacheUpdaters.<K, V>batched()
            : GridDataLoadCacheUpdaters.<K, V>groupLocked();
  }

  /** {@inheritDoc} */
  @Override
  @Nullable
  public String cacheName() {
    return cacheName;
  }

  /** {@inheritDoc} */
  @Override
  public int perNodeBufferSize() {
    return bufSize;
  }

  /** {@inheritDoc} */
  @Override
  public void perNodeBufferSize(int bufSize) {
    A.ensure(bufSize > 0, "bufSize > 0");

    this.bufSize = bufSize;
  }

  /** {@inheritDoc} */
  @Override
  public int perNodeParallelLoadOperations() {
    return parallelOps;
  }

  /** {@inheritDoc} */
  @Override
  public void perNodeParallelLoadOperations(int parallelOps) {
    this.parallelOps = parallelOps;
  }

  /** {@inheritDoc} */
  @Override
  public long autoFlushFrequency() {
    return autoFlushFreq;
  }

  /** {@inheritDoc} */
  @Override
  public void autoFlushFrequency(long autoFlushFreq) {
    A.ensure(autoFlushFreq >= 0, "autoFlushFreq >= 0");

    long old = this.autoFlushFreq;

    if (autoFlushFreq != old) {
      this.autoFlushFreq = autoFlushFreq;

      if (autoFlushFreq != 0 && old == 0) flushQ.add(this);
      else if (autoFlushFreq == 0) flushQ.remove(this);
    }
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> addData(Map<K, V> entries) throws IllegalStateException {
    A.notNull(entries, "entries");

    return addData(entries.entrySet());
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> addData(Collection<? extends Map.Entry<K, V>> entries) {
    A.notEmpty(entries, "entries");

    enterBusy();

    try {
      GridFutureAdapter<Object> resFut = new GridFutureAdapter<>(ctx);

      activeFuts.add(resFut);

      resFut.listenAsync(rmvActiveFut);

      Collection<K> keys = new GridConcurrentHashSet<>(entries.size(), 1.0f, 16);

      for (Map.Entry<K, V> entry : entries) keys.add(entry.getKey());

      load0(entries, resFut, keys, 0);

      return resFut;
    } finally {
      leaveBusy();
    }
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> addData(Map.Entry<K, V> entry) throws GridException, IllegalStateException {
    A.notNull(entry, "entry");

    return addData(F.asList(entry));
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> addData(K key, V val) throws GridException, IllegalStateException {
    A.notNull(key, "key");

    return addData(new Entry0<>(key, val));
  }

  /** {@inheritDoc} */
  @Override
  public GridFuture<?> removeData(K key) throws GridException, IllegalStateException {
    return addData(key, null);
  }

  /**
   * @param entries Entries.
   * @param resFut Result future.
   * @param activeKeys Active keys.
   * @param remaps Remaps count.
   */
  private void load0(
      Collection<? extends Map.Entry<K, V>> entries,
      final GridFutureAdapter<Object> resFut,
      final Collection<K> activeKeys,
      final int remaps) {
    assert entries != null;

    if (remaps >= MAX_REMAP_CNT) {
      resFut.onDone(new GridException("Failed to finish operation (too many remaps): " + remaps));

      return;
    }

    Map<GridNode, Collection<Map.Entry<K, V>>> mappings = new HashMap<>();

    boolean initPda = ctx.deploy().enabled() && jobPda == null;

    for (Map.Entry<K, V> entry : entries) {
      GridNode node;

      try {
        K key = entry.getKey();

        assert key != null;

        if (initPda) {
          jobPda = new DataLoaderPda(key, entry.getValue(), updater);

          initPda = false;
        }

        node = ctx.affinity().mapKeyToNode(cacheName, key);
      } catch (GridException e) {
        resFut.onDone(e);

        return;
      }

      if (node == null) {
        resFut.onDone(
            new GridTopologyException(
                "Failed to map key to node "
                    + "(no nodes with cache found in topology) [infos="
                    + entries.size()
                    + ", cacheName="
                    + cacheName
                    + ']'));

        return;
      }

      Collection<Map.Entry<K, V>> col = mappings.get(node);

      if (col == null) mappings.put(node, col = new ArrayList<>());

      col.add(entry);
    }

    for (final Map.Entry<GridNode, Collection<Map.Entry<K, V>>> e : mappings.entrySet()) {
      final UUID nodeId = e.getKey().id();

      Buffer buf = bufMappings.get(nodeId);

      if (buf == null) {
        Buffer old = bufMappings.putIfAbsent(nodeId, buf = new Buffer(e.getKey()));

        if (old != null) buf = old;
      }

      final Collection<Map.Entry<K, V>> entriesForNode = e.getValue();

      GridInClosure<GridFuture<?>> lsnr =
          new GridInClosure<GridFuture<?>>() {
            @Override
            public void apply(GridFuture<?> t) {
              try {
                t.get();

                for (Map.Entry<K, V> e : entriesForNode) activeKeys.remove(e.getKey());

                if (activeKeys.isEmpty()) resFut.onDone();
              } catch (GridException e1) {
                if (log.isDebugEnabled())
                  log.debug("Future finished with error [nodeId=" + nodeId + ", err=" + e1 + ']');

                if (cancelled) {
                  resFut.onDone(
                      new GridException(
                          "Data loader has been cancelled: " + GridDataLoaderImpl.this, e1));
                } else load0(entriesForNode, resFut, activeKeys, remaps + 1);
              }
            }
          };

      GridFutureAdapter<?> f;

      try {
        f = buf.update(entriesForNode, lsnr);
      } catch (GridInterruptedException e1) {
        resFut.onDone(e1);

        return;
      }

      if (ctx.discovery().node(nodeId) == null) {
        if (bufMappings.remove(nodeId, buf)) buf.onNodeLeft();

        if (f != null)
          f.onDone(
              new GridTopologyException(
                  "Failed to wait for request completion " + "(node has left): " + nodeId));
      }
    }
  }

  /**
   * Performs flush.
   *
   * @throws GridException If failed.
   */
  private void doFlush() throws GridException {
    lastFlushTime = U.currentTimeMillis();

    List<GridFuture> activeFuts0 = null;

    int doneCnt = 0;

    for (GridFuture<?> f : activeFuts) {
      if (!f.isDone()) {
        if (activeFuts0 == null) activeFuts0 = new ArrayList<>((int) (activeFuts.size() * 1.2));

        activeFuts0.add(f);
      } else {
        f.get();

        doneCnt++;
      }
    }

    if (activeFuts0 == null || activeFuts0.isEmpty()) return;

    while (true) {
      Queue<GridFuture<?>> q = null;

      for (Buffer buf : bufMappings.values()) {
        GridFuture<?> flushFut = buf.flush();

        if (flushFut != null) {
          if (q == null) q = new ArrayDeque<>(bufMappings.size() * 2);

          q.add(flushFut);
        }
      }

      if (q != null) {
        assert !q.isEmpty();

        boolean err = false;

        for (GridFuture fut = q.poll(); fut != null; fut = q.poll()) {
          try {
            fut.get();
          } catch (GridException e) {
            if (log.isDebugEnabled()) log.debug("Failed to flush buffer: " + e);

            err = true;
          }
        }

        if (err)
          // Remaps needed - flush buffers.
          continue;
      }

      doneCnt = 0;

      for (int i = 0; i < activeFuts0.size(); i++) {
        GridFuture f = activeFuts0.get(i);

        if (f == null) doneCnt++;
        else if (f.isDone()) {
          f.get();

          doneCnt++;

          activeFuts0.set(i, null);
        } else break;
      }

      if (doneCnt == activeFuts0.size()) return;
    }
  }

  /** {@inheritDoc} */
  @SuppressWarnings("ForLoopReplaceableByForEach")
  @Override
  public void flush() throws GridException {
    enterBusy();

    try {
      doFlush();
    } finally {
      leaveBusy();
    }
  }

  /**
   * Flushes every internal buffer if buffer was flushed before passed in threshold.
   *
   * <p>Does not wait for result and does not fail on errors assuming that this method should be
   * called periodically.
   */
  @Override
  public void tryFlush() throws GridInterruptedException {
    if (!busyLock.enterBusy()) return;

    try {
      for (Buffer buf : bufMappings.values()) buf.flush();

      lastFlushTime = U.currentTimeMillis();
    } finally {
      leaveBusy();
    }
  }

  /**
   * @param cancel {@code True} to close with cancellation.
   * @throws GridException If failed.
   */
  @Override
  public void close(boolean cancel) throws GridException {
    if (!closed.compareAndSet(false, true)) return;

    busyLock.block();

    if (log.isDebugEnabled())
      log.debug("Closing data loader [ldr=" + this + ", cancel=" + cancel + ']');

    GridException e = null;

    try {
      // Assuming that no methods are called on this loader after this method is called.
      if (cancel) {
        cancelled = true;

        for (Buffer buf : bufMappings.values()) buf.cancelAll();
      } else doFlush();

      ctx.event().removeLocalEventListener(discoLsnr);

      ctx.io().removeMessageListener(topic);
    } catch (GridException e0) {
      e = e0;
    }

    fut.onDone(null, e);

    if (e != null) throw e;
  }

  /** @return {@code true} If the loader is closed. */
  boolean isClosed() {
    return fut.isDone();
  }

  /** {@inheritDoc} */
  @Override
  public void close() throws GridException {
    close(false);
  }

  /** {@inheritDoc} */
  @Override
  public String toString() {
    return S.toString(GridDataLoaderImpl.class, this);
  }

  /** {@inheritDoc} */
  @Override
  public long getDelay(TimeUnit unit) {
    return unit.convert(nextFlushTime() - U.currentTimeMillis(), TimeUnit.MILLISECONDS);
  }

  /** @return Next flush time. */
  private long nextFlushTime() {
    return lastFlushTime + autoFlushFreq;
  }

  /** {@inheritDoc} */
  @Override
  public int compareTo(Delayed o) {
    return nextFlushTime() > ((GridDataLoaderImpl) o).nextFlushTime() ? 1 : -1;
  }

  /** */
  private class Buffer {
    /** Node. */
    private final GridNode node;

    /** Active futures. */
    private final Collection<GridFuture<Object>> locFuts;

    /** Buffered entries. */
    private List<Map.Entry<K, V>> entries;

    /** */
    @GridToStringExclude private GridFutureAdapter<Object> curFut;

    /** Local node flag. */
    private final boolean isLocNode;

    /** ID generator. */
    private final AtomicLong idGen = new AtomicLong();

    /** Active futures. */
    private final ConcurrentMap<Long, GridFutureAdapter<Object>> reqs;

    /** */
    private final Semaphore sem;

    /** Closure to signal on task finish. */
    @GridToStringExclude
    private final GridInClosure<GridFuture<Object>> signalC =
        new GridInClosure<GridFuture<Object>>() {
          @Override
          public void apply(GridFuture<Object> t) {
            signalTaskFinished(t);
          }
        };

    /** @param node Node. */
    Buffer(GridNode node) {
      assert node != null;

      this.node = node;

      locFuts = new GridConcurrentHashSet<>();
      reqs = new ConcurrentHashMap8<>();

      // Cache local node flag.
      isLocNode = node.equals(ctx.discovery().localNode());

      entries = newEntries();
      curFut = new GridFutureAdapter<>(ctx);
      curFut.listenAsync(signalC);

      sem = new Semaphore(parallelOps);
    }

    /**
     * @param newEntries Infos.
     * @param lsnr Listener for the operation future.
     * @throws GridInterruptedException If failed.
     * @return Future for operation.
     */
    @Nullable
    GridFutureAdapter<?> update(
        Iterable<Map.Entry<K, V>> newEntries, GridInClosure<GridFuture<?>> lsnr)
        throws GridInterruptedException {
      List<Map.Entry<K, V>> entries0 = null;
      GridFutureAdapter<Object> curFut0;

      synchronized (this) {
        curFut0 = curFut;

        curFut0.listenAsync(lsnr);

        for (Map.Entry<K, V> entry : newEntries) entries.add(entry);

        if (entries.size() >= bufSize) {
          entries0 = entries;

          entries = newEntries();
          curFut = new GridFutureAdapter<>(ctx);
          curFut.listenAsync(signalC);
        }
      }

      if (entries0 != null) {
        submit(entries0, curFut0);

        if (cancelled)
          curFut0.onDone(
              new GridException("Data loader has been cancelled: " + GridDataLoaderImpl.this));
      }

      return curFut0;
    }

    /** @return Fresh collection with some space for outgrowth. */
    private List<Map.Entry<K, V>> newEntries() {
      return new ArrayList<>((int) (bufSize * 1.2));
    }

    /**
     * @return Future if any submitted.
     * @throws GridInterruptedException If thread has been interrupted.
     */
    @Nullable
    GridFuture<?> flush() throws GridInterruptedException {
      List<Map.Entry<K, V>> entries0 = null;
      GridFutureAdapter<Object> curFut0 = null;

      synchronized (this) {
        if (!entries.isEmpty()) {
          entries0 = entries;
          curFut0 = curFut;

          entries = newEntries();
          curFut = new GridFutureAdapter<>(ctx);
          curFut.listenAsync(signalC);
        }
      }

      if (entries0 != null) submit(entries0, curFut0);

      // Create compound future for this flush.
      GridCompoundFuture<Object, Object> res = null;

      for (GridFuture<Object> f : locFuts) {
        if (res == null) res = new GridCompoundFuture<>(ctx);

        res.add(f);
      }

      for (GridFuture<Object> f : reqs.values()) {
        if (res == null) res = new GridCompoundFuture<>(ctx);

        res.add(f);
      }

      if (res != null) res.markInitialized();

      return res;
    }

    /**
     * Increments active tasks count.
     *
     * @throws GridInterruptedException If thread has been interrupted.
     */
    private void incrementActiveTasks() throws GridInterruptedException {
      U.acquire(sem);
    }

    /** @param f Future that finished. */
    private void signalTaskFinished(GridFuture<Object> f) {
      assert f != null;

      sem.release();
    }

    /**
     * @param entries Entries to submit.
     * @param curFut Current future.
     * @throws GridInterruptedException If interrupted.
     */
    private void submit(final List<Map.Entry<K, V>> entries, final GridFutureAdapter<Object> curFut)
        throws GridInterruptedException {
      assert entries != null;
      assert !entries.isEmpty();
      assert curFut != null;

      incrementActiveTasks();

      GridFuture<Object> fut;
      if (isLocNode) {
        fut =
            ctx.closure()
                .callLocalSafe(
                    new GridDataLoadUpdateJob<>(ctx, log, cacheName, entries, false, updater),
                    false);

        locFuts.add(fut);

        fut.listenAsync(
            new GridInClosure<GridFuture<Object>>() {
              @Override
              public void apply(GridFuture<Object> t) {
                try {
                  boolean rmv = locFuts.remove(t);

                  assert rmv;

                  curFut.onDone(t.get());
                } catch (GridException e) {
                  curFut.onDone(e);
                }
              }
            });
      } else {
        byte[] entriesBytes;

        try {
          entriesBytes = ctx.config().getMarshaller().marshal(entries);

          if (updaterBytes == null) {
            assert updater != null;

            updaterBytes = ctx.config().getMarshaller().marshal(updater);
          }

          if (topicBytes == null) topicBytes = ctx.config().getMarshaller().marshal(topic);
        } catch (GridException e) {
          U.error(log, "Failed to marshal (request will not be sent).", e);

          return;
        }

        GridDeployment dep = null;
        GridPeerDeployAware jobPda0 = null;

        if (ctx.deploy().enabled()) {
          try {
            jobPda0 = jobPda;

            assert jobPda0 != null;

            dep = ctx.deploy().deploy(jobPda0.deployClass(), jobPda0.classLoader());
          } catch (GridException e) {
            U.error(
                log,
                "Failed to deploy class (request will not be sent): " + jobPda0.deployClass(),
                e);

            return;
          }

          if (dep == null)
            U.warn(log, "Failed to deploy class (request will be sent): " + jobPda0.deployClass());
        }

        long reqId = idGen.incrementAndGet();

        fut = curFut;

        reqs.put(reqId, (GridFutureAdapter<Object>) fut);

        GridDataLoadRequest<Object, Object> req =
            new GridDataLoadRequest<>(
                reqId,
                topicBytes,
                cacheName,
                updaterBytes,
                entriesBytes,
                true,
                dep != null ? dep.deployMode() : null,
                dep != null ? jobPda0.deployClass().getName() : null,
                dep != null ? dep.userVersion() : null,
                dep != null ? dep.participants() : null,
                dep != null ? dep.classLoaderId() : null,
                dep == null);

        try {
          ctx.io().send(node, TOPIC_DATALOAD, req, PUBLIC_POOL);

          if (log.isDebugEnabled())
            log.debug("Sent request to node [nodeId=" + node.id() + ", req=" + req + ']');
        } catch (GridException e) {
          if (ctx.discovery().alive(node) && ctx.discovery().pingNode(node.id()))
            ((GridFutureAdapter<Object>) fut).onDone(e);
          else
            ((GridFutureAdapter<Object>) fut)
                .onDone(
                    new GridTopologyException(
                        "Failed to send " + "request (node has left): " + node.id()));
        }
      }
    }

    /** */
    void onNodeLeft() {
      assert !isLocNode;
      assert bufMappings.get(node.id()) != this;

      if (log.isDebugEnabled())
        log.debug("Forcibly completing futures (node has left): " + node.id());

      Exception e =
          new GridTopologyException(
              "Failed to wait for request completion " + "(node has left): " + node.id());

      for (GridFutureAdapter<Object> f : reqs.values()) f.onDone(e);

      // Make sure to complete current future.
      GridFutureAdapter<Object> curFut0;

      synchronized (this) {
        curFut0 = curFut;
      }

      curFut0.onDone(e);
    }

    /** @param res Response. */
    void onResponse(GridDataLoadResponse res) {
      if (log.isDebugEnabled()) log.debug("Received data load response: " + res);

      GridFutureAdapter<?> f = reqs.remove(res.requestId());

      if (f == null) {
        if (log.isDebugEnabled())
          log.debug("Future for request has not been found: " + res.requestId());

        return;
      }

      Throwable err = null;

      byte[] errBytes = res.errorBytes();

      if (errBytes != null) {
        try {
          GridPeerDeployAware jobPda0 = jobPda;

          err =
              ctx.config()
                  .getMarshaller()
                  .unmarshal(
                      errBytes, jobPda0 != null ? jobPda0.classLoader() : U.gridClassLoader());
        } catch (GridException e) {
          f.onDone(null, new GridException("Failed to unmarshal response.", e));

          return;
        }
      }

      f.onDone(null, err);

      if (log.isDebugEnabled())
        log.debug(
            "Finished future [fut=" + f + ", reqId=" + res.requestId() + ", err=" + err + ']');
    }

    /** */
    void cancelAll() {
      GridException err =
          new GridException("Data loader has been cancelled: " + GridDataLoaderImpl.this);

      for (GridFuture<?> f : locFuts) {
        try {
          f.cancel();
        } catch (GridException e) {
          U.error(log, "Failed to cancel mini-future.", e);
        }
      }

      for (GridFutureAdapter<?> f : reqs.values()) f.onDone(err);
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
      int size;

      synchronized (this) {
        size = entries.size();
      }

      return S.toString(
          Buffer.class,
          this,
          "entriesCnt",
          size,
          "locFutsSize",
          locFuts.size(),
          "reqsSize",
          reqs.size());
    }
  }

  /** Data loader peer-deploy aware. */
  private class DataLoaderPda implements GridPeerDeployAware {
    /** Deploy class. */
    private Class<?> cls;

    /** Class loader. */
    private ClassLoader ldr;

    /** Collection of objects to detect deploy class and class loader. */
    private Collection<Object> objs;

    /**
     * Constructs data loader peer-deploy aware.
     *
     * @param objs Collection of objects to detect deploy class and class loader.
     */
    private DataLoaderPda(Object... objs) {
      this.objs = Arrays.asList(objs);
    }

    /** {@inheritDoc} */
    @Override
    public Class<?> deployClass() {
      if (cls == null) {
        Class<?> cls0 = null;

        if (depCls != null) cls0 = depCls;
        else {
          for (Iterator<Object> it = objs.iterator();
              (cls0 == null || U.isJdk(cls0)) && it.hasNext(); ) {
            Object o = it.next();

            if (o != null) cls0 = U.detectClass(o);
          }

          if (cls0 == null || U.isJdk(cls0)) cls0 = GridDataLoaderImpl.class;
        }

        assert cls0 != null : "Failed to detect deploy class [objs=" + objs + ']';

        cls = cls0;
      }

      return cls;
    }

    /** {@inheritDoc} */
    @Override
    public ClassLoader classLoader() {
      if (ldr == null) {
        ClassLoader ldr0 = deployClass().getClassLoader();

        // Safety.
        if (ldr0 == null) ldr0 = U.gridClassLoader();

        assert ldr0 != null : "Failed to detect classloader [objs=" + objs + ']';

        ldr = ldr0;
      }

      return ldr;
    }
  }

  /** Entry. */
  private static class Entry0<K, V> implements Map.Entry<K, V>, Externalizable {
    /** */
    private K key;

    /** */
    private V val;

    /**
     * @param key Key.
     * @param val Value.
     */
    private Entry0(K key, @Nullable V val) {
      assert key != null;

      this.key = key;
      this.val = val;
    }

    /** For {@link Externalizable}. */
    @SuppressWarnings("UnusedDeclaration")
    public Entry0() {
      // No-op.
    }

    /** {@inheritDoc} */
    @Override
    public K getKey() {
      return key;
    }

    /** {@inheritDoc} */
    @Override
    public V getValue() {
      return val;
    }

    /** {@inheritDoc} */
    @Override
    public V setValue(V val) {
      throw new UnsupportedOperationException();
    }

    /** {@inheritDoc} */
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
      out.writeObject(key);
      out.writeObject(val);
    }

    /** {@inheritDoc} */
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
      key = (K) in.readObject();
      val = (V) in.readObject();
    }
  }
}
예제 #17
0
  /**
   * Performs flush.
   *
   * @throws GridException If failed.
   */
  private void doFlush() throws GridException {
    lastFlushTime = U.currentTimeMillis();

    List<GridFuture> activeFuts0 = null;

    int doneCnt = 0;

    for (GridFuture<?> f : activeFuts) {
      if (!f.isDone()) {
        if (activeFuts0 == null) activeFuts0 = new ArrayList<>((int) (activeFuts.size() * 1.2));

        activeFuts0.add(f);
      } else {
        f.get();

        doneCnt++;
      }
    }

    if (activeFuts0 == null || activeFuts0.isEmpty()) return;

    while (true) {
      Queue<GridFuture<?>> q = null;

      for (Buffer buf : bufMappings.values()) {
        GridFuture<?> flushFut = buf.flush();

        if (flushFut != null) {
          if (q == null) q = new ArrayDeque<>(bufMappings.size() * 2);

          q.add(flushFut);
        }
      }

      if (q != null) {
        assert !q.isEmpty();

        boolean err = false;

        for (GridFuture fut = q.poll(); fut != null; fut = q.poll()) {
          try {
            fut.get();
          } catch (GridException e) {
            if (log.isDebugEnabled()) log.debug("Failed to flush buffer: " + e);

            err = true;
          }
        }

        if (err)
          // Remaps needed - flush buffers.
          continue;
      }

      doneCnt = 0;

      for (int i = 0; i < activeFuts0.size(); i++) {
        GridFuture f = activeFuts0.get(i);

        if (f == null) doneCnt++;
        else if (f.isDone()) {
          f.get();

          doneCnt++;

          activeFuts0.set(i, null);
        } else break;
      }

      if (doneCnt == activeFuts0.size()) return;
    }
  }
  /**
   * @param depMode Deployment mode.
   * @param ldr Class loader to deploy.
   * @param cls Class.
   * @param alias Class alias.
   * @return Deployment.
   */
  @SuppressWarnings({"ConstantConditions"})
  private GridDeployment deploy(
      GridDeploymentMode depMode, ClassLoader ldr, Class<?> cls, String alias) {
    assert Thread.holdsLock(mux);

    LinkedList<GridDeployment> cachedDeps = null;

    GridDeployment dep = null;

    // Find existing class loader info.
    for (LinkedList<GridDeployment> deps : cache.values()) {
      for (GridDeployment d : deps) {
        if (d.classLoader() == ldr) {
          // Cache class and alias.
          d.addDeployedClass(cls, alias);

          cachedDeps = deps;

          dep = d;

          break;
        }
      }

      if (cachedDeps != null) {
        break;
      }
    }

    if (cachedDeps != null) {
      assert dep != null;

      cache.put(alias, cachedDeps);

      if (!cls.getName().equals(alias)) {
        // Cache by class name as well.
        cache.put(cls.getName(), cachedDeps);
      }

      return dep;
    }

    GridUuid ldrId = GridUuid.randomUuid();

    long seqNum = seq.incrementAndGet();

    String userVer = getUserVersion(ldr);

    dep = new GridDeployment(depMode, ldr, ldrId, seqNum, userVer, cls.getName(), true);

    dep.addDeployedClass(cls, alias);

    LinkedList<GridDeployment> deps =
        F.addIfAbsent(cache, alias, F.<GridDeployment>newLinkedList());

    if (!deps.isEmpty()) {
      for (GridDeployment d : deps) {
        if (!d.isUndeployed()) {
          U.error(
              log,
              "Found more than one active deployment for the same resource "
                  + "[cls="
                  + cls
                  + ", depMode="
                  + depMode
                  + ", dep="
                  + d
                  + ']');

          return null;
        }
      }
    }

    // Add at the beginning of the list for future fast access.
    deps.addFirst(dep);

    if (!cls.getName().equals(alias)) {
      // Cache by class name as well.
      cache.put(cls.getName(), deps);
    }

    if (log.isDebugEnabled()) {
      log.debug("Created new deployment: " + dep);
    }

    return dep;
  }
  /** {@inheritDoc} */
  @Override
  public GridDeployment getDeployment(GridDeploymentMetadata meta) {
    assert meta != null;

    assert ctx.config().isPeerClassLoadingEnabled();

    // Validate metadata.
    assert meta.classLoaderId() != null;
    assert meta.senderNodeId() != null;
    assert meta.sequenceNumber() >= -1;
    assert meta.parentLoader() == null;

    if (log.isDebugEnabled())
      log.debug("Starting to peer-load class based on deployment metadata: " + meta);

    while (true) {
      List<SharedDeployment> depsToCheck = null;

      SharedDeployment dep = null;

      synchronized (mux) {
        // Check obsolete request.
        if (isDeadClassLoader(meta)) return null;

        List<SharedDeployment> deps = cache.get(meta.userVersion());

        if (deps != null) {
          assert !deps.isEmpty();

          for (SharedDeployment d : deps) {
            if (d.hasParticipant(meta.senderNodeId(), meta.classLoaderId())
                || meta.senderNodeId().equals(ctx.localNodeId())) {
              // Done.
              dep = d;

              break;
            }
          }

          if (dep == null) {
            GridTuple2<Boolean, SharedDeployment> redeployCheck = checkRedeploy(meta);

            if (!redeployCheck.get1()) {
              // Checking for redeployment encountered invalid state.
              if (log.isDebugEnabled())
                log.debug("Checking for redeployment encountered invalid state: " + meta);

              return null;
            }

            dep = redeployCheck.get2();

            if (dep == null) {
              // Find existing deployments that need to be checked
              // whether they should be reused for this request.
              for (SharedDeployment d : deps) {
                if (!d.isPendingUndeploy() && !d.isUndeployed()) {
                  if (depsToCheck == null) depsToCheck = new LinkedList<SharedDeployment>();

                  if (log.isDebugEnabled()) log.debug("Adding deployment to check: " + d);

                  depsToCheck.add(d);
                }
              }

              // If no deployment can be reused, create a new one.
              if (depsToCheck == null) {
                dep = createNewDeployment(meta, false);

                deps.add(dep);
              }
            }
          }
        } else {
          GridTuple2<Boolean, SharedDeployment> redeployCheck = checkRedeploy(meta);

          if (!redeployCheck.get1()) {
            // Checking for redeployment encountered invalid state.
            if (log.isDebugEnabled())
              log.debug("Checking for redeployment encountered invalid state: " + meta);

            return null;
          }

          dep = redeployCheck.get2();

          if (dep == null)
            // Create peer class loader.
            dep = createNewDeployment(meta, true);
        }
      }

      if (dep != null) {
        if (log.isDebugEnabled())
          log.debug("Found SHARED or CONTINUOUS deployment after first check: " + dep);

        // Cache the deployed class.
        Class<?> cls = dep.deployedClass(meta.className(), meta.alias());

        if (cls == null) {
          U.warn(
              log,
              "Failed to load peer class (ignore if class got undeployed during preloading) [alias="
                  + meta.alias()
                  + ", dep="
                  + dep
                  + ']');

          return null;
        }

        return dep;
      }

      assert meta.parentLoader() == null;
      assert depsToCheck != null;
      assert !depsToCheck.isEmpty();

      /*
       * Logic below must be performed outside of synchronization
       * because it involves network calls.
       */

      // Check if class can be loaded from existing nodes.
      // In most cases this loop will find something.
      for (SharedDeployment d : depsToCheck) {
        // Load class. Note, that remote node will not load this class.
        // The class will only be loaded on this node.
        Class<?> cls = d.deployedClass(meta.className(), meta.alias());

        if (cls != null) {
          synchronized (mux) {
            if (!d.isUndeployed() && !d.isPendingUndeploy()) {
              if (!addParticipant(d, meta)) return null;

              if (log.isDebugEnabled())
                log.debug(
                    "Acquired deployment after verifying it's availability on "
                        + "existing nodes [depCls="
                        + cls
                        + ", dep="
                        + d
                        + ", meta="
                        + meta
                        + ']');

              return d;
            }
          }
        } else if (log.isDebugEnabled()) {
          log.debug(
              "Deployment cannot be reused (class does not exist on participating nodes) [dep="
                  + d
                  + ", meta="
                  + meta
                  + ']');
        }
      }

      // We are here either because all participant nodes failed
      // or the class indeed should have a separate deployment.
      for (SharedDeployment d : depsToCheck) {
        // Temporary class loader.
        ClassLoader temp =
            new GridDeploymentClassLoader(
                GridUuid.randomUuid(),
                meta.userVersion(),
                meta.deploymentMode(),
                true,
                ctx,
                ctxLdr,
                meta.classLoaderId(),
                meta.senderNodeId(),
                meta.sequenceNumber(),
                comm,
                ctx.config().getNetworkTimeout(),
                log,
                ctx.config().getPeerClassLoadingClassPathExclude(),
                0,
                false);

        String path = U.classNameToResourceName(d.sampleClassName());

        // We check if any random class from existing deployment can be
        // loaded from sender node. If it can, then we reuse existing
        // deployment.
        InputStream rsrcIn = temp.getResourceAsStream(path);

        if (rsrcIn != null) {
          // We don't need the actual stream.
          U.closeQuiet(rsrcIn);

          synchronized (mux) {
            if (d.isUndeployed() || d.isPendingUndeploy()) continue;

            // Add new node prior to loading the class, so we attempt
            // to load the class from the latest node.
            if (!addParticipant(d, meta)) {
              if (log.isDebugEnabled())
                log.debug(
                    "Failed to add participant to deployment "
                        + "[meta="
                        + meta
                        + ", dep="
                        + dep
                        + ']');

              return null;
            }
          }

          Class<?> depCls = d.deployedClass(meta.className(), meta.alias());

          if (depCls == null) {
            U.error(
                log,
                "Failed to peer load class after loading it as a resource [alias="
                    + meta.alias()
                    + ", dep="
                    + dep
                    + ']');

            return null;
          }

          if (log.isDebugEnabled())
            log.debug(
                "Acquired deployment class after verifying other class "
                    + "availability on sender node [depCls="
                    + depCls
                    + ", rndCls="
                    + d.sampleClass()
                    + ", sampleClsName="
                    + d.sampleClassName()
                    + ", meta="
                    + meta
                    + ']');

          return d;
        } else if (log.isDebugEnabled())
          log.debug(
              "Deployment cannot be reused (random class could not be loaded from sender node) [dep="
                  + d
                  + ", meta="
                  + meta
                  + ']');
      }

      synchronized (mux) {
        if (log.isDebugEnabled())
          log.debug(
              "None of the existing class-loaders fit (will try to create a new one): " + meta);

        // Check obsolete request.
        if (isDeadClassLoader(meta)) return null;

        // Check that deployment picture has not changed.
        List<SharedDeployment> deps = cache.get(meta.userVersion());

        if (deps != null) {
          assert !deps.isEmpty();

          boolean retry = false;

          for (SharedDeployment d : deps) {
            // Double check if sender was already added.
            if (d.hasParticipant(meta.senderNodeId(), meta.classLoaderId())) {
              dep = d;

              retry = false;

              break;
            }

            // New deployment was added while outside of synchronization.
            // Need to recheck it again.
            if (!d.isPendingUndeploy() && !d.isUndeployed() && !depsToCheck.contains(d))
              retry = true;
          }

          if (retry) {
            if (log.isDebugEnabled()) log.debug("Retrying due to concurrency issues: " + meta);

            // Outer while loop.
            continue;
          }

          if (dep == null) {
            // No new deployments were added, so we can safely add ours.
            dep = createNewDeployment(meta, false);

            deps.add(dep);

            if (log.isDebugEnabled())
              log.debug(
                  "Adding new deployment within second check [dep=" + dep + ", meta=" + meta + ']');
          }
        } else {
          dep = createNewDeployment(meta, true);

          if (log.isDebugEnabled())
            log.debug(
                "Created new deployment within second check [dep=" + dep + ", meta=" + meta + ']');
        }
      }

      if (dep != null) {
        // Cache the deployed class.
        Class<?> cls = dep.deployedClass(meta.className(), meta.alias());

        if (cls == null) {
          U.warn(
              log,
              "Failed to load peer class (ignore if class got undeployed during preloading) [alias="
                  + meta.alias()
                  + ", dep="
                  + dep
                  + ']');

          return null;
        }
      }

      return dep;
    }
  }
  /**
   * Removes obsolete deployments in case of redeploy.
   *
   * @param meta Request metadata.
   * @return List of shares deployment.
   */
  private GridTuple2<Boolean, SharedDeployment> checkRedeploy(GridDeploymentMetadata meta) {
    assert Thread.holdsLock(mux);

    SharedDeployment newDep = null;

    for (List<SharedDeployment> deps : cache.values()) {
      for (SharedDeployment dep : deps) {
        if (!dep.isUndeployed() && !dep.isPendingUndeploy()) {
          long undeployTimeout = ctx.config().getNetworkTimeout();

          SharedDeployment doomed = null;

          // Only check deployments with no participants.
          if (!dep.hasParticipants()) {
            // In case of SHARED deployment it is possible to get hear if
            // unmarshalling happens during undeploy. In this case, we
            // simply don't do anything.
            if (dep.deployMode() == CONTINUOUS) {
              if (dep.existingDeployedClass(meta.className()) != null) {
                // Change from shared deploy to shared undeploy or user version change.
                // Simply remove all deployments with no participating nodes.
                if (meta.deploymentMode() == SHARED
                    || !meta.userVersion().equals(dep.userVersion())) doomed = dep;
              }
            }
          }
          // If there are participants, we undeploy if class loader ID on some node changed.
          else if (dep.existingDeployedClass(meta.className()) != null) {
            GridTuple2<GridUuid, Long> ldr = dep.getClassLoaderId(meta.senderNodeId());

            if (ldr != null) {
              if (!ldr.get1().equals(meta.classLoaderId())) {
                // If deployed sequence number is less, then schedule for undeployment.
                if (ldr.get2() < meta.sequenceNumber()) {
                  if (log.isDebugEnabled())
                    log.debug(
                        "Received request for a class with newer sequence number "
                            + "(will schedule current class for undeployment) [newSeq="
                            + meta.sequenceNumber()
                            + ", oldSeq="
                            + ldr.get2()
                            + ", senderNodeId="
                            + meta.senderNodeId()
                            + ", newClsLdrId="
                            + meta.classLoaderId()
                            + ", oldClsLdrId="
                            + ldr.get1()
                            + ']');

                  doomed = dep;
                } else if (ldr.get2() > meta.sequenceNumber()) {
                  long time = System.currentTimeMillis() - dep.timestamp();

                  if (newDep == null && time < ctx.config().getNetworkTimeout()) {
                    // Set undeployTimeout, so the class will be scheduled
                    // for undeployment.
                    undeployTimeout = ctx.config().getNetworkTimeout() - time;

                    if (log.isDebugEnabled())
                      log.debug(
                          "Received execution request for a stale class (will deploy and "
                              + "schedule undeployment in "
                              + undeployTimeout
                              + "ms) "
                              + "[curSeq="
                              + ldr.get2()
                              + ", staleSeq="
                              + meta.sequenceNumber()
                              + ", cls="
                              + meta.className()
                              + ", senderNodeId="
                              + meta.senderNodeId()
                              + ", curLdrId="
                              + ldr.get1()
                              + ", staleLdrId="
                              + meta.classLoaderId()
                              + ']');

                    // We got the redeployed class before the old one.
                    // Simply create a temporary deployment for the sender node,
                    // and schedule undeploy for it.
                    newDep = createNewDeployment(meta, false);

                    doomed = newDep;
                  } else {
                    U.warn(
                        log,
                        "Received execution request for a class that has been redeployed "
                            + "(will ignore): "
                            + meta.alias());

                    if (log.isDebugEnabled())
                      log.debug(
                          "Received execution request for a class that has been redeployed "
                              + "(will ignore) [alias="
                              + meta.alias()
                              + ", dep="
                              + dep
                              + ']');

                    return F.t(false, null);
                  }
                } else {
                  U.error(
                      log,
                      "Sequence number does not correspond to class loader ID [seqNum="
                          + meta.sequenceNumber()
                          + ", dep="
                          + dep
                          + ']');

                  return F.t(false, null);
                }
              }
            }
          }

          if (doomed != null) {
            doomed.onUndeployScheduled();

            if (log.isDebugEnabled()) log.debug("Deployment was scheduled for undeploy: " + doomed);

            // Lifespan time.
            final long endTime = System.currentTimeMillis() + undeployTimeout;

            // Deployment to undeploy.
            final SharedDeployment undep = doomed;

            ctx.timeout()
                .addTimeoutObject(
                    new GridTimeoutObject() {
                      @Override
                      public GridUuid timeoutId() {
                        return undep.classLoaderId();
                      }

                      @Override
                      public long endTime() {
                        return endTime < 0 ? Long.MAX_VALUE : endTime;
                      }

                      @Override
                      public void onTimeout() {
                        boolean removed = false;

                        // Hot redeployment.
                        synchronized (mux) {
                          assert undep.isPendingUndeploy();

                          if (!undep.isUndeployed()) {
                            undep.undeploy();

                            undep.onRemoved();

                            removed = true;

                            Collection<SharedDeployment> deps = cache.get(undep.userVersion());

                            if (deps != null) {
                              for (Iterator<SharedDeployment> i = deps.iterator(); i.hasNext(); )
                                if (i.next() == undep) i.remove();

                              if (deps.isEmpty()) cache.remove(undep.userVersion());
                            }

                            if (log.isInfoEnabled())
                              log.info(
                                  "Undeployed class loader due to deployment mode change, "
                                      + "user version change, or hot redeployment: "
                                      + undep);
                          }
                        }

                        // Outside synchronization.
                        if (removed) undep.recordUndeployed(null);
                      }
                    });
          }
        }
      }
    }

    if (newDep != null) {
      List<SharedDeployment> list =
          F.addIfAbsent(cache, meta.userVersion(), F.<SharedDeployment>newList());

      assert list != null;

      list.add(newDep);
    }

    return F.t(true, newDep);
  }
예제 #21
0
    /**
     * @param entries Entries to submit.
     * @param curFut Current future.
     * @throws GridInterruptedException If interrupted.
     */
    private void submit(final List<Map.Entry<K, V>> entries, final GridFutureAdapter<Object> curFut)
        throws GridInterruptedException {
      assert entries != null;
      assert !entries.isEmpty();
      assert curFut != null;

      incrementActiveTasks();

      GridFuture<Object> fut;
      if (isLocNode) {
        fut =
            ctx.closure()
                .callLocalSafe(
                    new GridDataLoadUpdateJob<>(ctx, log, cacheName, entries, false, updater),
                    false);

        locFuts.add(fut);

        fut.listenAsync(
            new GridInClosure<GridFuture<Object>>() {
              @Override
              public void apply(GridFuture<Object> t) {
                try {
                  boolean rmv = locFuts.remove(t);

                  assert rmv;

                  curFut.onDone(t.get());
                } catch (GridException e) {
                  curFut.onDone(e);
                }
              }
            });
      } else {
        byte[] entriesBytes;

        try {
          entriesBytes = ctx.config().getMarshaller().marshal(entries);

          if (updaterBytes == null) {
            assert updater != null;

            updaterBytes = ctx.config().getMarshaller().marshal(updater);
          }

          if (topicBytes == null) topicBytes = ctx.config().getMarshaller().marshal(topic);
        } catch (GridException e) {
          U.error(log, "Failed to marshal (request will not be sent).", e);

          return;
        }

        GridDeployment dep = null;
        GridPeerDeployAware jobPda0 = null;

        if (ctx.deploy().enabled()) {
          try {
            jobPda0 = jobPda;

            assert jobPda0 != null;

            dep = ctx.deploy().deploy(jobPda0.deployClass(), jobPda0.classLoader());
          } catch (GridException e) {
            U.error(
                log,
                "Failed to deploy class (request will not be sent): " + jobPda0.deployClass(),
                e);

            return;
          }

          if (dep == null)
            U.warn(log, "Failed to deploy class (request will be sent): " + jobPda0.deployClass());
        }

        long reqId = idGen.incrementAndGet();

        fut = curFut;

        reqs.put(reqId, (GridFutureAdapter<Object>) fut);

        GridDataLoadRequest<Object, Object> req =
            new GridDataLoadRequest<>(
                reqId,
                topicBytes,
                cacheName,
                updaterBytes,
                entriesBytes,
                true,
                dep != null ? dep.deployMode() : null,
                dep != null ? jobPda0.deployClass().getName() : null,
                dep != null ? dep.userVersion() : null,
                dep != null ? dep.participants() : null,
                dep != null ? dep.classLoaderId() : null,
                dep == null);

        try {
          ctx.io().send(node, TOPIC_DATALOAD, req, PUBLIC_POOL);

          if (log.isDebugEnabled())
            log.debug("Sent request to node [nodeId=" + node.id() + ", req=" + req + ']');
        } catch (GridException e) {
          if (ctx.discovery().alive(node) && ctx.discovery().pingNode(node.id()))
            ((GridFutureAdapter<Object>) fut).onDone(e);
          else
            ((GridFutureAdapter<Object>) fut)
                .onDone(
                    new GridTopologyException(
                        "Failed to send " + "request (node has left): " + node.id()));
        }
      }
    }
예제 #22
0
  /**
   * @param ctx Grid kernal context.
   * @param cacheName Cache name.
   * @param flushQ Flush queue.
   */
  public GridDataLoaderImpl(
      final GridKernalContext ctx,
      @Nullable final String cacheName,
      DelayQueue<GridDataLoaderImpl<K, V>> flushQ) {
    assert ctx != null;

    this.ctx = ctx;
    this.cacheName = cacheName;
    this.flushQ = flushQ;

    log = U.logger(ctx, logRef, GridDataLoaderImpl.class);

    discoLsnr =
        new GridLocalEventListener() {
          @Override
          public void onEvent(GridEvent evt) {
            assert evt.type() == EVT_NODE_FAILED || evt.type() == EVT_NODE_LEFT;

            GridDiscoveryEvent discoEvt = (GridDiscoveryEvent) evt;

            UUID id = discoEvt.eventNodeId();

            // Remap regular mappings.
            final Buffer buf = bufMappings.remove(id);

            if (buf != null) {
              // Only async notification is possible since
              // discovery thread may be trapped otherwise.
              ctx.closure()
                  .callLocalSafe(
                      new Callable<Object>() {
                        @Override
                        public Object call() throws Exception {
                          buf.onNodeLeft();

                          return null;
                        }
                      },
                      true /* system pool */);
            }
          }
        };

    ctx.event().addLocalEventListener(discoLsnr, EVT_NODE_FAILED, EVT_NODE_LEFT);

    // Generate unique topic for this loader.
    topic = TOPIC_DATALOAD.topic(GridUuid.fromUuid(ctx.localNodeId()));

    ctx.io()
        .addMessageListener(
            topic,
            new GridMessageListener() {
              @Override
              public void onMessage(UUID nodeId, Object msg) {
                assert msg instanceof GridDataLoadResponse;

                GridDataLoadResponse res = (GridDataLoadResponse) msg;

                if (log.isDebugEnabled()) log.debug("Received data load response: " + res);

                Buffer buf = bufMappings.get(nodeId);

                if (buf != null) buf.onResponse(res);
                else if (log.isDebugEnabled())
                  log.debug("Ignoring response since node has left [nodeId=" + nodeId + ", ");
              }
            });

    if (log.isDebugEnabled()) log.debug("Added response listener within topic: " + topic);

    fut = new GridDataLoaderFuture(ctx, this);
  }
  /** {@inheritDoc} */
  @Nullable
  @SuppressWarnings({"UnusedCatchParameter"})
  @Override
  public GridDeployment getDeployment(GridDeploymentMetadata meta) {
    GridDeployment dep;

    Class<?> cls = null;

    String alias = meta.alias();

    synchronized (mux) {
      // Validate metadata.
      assert meta.alias() != null;

      dep = getDeployment(meta.alias());

      if (dep != null) {
        if (log.isDebugEnabled()) {
          log.debug("Acquired deployment class from local cache: " + dep);
        }

        return dep;
      }

      GridDeploymentResource rsrc = spi.findResource(meta.alias());

      if (rsrc != null) {
        dep =
            deploy(
                ctx.config().getDeploymentMode(),
                rsrc.getClassLoader(),
                rsrc.getResourceClass(),
                alias);

        if (dep == null) {
          return null;
        }

        if (log.isDebugEnabled()) {
          log.debug("Acquired deployment class from SPI: " + dep);
        }
      }
      // Auto-deploy.
      else {
        ClassLoader ldr = meta.classLoader();

        if (ldr == null) {
          ldr = Thread.currentThread().getContextClassLoader();

          // Safety.
          if (ldr == null) {
            ldr = ctxLdr;
          }
        }

        // Don't auto-deploy locally in case of nested execution.
        if (ldr instanceof GridDeploymentClassLoader) {
          return null;
        }

        try {
          // Check that class can be loaded.
          cls = ldr.loadClass(meta.alias());

          spi.register(ldr, cls);

          rsrc = spi.findResource(alias);

          if (rsrc != null && rsrc.getResourceClass().equals(cls)) {
            if (log.isDebugEnabled()) {
              log.debug("Retrieved auto-loaded resource from spi: " + rsrc);
            }

            dep = deploy(ctx.config().getDeploymentMode(), ldr, cls, alias);

            if (dep == null) {
              return null;
            }
          } else {
            U.warn(
                log,
                "Failed to find resource from deployment SPI even after registering it: "
                    + meta.alias());

            return null;
          }
        } catch (ClassNotFoundException e) {
          if (log.isDebugEnabled()) {
            log.debug(
                "Failed to load class for local auto-deployment [ldr="
                    + ldr
                    + ", meta="
                    + meta
                    + ']');
          }

          return null;
        } catch (GridSpiException e) {
          U.error(log, "Failed to deploy local class: " + meta.alias(), e);

          return null;
        }
      }
    }

    if (cls != null) {
      recordDeploy(cls, alias, meta.isRecord());

      dep.addDeployedClass(cls, meta.className(), meta.alias());
    }

    if (log.isDebugEnabled()) {
      log.debug("Acquired deployment class: " + dep);
    }

    return dep;
  }