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

com.bigdata.ha.FutureTaskInvariantMon 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
*/
package com.bigdata.ha;

import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import org.apache.log4j.Logger;

import com.bigdata.concurrent.FutureTaskMon;
import com.bigdata.quorum.Quorum;
import com.bigdata.quorum.QuorumEvent;
import com.bigdata.quorum.QuorumEventEnum;
import com.bigdata.quorum.QuorumListener;
import com.bigdata.util.StackInfoReport;

/**
 * A {@link Future} that allows you to cancel a computation if an invariant is
 * violated. This class is specifically designed to monitor quorum related
 * invariants for HA.
 * 

* Once an invariant is established, listening for the relevant quorum events * commences and a check is made to verify that the invariant holds on entry. * This pattern ensures there is no possibility of a missed event. *

* This {@link FutureTask} wrapper will return the value of the {@link Callable} * (or the specified result for the {@link Runnable}) iff the task completes * successfully without the invariant being violated. * * @author Bryan Thompson * @param * The generic type of the future. */ public abstract class FutureTaskInvariantMon extends FutureTaskMon implements QuorumListener { private static final Logger log = Logger.getLogger(FutureTaskInvariantMon.class); private final Quorum> m_quorum; /** * The quorum token on entry. */ private final long token; private final List m_triggers = new CopyOnWriteArrayList(); public FutureTaskInvariantMon(final Callable callable, final Quorum> quorum) { super(callable); if (quorum == null) throw new IllegalArgumentException(); m_quorum = quorum; token = quorum.token(); } public FutureTaskInvariantMon(final Runnable runnable, final T result, Quorum> quorum) { super(runnable, result); if (quorum == null) throw new IllegalArgumentException(); m_quorum = quorum; token = quorum.token(); } /** * Concrete implementations use this callback hook to establish the * invariants to be monitored. */ abstract protected void establishInvariants(); /** * {@inheritDoc} *

* Hook to manage listener registration and establish invariants. */ @Override public void run() { boolean didStart = false; m_quorum.addListener(this); try { establishInvariants(); didStart = true; super.run(); } finally { m_quorum.removeListener(this); if (!didStart) { /* * Guarantee cancelled unless run() invoked. */ cancel(true/* mayInterruptIfRunning */); } } } /** * Establish an invariant that the specified service is a member of the * quorum. * * @param serviceId * The service. */ public void assertMember(final UUID serviceId) { m_triggers.add(new QuorumEventInvariant(QuorumEventEnum.MEMBER_REMOVE, serviceId)); // now check that already a member and break if not assertMembership(m_quorum.getMembers(), serviceId); } /** * Establish an invariant that the specified service is joined with the met * quorum. * * @param serviceId * The service. */ public void assertJoined(final UUID serviceId) { m_triggers.add(new QuorumEventInvariant(QuorumEventEnum.SERVICE_LEAVE, serviceId)); // now check that already joined and break if not assertMembership(m_quorum.getJoined(), serviceId); } /** * Establish an invariant that the specified service is a not joined with * the met quorum. * * @param serviceId * The service. */ public void assertNotJoined(final UUID serviceId) { m_triggers.add(new QuorumEventInvariant(QuorumEventEnum.SERVICE_JOIN, serviceId)); // now check not already joined and break if it is if (isMember(m_quorum.getJoined(), serviceId)) broken(); } /** * Establish an invariant that the specified service is in the quorum * pipeline. * * @param serviceId * The service. */ public void assertInPipeline(final UUID serviceId) { m_triggers.add(new QuorumEventInvariant( QuorumEventEnum.PIPELINE_REMOVE, serviceId)); // now check that already in pipeline and break if not assertMembership(m_quorum.getPipeline(), serviceId); } /** * Establish an invariant that the quorum is met and remains met on the same * token (the one specified to the constructor). */ public void assertQuorumMet() { m_triggers.add(new QuorumEventInvariant(QuorumEventEnum.QUORUM_BROKE, null/* serviceId */)); // now check that quorum is met and break if not if (!m_quorum.isQuorumMet()) broken(); if (m_quorum.token() != token) broken(); } /** * Establish an invariant that the quorum is fully met. */ public void assertQuorumFullyMet() { // no-one must leave! m_triggers.add(new QuorumEventInvariant(QuorumEventEnum.SERVICE_LEAVE, null/* serviceId */)); // now check that quorum is fully met on the current token and break if not if (!m_quorum.isQuorumFullyMet(m_quorum.token())) broken(); } private void assertMembership(final UUID[] members, final UUID serviceId) { if (isMember(members, serviceId)) return; broken(); } private boolean isMember(final UUID[] members, final UUID serviceId) { for (UUID member : members) { if (member.equals(serviceId)) return true; } return false; } /** * Any QuorumEvent must be checked to see if it matches an Invariant trigger */ @Override public void notify(final QuorumEvent e) { boolean interrupt = false; for (QuorumEventInvariant inv : m_triggers) { if (inv.matches(e)) { interrupt = true; break; } } // interrupt the call thread if (interrupt) { broken(); } else { if (log.isDebugEnabled()) log.debug("Ignoring event: " + e); } } private void broken() { log.warn("BROKEN", new StackInfoReport()); cancel(true/*mayInterruptIfRunning*/); } @SuppressWarnings("serial") private class QuorumEventInvariant implements QuorumEvent, Serializable { private final QuorumEventEnum m_qe; private final UUID m_sid; /** * * @param qe * The {@link QuorumEvent} type (required). * @param sid * The service {@link UUID} (optional). When null * the {@link QuorumEventEnum} will be matched for ANY service * {@link UUID}. */ public QuorumEventInvariant(final QuorumEventEnum qe, final UUID sid) { if (qe == null) throw new IllegalArgumentException(); m_qe = qe; m_sid = sid; } @Override public QuorumEventEnum getEventType() { return m_qe; } @Override public long lastValidToken() { throw new UnsupportedOperationException(); } @Override public long token() { throw new UnsupportedOperationException(); } @Override public UUID getServiceId() { return m_sid; } @Override public long lastCommitTime() { throw new UnsupportedOperationException(); } public boolean matches(final QuorumEvent qe) { return qe.getEventType() == m_qe && (m_sid == null || m_sid.equals(qe.getServiceId())); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy