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

org.apache.hadoop.hbase.regionserver.Leases Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/**
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hbase.regionserver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.util.HasThread;

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Delayed;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;

import java.io.IOException;

/**
 * Leases
 *
 * There are several server classes in HBase that need to track external
 * clients that occasionally send heartbeats.
 *
 * 

These external clients hold resources in the server class. * Those resources need to be released if the external client fails to send a * heartbeat after some interval of time passes. * *

The Leases class is a general reusable class for this kind of pattern. * An instance of the Leases class will create a thread to do its dirty work. * You should close() the instance if you want to clean up the thread properly. * *

* NOTE: This class extends Thread rather than Chore because the sleep time * can be interrupted when there is something to do, rather than the Chore * sleep time which is invariant. */ @InterfaceAudience.Private public class Leases extends HasThread { private static final Log LOG = LogFactory.getLog(Leases.class.getName()); public static final int MIN_WAIT_TIME = 100; private final Map leases = new ConcurrentHashMap(); protected final int leaseCheckFrequency; protected volatile boolean stopRequested = false; /** * Creates a lease monitor * * @param leaseCheckFrequency - how often the lease should be checked * (milliseconds) */ public Leases(final int leaseCheckFrequency) { this.leaseCheckFrequency = leaseCheckFrequency; setDaemon(true); } /** * @see Thread#run() */ @Override public void run() { long toWait = leaseCheckFrequency; Lease nextLease = null; long nextLeaseDelay = Long.MAX_VALUE; while (!stopRequested || (stopRequested && !leases.isEmpty()) ) { try { if (nextLease != null) { toWait = nextLease.getDelay(TimeUnit.MILLISECONDS); } toWait = Math.min(leaseCheckFrequency, toWait); toWait = Math.max(MIN_WAIT_TIME, toWait); Thread.sleep(toWait); } catch (InterruptedException e) { continue; } catch (ConcurrentModificationException e) { continue; } catch (Throwable e) { LOG.fatal("Unexpected exception killed leases thread", e); break; } nextLease = null; nextLeaseDelay = Long.MAX_VALUE; for (Iterator> it = leases.entrySet().iterator(); it.hasNext();) { Map.Entry entry = it.next(); Lease lease = entry.getValue(); long thisLeaseDelay = lease.getDelay(TimeUnit.MILLISECONDS); if ( thisLeaseDelay > 0) { if (nextLease == null || thisLeaseDelay < nextLeaseDelay) { nextLease = lease; nextLeaseDelay = thisLeaseDelay; } } else { // A lease expired. Run the expired code before removing from map // since its presence in map is used to see if lease exists still. if (lease.getListener() == null) { LOG.error("lease listener is null for lease " + lease.getLeaseName()); } else { lease.getListener().leaseExpired(); } it.remove(); } } } close(); } /** * Shuts down this lease instance when all outstanding leases expire. * Like {@link #close()} but rather than violently end all leases, waits * first on extant leases to finish. Use this method if the lease holders * could loose data, leak locks, etc. Presumes client has shutdown * allocation of new leases. */ public void closeAfterLeasesExpire() { this.stopRequested = true; } /** * Shut down this Leases instance. All pending leases will be destroyed, * without any cancellation calls. */ public void close() { LOG.info(Thread.currentThread().getName() + " closing leases"); this.stopRequested = true; leases.clear(); LOG.info(Thread.currentThread().getName() + " closed leases"); } /** * Obtain a lease. * * @param leaseName name of the lease * @param leaseTimeoutPeriod length of the lease in milliseconds * @param listener listener that will process lease expirations * @throws LeaseStillHeldException */ public void createLease(String leaseName, int leaseTimeoutPeriod, final LeaseListener listener) throws LeaseStillHeldException { addLease(new Lease(leaseName, leaseTimeoutPeriod, listener)); } /** * Inserts lease. Resets expiration before insertion. * @param lease * @throws LeaseStillHeldException */ public void addLease(final Lease lease) throws LeaseStillHeldException { if (this.stopRequested) { return; } lease.resetExpirationTime(); if (leases.containsKey(lease.getLeaseName())) { throw new LeaseStillHeldException(lease.getLeaseName()); } leases.put(lease.getLeaseName(), lease); } /** * Renew a lease * * @param leaseName name of lease * @throws LeaseException */ public void renewLease(final String leaseName) throws LeaseException { Lease lease = leases.get(leaseName); // We need to check to see if the remove is successful as the poll in the run() // method could have completed between the get and the remove which will result // in a corrupt leaseQueue. if (lease == null ) { throw new LeaseException("lease '" + leaseName + "' does not exist or has already expired"); } lease.resetExpirationTime(); } /** * Client explicitly cancels a lease. * @param leaseName name of lease * @throws org.apache.hadoop.hbase.regionserver.LeaseException */ public void cancelLease(final String leaseName) throws LeaseException { removeLease(leaseName); } /** * Remove named lease. * Lease is removed from the list of leases and removed from the delay queue. * Lease can be resinserted using {@link #addLease(Lease)} * * @param leaseName name of lease * @throws org.apache.hadoop.hbase.regionserver.LeaseException * @return Removed lease */ Lease removeLease(final String leaseName) throws LeaseException { Lease lease = leases.remove(leaseName); if (lease == null) { throw new LeaseException("lease '" + leaseName + "' does not exist"); } return lease; } /** * Thrown if we are asked create a lease but lease on passed name already * exists. */ @SuppressWarnings("serial") public static class LeaseStillHeldException extends IOException { private final String leaseName; /** * @param name */ public LeaseStillHeldException(final String name) { this.leaseName = name; } /** @return name of lease */ public String getName() { return this.leaseName; } } /** This class tracks a single Lease. */ static class Lease implements Delayed { private final String leaseName; private final LeaseListener listener; private int leaseTimeoutPeriod; private long expirationTime; Lease(final String leaseName, int leaseTimeoutPeriod, LeaseListener listener) { this.leaseName = leaseName; this.listener = listener; this.leaseTimeoutPeriod = leaseTimeoutPeriod; this.expirationTime = 0; } /** @return the lease name */ public String getLeaseName() { return leaseName; } /** @return listener */ public LeaseListener getListener() { return this.listener; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return this.hashCode() == obj.hashCode(); } @Override public int hashCode() { return this.leaseName.hashCode(); } public long getDelay(TimeUnit unit) { return unit.convert(this.expirationTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } public int compareTo(Delayed o) { long delta = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS); return this.equals(o) ? 0 : (delta > 0 ? 1 : -1); } /** * Resets the expiration time of the lease. */ public void resetExpirationTime() { this.expirationTime = System.currentTimeMillis() + this.leaseTimeoutPeriod; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy