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

org.apache.accumulo.tserver.session.SessionManager Maven / Gradle / Ivy

There is a newer version: 3.0.0
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.accumulo.tserver.session;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.accumulo.core.clientImpl.Translator;
import org.apache.accumulo.core.clientImpl.Translators;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.TableId;
import org.apache.accumulo.core.dataImpl.thrift.MultiScanResult;
import org.apache.accumulo.core.tabletserver.thrift.ActiveScan;
import org.apache.accumulo.core.tabletserver.thrift.ScanState;
import org.apache.accumulo.core.tabletserver.thrift.ScanType;
import org.apache.accumulo.core.util.MapCounter;
import org.apache.accumulo.server.util.time.SimpleTimer;
import org.apache.accumulo.tserver.scan.ScanRunState;
import org.apache.accumulo.tserver.scan.ScanTask;
import org.apache.accumulo.tserver.session.Session.State;
import org.apache.accumulo.tserver.tablet.ScanBatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

public class SessionManager {
  private static final Logger log = LoggerFactory.getLogger(SessionManager.class);

  private final SecureRandom random;
  private final ConcurrentMap sessions = new ConcurrentHashMap<>();
  private final long maxIdle;
  private final long maxUpdateIdle;
  private final List idleSessions = new ArrayList<>();
  private final Long expiredSessionMarker = (long) -1;
  private final AccumuloConfiguration aconf;

  public SessionManager(AccumuloConfiguration conf) {
    aconf = conf;
    maxUpdateIdle = conf.getTimeInMillis(Property.TSERV_UPDATE_SESSION_MAXIDLE);
    maxIdle = conf.getTimeInMillis(Property.TSERV_SESSION_MAXIDLE);

    SecureRandom sr;
    try {
      // This is faster than the default secure random which uses /dev/urandom
      sr = SecureRandom.getInstance("SHA1PRNG");
    } catch (NoSuchAlgorithmException e) {
      log.debug("Unable to create SHA1PRNG secure random, using default");
      sr = new SecureRandom();
    }
    random = sr;

    Runnable r = new Runnable() {
      @Override
      public void run() {
        sweep(maxIdle, maxUpdateIdle);
      }
    };

    SimpleTimer.getInstance(conf).schedule(r, 0, Math.max(maxIdle / 2, 1000));
  }

  public long createSession(Session session, boolean reserve) {
    long sid = random.nextLong();

    synchronized (session) {
      Preconditions.checkArgument(session.state == State.NEW);
      session.state = reserve ? State.RESERVED : State.UNRESERVED;
      session.startTime = session.lastAccessTime = System.currentTimeMillis();
    }

    while (sessions.putIfAbsent(sid, session) != null) {
      sid = random.nextLong();
    }

    return sid;
  }

  public long getMaxIdleTime() {
    return maxIdle;
  }

  /**
   * while a session is reserved, it cannot be canceled or removed
   */

  public Session reserveSession(long sessionId) {
    Session session = sessions.get(sessionId);
    if (session != null) {
      synchronized (session) {
        if (session.state == State.RESERVED)
          throw new IllegalStateException(
              "Attempted to reserved session that is already reserved " + sessionId);
        if (session.state == State.REMOVED)
          return null;
        session.state = State.RESERVED;
      }
    }

    return session;

  }

  public Session reserveSession(long sessionId, boolean wait) {
    Session session = sessions.get(sessionId);
    if (session != null) {
      synchronized (session) {

        if (session.state == State.REMOVED)
          return null;

        while (wait && session.state == State.RESERVED) {
          try {
            session.wait(1000);
          } catch (InterruptedException e) {
            throw new RuntimeException();
          }
        }

        if (session.state == State.RESERVED)
          throw new IllegalStateException(
              "Attempted to reserved session that is already reserved " + sessionId);
        if (session.state == State.REMOVED)
          return null;
        session.state = State.RESERVED;
      }
    }

    return session;

  }

  public void unreserveSession(Session session) {
    synchronized (session) {
      if (session.state == State.REMOVED)
        return;
      if (session.state != State.RESERVED)
        throw new IllegalStateException("Cannon unreserve, state: " + session.state);
      session.notifyAll();
      session.state = State.UNRESERVED;
      session.lastAccessTime = System.currentTimeMillis();
    }
  }

  public void unreserveSession(long sessionId) {
    Session session = getSession(sessionId);
    if (session != null) {
      unreserveSession(session);
    }
  }

  public Session getSession(long sessionId) {
    Session session = sessions.get(sessionId);

    if (session != null) {
      synchronized (session) {
        if (session.state == State.REMOVED) {
          return null;
        }
        session.lastAccessTime = System.currentTimeMillis();
      }
    }

    return session;
  }

  public Session removeSession(long sessionId) {
    return removeSession(sessionId, false);
  }

  public Session removeSession(long sessionId, boolean unreserve) {

    Session session = sessions.remove(sessionId);
    if (session != null) {
      boolean doCleanup = false;
      synchronized (session) {
        if (session.state != State.REMOVED) {
          if (unreserve) {
            unreserveSession(session);
          }
          doCleanup = true;
          session.state = State.REMOVED;
        }
      }

      if (doCleanup) {
        session.cleanup();
      }
    }

    return session;
  }

  private void sweep(final long maxIdle, final long maxUpdateIdle) {
    List sessionsToCleanup = new ArrayList<>();
    Iterator iter = sessions.values().iterator();
    while (iter.hasNext()) {
      Session session = iter.next();
      synchronized (session) {
        if (session.state == State.UNRESERVED) {
          long configuredIdle = maxIdle;
          if (session instanceof UpdateSession) {
            configuredIdle = maxUpdateIdle;
          }
          long idleTime = System.currentTimeMillis() - session.lastAccessTime;
          if (idleTime > configuredIdle) {
            log.info("Closing idle session from user={}, client={}, idle={}ms", session.getUser(),
                session.client, idleTime);
            iter.remove();
            sessionsToCleanup.add(session);
            session.state = State.REMOVED;
          }
        }
      }
    }

    // do clean up outside of lock for TabletServer in a synchronized block for simplicity vice a
    // synchronized list

    synchronized (idleSessions) {
      sessionsToCleanup.addAll(idleSessions);
      idleSessions.clear();
    }

    // perform cleanup for all of the sessions
    for (Session session : sessionsToCleanup) {
      if (!session.cleanup())
        synchronized (idleSessions) {
          idleSessions.add(session);
        }
    }
  }

  public void removeIfNotAccessed(final long sessionId, final long delay) {
    Session session = sessions.get(sessionId);
    if (session != null) {
      long tmp;
      synchronized (session) {
        tmp = session.lastAccessTime;
      }
      final long removeTime = tmp;
      TimerTask r = new TimerTask() {
        @Override
        public void run() {
          Session session2 = sessions.get(sessionId);
          if (session2 != null) {
            boolean shouldRemove = false;
            synchronized (session2) {
              if (session2.lastAccessTime == removeTime && session2.state == State.UNRESERVED) {
                session2.state = State.REMOVED;
                shouldRemove = true;
              }
            }

            if (shouldRemove) {
              log.info("Closing not accessed session from user=" + session2.getUser() + ", client="
                  + session2.client + ", duration=" + delay + "ms");
              sessions.remove(sessionId);
              session2.cleanup();
            }
          }
        }
      };

      SimpleTimer.getInstance(aconf).schedule(r, delay);
    }
  }

  public Map> getActiveScansPerTable() {
    Map> counts = new HashMap<>();

    Set> copiedIdleSessions = new HashSet<>();

    synchronized (idleSessions) {
      /**
       * Add sessions so that get the list returned in the active scans call
       */
      for (Session session : idleSessions) {
        copiedIdleSessions.add(Maps.immutableEntry(expiredSessionMarker, session));
      }
    }

    for (Entry entry : Iterables.concat(sessions.entrySet(), copiedIdleSessions)) {

      Session session = entry.getValue();
      @SuppressWarnings("rawtypes")
      ScanTask nbt = null;
      TableId tableID = null;

      if (session instanceof SingleScanSession) {
        SingleScanSession ss = (SingleScanSession) session;
        nbt = ss.nextBatchTask;
        tableID = ss.extent.getTableId();
      } else if (session instanceof MultiScanSession) {
        MultiScanSession mss = (MultiScanSession) session;
        nbt = mss.lookupTask;
        tableID = mss.threadPoolExtent.getTableId();
      }

      if (nbt == null)
        continue;

      ScanRunState srs = nbt.getScanRunState();

      if (srs == ScanRunState.FINISHED)
        continue;

      MapCounter stateCounts = counts.get(tableID);
      if (stateCounts == null) {
        stateCounts = new MapCounter<>();
        counts.put(tableID, stateCounts);
      }

      stateCounts.increment(srs, 1);
    }

    return counts;
  }

  public List getActiveScans() {

    final List activeScans = new ArrayList<>();
    final long ct = System.currentTimeMillis();
    final Set> copiedIdleSessions = new HashSet<>();

    synchronized (idleSessions) {
      /**
       * Add sessions so that get the list returned in the active scans call
       */
      for (Session session : idleSessions) {
        copiedIdleSessions.add(Maps.immutableEntry(expiredSessionMarker, session));
      }
    }

    for (Entry entry : Iterables.concat(sessions.entrySet(), copiedIdleSessions)) {
      Session session = entry.getValue();
      if (session instanceof SingleScanSession) {
        SingleScanSession ss = (SingleScanSession) session;

        ScanState state = ScanState.RUNNING;

        ScanTask nbt = ss.nextBatchTask;
        if (nbt == null) {
          state = ScanState.IDLE;
        } else {
          switch (nbt.getScanRunState()) {
            case QUEUED:
              state = ScanState.QUEUED;
              break;
            case FINISHED:
              state = ScanState.IDLE;
              break;
            case RUNNING:
            default:
              /* do nothing */
              break;
          }
        }

        ActiveScan activeScan =
            new ActiveScan(ss.client, ss.getUser(), ss.extent.getTableId().canonical(),
                ct - ss.startTime, ct - ss.lastAccessTime, ScanType.SINGLE, state,
                ss.extent.toThrift(), Translator.translate(ss.columnSet, Translators.CT),
                ss.ssiList, ss.ssio, ss.auths.getAuthorizationsBB(), ss.context);

        // scanId added by ACCUMULO-2641 is an optional thrift argument and not available in
        // ActiveScan constructor
        activeScan.setScanId(entry.getKey());
        activeScans.add(activeScan);

      } else if (session instanceof MultiScanSession) {
        MultiScanSession mss = (MultiScanSession) session;

        ScanState state = ScanState.RUNNING;

        ScanTask nbt = mss.lookupTask;
        if (nbt == null) {
          state = ScanState.IDLE;
        } else {
          switch (nbt.getScanRunState()) {
            case QUEUED:
              state = ScanState.QUEUED;
              break;
            case FINISHED:
              state = ScanState.IDLE;
              break;
            case RUNNING:
            default:
              /* do nothing */
              break;
          }
        }

        activeScans.add(new ActiveScan(mss.client, mss.getUser(),
            mss.threadPoolExtent.getTableId().canonical(), ct - mss.startTime,
            ct - mss.lastAccessTime, ScanType.BATCH, state, mss.threadPoolExtent.toThrift(),
            Translator.translate(mss.columnSet, Translators.CT), mss.ssiList, mss.ssio,
            mss.auths.getAuthorizationsBB(), mss.context));
      }
    }

    return activeScans;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy