/** {@inheritDoc} */ @Override public void addAttributeListener(GridTaskSessionAttributeListener lsnr, boolean rewind) { A.notNull(lsnr, "lsnr"); Map<Object, Object> attrs = null; List<GridTaskSessionAttributeListener> lsnrs; synchronized (mux) { lsnrs = new ArrayList<GridTaskSessionAttributeListener>(this.lsnrs.size()); lsnrs.addAll(this.lsnrs); lsnrs.add(lsnr); lsnrs = Collections.unmodifiableList(lsnrs); this.lsnrs = lsnrs; if (rewind) attrs = new HashMap<Object, Object>(this.attrs); } if (rewind) { for (Map.Entry<Object, Object> entry : attrs.entrySet()) { for (GridTaskSessionAttributeListener l : lsnrs) { l.onAttributeSet(entry.getKey(), entry.getValue()); } } } }
/** * @return Collection of readers after check. * @throws GridCacheEntryRemovedException If removed. */ public Collection<ReaderId> checkReaders() throws GridCacheEntryRemovedException { synchronized (mux) { checkObsolete(); if (!readers.isEmpty()) { List<ReaderId> rmv = null; for (ReaderId reader : readers) { if (!cctx.discovery().alive(reader.nodeId())) { if (rmv == null) rmv = new LinkedList<ReaderId>(); rmv.add(reader); } } if (rmv != null) { readers = new LinkedList<ReaderId>(readers); for (ReaderId rdr : rmv) readers.remove(rdr); readers = Collections.unmodifiableList(readers); } } return readers; } }
/** * Gets values referenced by sequential keys, e.g. {@code key1...keyN}. * * @param keyPrefix Key prefix, e.g. {@code key} for {@code key1...keyN}. * @param params Parameters map. * @return Values. */ @Nullable protected List<Object> values(String keyPrefix, Map<String, Object> params) { assert keyPrefix != null; List<Object> vals = new LinkedList<>(); for (int i = 1; ; i++) { String key = keyPrefix + i; if (params.containsKey(key)) vals.add(params.get(key)); else break; } return vals; }
/** * 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 nodeId Reader to add. * @param msgId Message ID. * @return Future for all relevant transactions that were active at the time of adding reader, or * {@code null} if reader was added * @throws GridCacheEntryRemovedException If entry was removed. */ @Nullable public GridFuture<Boolean> addReader(UUID nodeId, long msgId) throws GridCacheEntryRemovedException { // Don't add local node as reader. if (cctx.nodeId().equals(nodeId)) return null; GridNode node = cctx.discovery().node(nodeId); // If remote node has no near cache, don't add it. if (node == null || !U.hasNearCache(node, cctx.dht().near().name())) return null; // If remote node is (primary?) or back up, don't add it as a reader. if (U.nodeIds(cctx.affinity(partition(), CU.allNodes(cctx))).contains(nodeId)) return null; boolean ret = false; GridCacheMultiTxFuture<K, V> txFut; Collection<GridCacheMvccCandidate<K>> cands = null; synchronized (mux) { checkObsolete(); txFut = this.txFut; ReaderId reader = readerId(nodeId); if (reader == null) { reader = new ReaderId(nodeId, msgId); readers = new LinkedList<ReaderId>(readers); readers.add(reader); // Seal. readers = Collections.unmodifiableList(readers); txFut = this.txFut = new GridCacheMultiTxFuture<K, V>(cctx); cands = localCandidates(); ret = true; } else { long id = reader.messageId(); if (id < msgId) reader.messageId(msgId); } } if (ret) { assert txFut != null; if (!F.isEmpty(cands)) { for (GridCacheMvccCandidate<K> c : cands) { GridCacheTxEx<K, V> tx = cctx.tm().<GridCacheTxEx<K, V>>tx(c.version()); if (tx != null) { assert tx.local(); txFut.addTx(tx); } } } txFut.init(); if (!txFut.isDone()) { txFut.listenAsync( new CI1<GridFuture<?>>() { @Override public void apply(GridFuture<?> f) { synchronized (mux) { // Release memory. GridDhtCacheEntry.this.txFut = null; } } }); } else // Release memory. txFut = this.txFut = null; } return txFut; }
/** * Creates and caches new deployment. * * @param meta Deployment metadata. * @param isCache Whether or not to cache. * @return New deployment. */ private SharedDeployment createNewDeployment(GridDeploymentMetadata meta, boolean isCache) { assert Thread.holdsLock(mux); assert meta.parentLoader() == null; GridUuid ldrId = GridUuid.randomUuid(); GridDeploymentClassLoader clsLdr; if (meta.deploymentMode() == CONTINUOUS || meta.participants() == null) { // Create peer class loader. // Note that we are passing empty list for local P2P exclude, as it really // does not make sense with shared deployment. clsLdr = new GridDeploymentClassLoader( ldrId, meta.userVersion(), meta.deploymentMode(), false, ctx, ctxLdr, meta.classLoaderId(), meta.senderNodeId(), meta.sequenceNumber(), comm, ctx.config().getNetworkTimeout(), log, ctx.config().getPeerClassLoadingClassPathExclude(), ctx.config().getPeerClassLoadingMissedResourcesCacheSize(), meta.deploymentMode() == CONTINUOUS /* enable class byte cache in CONTINUOUS mode */); if (meta.participants() != null) for (Map.Entry<UUID, GridTuple2<GridUuid, Long>> e : meta.participants().entrySet()) clsLdr.register(e.getKey(), e.getValue().get1(), e.getValue().get2()); if (log.isDebugEnabled()) log.debug( "Created class loader in CONTINUOUS mode or without participants " + "[ldr=" + clsLdr + ", meta=" + meta + ']'); } else { assert meta.deploymentMode() == SHARED; // Create peer class loader. // Note that we are passing empty list for local P2P exclude, as it really // does not make sense with shared deployment. clsLdr = new GridDeploymentClassLoader( ldrId, meta.userVersion(), meta.deploymentMode(), false, ctx, ctxLdr, meta.participants(), comm, ctx.config().getNetworkTimeout(), log, ctx.config().getPeerClassLoadingClassPathExclude(), ctx.config().getPeerClassLoadingMissedResourcesCacheSize(), false); if (log.isDebugEnabled()) log.debug( "Created classloader in SHARED mode with participants " + "[ldr=" + clsLdr + ", meta=" + meta + ']'); } // Give this deployment a unique class loader to emphasize that this // ID is unique to this shared deployment and is not ID of loader on // sender node. SharedDeployment dep = new SharedDeployment( meta.deploymentMode(), clsLdr, ldrId, -1, meta.userVersion(), meta.alias()); if (log.isDebugEnabled()) log.debug("Created new deployment: " + dep); if (isCache) { List<SharedDeployment> deps = F.addIfAbsent(cache, meta.userVersion(), new LinkedList<SharedDeployment>()); assert deps != null; deps.add(dep); if (log.isDebugEnabled()) log.debug("Added deployment to cache: " + cache); } 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); }
/** {@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; } }