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

com.bigdata.service.ndx.pipeline.AbstractPendingSetMasterTask Maven / Gradle / Ivy

/*

 Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

 Contact:
 SYSTAP, LLC DBA Blazegraph
 2501 Calvert ST NW #106
 Washington, DC 20008
 [email protected]

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2 of the License.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*
 * Created on Jul 13, 2009
 */

package com.bigdata.service.ndx.pipeline;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

import com.bigdata.BigdataStatics;
import com.bigdata.btree.BigdataMap;
import com.bigdata.relation.accesspath.BlockingBuffer;
import com.bigdata.service.AbstractDistributedFederation;
import com.bigdata.service.master.INotifyOutcome;

/**
 * Extends the master task to track outstanding asynchronous operations on work
 * items.
 * 

* The clients notify the {@link AbstractPendingSetSubtask} as each operation * completes. The subtask notifies the master, which then clears the entry from * its {@link #pendingMap} and also clears the entry from any other subtask that * had been tasked with the same work item (this permits subtasks to terminate * as soon as their work is complete regardless of which subtask actually * performed the work). The master will not terminate until all outstanding * asynchronous operations (the pending set) are complete. * * @author Bryan Thompson * @version $Id$ */ abstract public class AbstractPendingSetMasterTask, // E, // S extends AbstractPendingSetSubtask, // L> // extends AbstractMasterTask // implements INotifyOutcome { /** * Log may be used to see just success/error reporting for the master * without the log information from the base class. */ static protected transient final Logger log = Logger .getLogger(AbstractPendingSetMasterTask.class); /** * Lock used to serialize operations on the {@link #pendingMap}. */ private final ReentrantLock lock = new ReentrantLock(); private final AbstractDistributedFederation fed; public AbstractDistributedFederation getFederation() { return fed; } /** * A proxy for this class which is used by the client task to send * asynchronous notifications. */ protected final INotifyOutcome masterProxy; /** * Return the pending map. The pending map reflects the resources which are * in process. Resources are added to this collection when they are posted * to a client for processing and are removed when the client asynchronously * reports success or failure for the resource. */ abstract protected Map> getPendingMap(); /** * @param stats * @param buffer * @param sinkIdleTimeoutNanos * @param sinkPollTimeoutNanos */ public AbstractPendingSetMasterTask(final AbstractDistributedFederation fed, final H stats, final BlockingBuffer buffer, final long sinkIdleTimeoutNanos, final long sinkPollTimeoutNanos) { super(stats, buffer, sinkIdleTimeoutNanos, sinkPollTimeoutNanos); if (fed == null) throw new IllegalArgumentException(); this.fed = fed; this.masterProxy = (INotifyOutcome) fed .getProxy(this, true/* enableDGC */); } final protected boolean nothingPending() { lock.lock(); try { return getPendingMap().isEmpty(); } finally { lock.unlock(); } } final public int getPendingSetSize() { lock.lock(); try { return getPendingMap().size(); } finally { lock.unlock(); } } /** * Add a work item to the pending set. *

* Note: This method is written such that a {@link BigdataMap} could be used * as the implementation object. (The tuple is always updated by an insert * when its value's state is changed.) * * @param e * The work item. * @param locator * The locator of the subtask/client that will process that work * item. * * @return true iff the pending set did not contain an entry * for that work item. Since entries are cleared from the map when * work is successfully complete or if all pending operations fail * for a work item, a true return does not * conclusively indicate a new work item. */ protected boolean addPending(final E e, final AbstractPendingSetSubtask sink, final L locator) { if (e == null) throw new IllegalArgumentException(); if (sink == null) throw new IllegalArgumentException(); if (locator == null) throw new IllegalArgumentException(); final boolean modifiedMap; lock.lock(); try { Collection locators = getPendingMap().remove(e); if (locators == null) { locators = new LinkedHashSet(); locators.add(locator); getPendingMap().put(e, locators); sink.getPendingSet().add(e); // added to the map. modifiedMap = true; } else { // already in the map. locators.add(locator); getPendingMap().put(e, locators); sink.getPendingSet().add(e); modifiedMap = false; } if (BigdataStatics.debug || log.isDebugEnabled()) { String msg = "Added pending: size=" + getPendingSetSize() + ", resource=" + e + ", locator=" + locator + ", sinkSize=" + sink.getPendingSetSize(); if (BigdataStatics.debug) System.err.println(msg); if (log.isDebugEnabled()) log.debug(msg); } return modifiedMap; } finally { lock.unlock(); } } /** * Remove a work item from the pending set. *

* Note: This method is written such that a {@link BigdataMap} could be used * as the implementation object. (The tuple is always updated by an insert * when its value's state is changed.) * * @param e * The work item. * @param locator * The subtask / client locator. * @param cause * null unless an error is being reported. * * @return true iff the work item was cleared from the * pending set (present on entry but cleared on exit). * * @todo unit tests for the add/remove pending methods since they are a bit * complex internally. */ protected boolean removePending(final E e, final L locator, final Throwable cause) { if (e == null) throw new IllegalArgumentException(); if (locator == null) throw new IllegalArgumentException(); // if (cause == null) // throw new IllegalArgumentException(); boolean notify = false; final int sizeUnderLock; lock.lock(); try { if (cause == null) { /* * Successful completion. */ final Collection locators = getPendingMap().remove(e); if (locators == null) { /* * Presume already successful since not in the map. Return * false since map was not modified. */ return false; } // for each locator tasked with that item. for (L t : locators) { // clear item from the sink's pending set. final S sink; try { sink = super.getSink(t, false/* reopen */); } catch (InterruptedException ex) { halt(ex); throw new RuntimeException(ex); } sink.removePending(e); } // notify (success). notify = locators != null; // no more requests remain for that work item. return true; } /* * Error reported. */ final Collection locators = getPendingMap().get(e); if (locators == null) { /* * Presume already successful since not in the map. Return false * since map was not modified. */ return false; } // remove the entry for the locator which reported the error. locators.remove(locator); { // clear item from the sink's pending set. final S sink; try { sink = super.getSink(locator, false/* reopen */); } catch (InterruptedException ex) { halt(ex); throw new RuntimeException(ex); } sink.removePending(e); } if (locators.isEmpty()) { // no outstanding requests remain, so will notify error. getPendingMap().remove(e); // will notify. notify = true; // no more requests for that work item. return true; } else { // otherwise outstanding requests remain, so update map. getPendingMap().put(e, locators); // requests remain for that work item. return false; } } finally { try { sizeUnderLock = getPendingMap().size(); } finally { lock.unlock(); } // notify once we have released the lock. if (notify) { if (cause == null) { didSucceed(e); } else { didFail(e, cause); } } if (BigdataStatics.debug || log.isDebugEnabled()) { final String msg = "resource=" + e + ", notify=" + notify + ", pendingSetSize=" + sizeUnderLock + ", locator=" + locator + (cause == null ? "" : "cause=" + cause); if (BigdataStatics.debug) System.err.println(msg); if (log.isDebugEnabled()) log.debug(msg); } } } /** * Return a new pending map instance. The size of this collection places a * machine limit on the #of resources which may be processed concurrently. A * {@link BigdataMap} may be used if sufficient RAM is not available. */ abstract protected Map> newPendingMap(); /** * The resource is removed from the {@link #pendingMap} and the pending set * for each sink for which there is an outstanding request for that * resource. {@link #didSucceed(Object)} will be invoked the first time * a request succeeds for that resource. */ final public void success(final E e, final L locator) { removePending(e, locator, null/* cause */); } /** * The resource is removed from the pending set for the sink associated with * that locator. If there are no more outstanding requests for that resource * in the {@link #pendingMap} then the resource is removed from the pending * map as well. {@link #didFail(Object, Throwable)} will be invoked if no * requests remain for that resource in the {@link #pendingMap}. */ final public void error(final E resource, final L locator, final Throwable cause) { removePending(resource, locator, null/* cause */); // if (removePending(resource, locator, null/* cause */)) { // // // all pending operations have failed for this resource. // log.error(resource, cause); // // } } /** * Hook provides notification the first time work for the resource has been * successfully completed for any set of concurrent outstanding work * requests and may be extended if necessary. The final outcome * for each resource is not retained. Therefore if the same resource is * resubmitted after its successful completion or its failure, then this * method may be invoked again for that resource. The default implementation * logs the event @ INFO. */ protected void didSucceed(final E e) { if (log.isInfoEnabled()) { // an asynchronous operation has succeeded for this resource. log.info(e.toString()); } } /** * Hook provides notification if all outstanding work requests for the * resource have failed. There may be more than one request to perform the * same work. This method is not invoked until all such requests have failed * and is not invoked if any of those requests succeed. Note that work * requests MUST be idempotent. The default implementation logs the event @ ERROR. * * @param resource * The resource. * @param cause * The exception. */ protected void didFail(final E resource, final Throwable cause) { // all pending operations have failed for this resource. log.error(resource, cause); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy