Package com.caucho.server.distcache

Source Code of com.caucho.server.distcache.MnodeStore$CacheMapConnection

/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.server.distcache;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.caucho.util.Alarm;
import com.caucho.util.AlarmListener;
import com.caucho.util.FreeList;
import com.caucho.util.HashKey;
import com.caucho.util.JdbcUtil;


/**
* Manages backing for the cache map.
*/
public class MnodeStore implements AlarmListener {
  private static final Logger log
    = Logger.getLogger(MnodeStore.class.getName());

  private FreeList<CacheMapConnection> _freeConn
    = new FreeList<CacheMapConnection>(32);

  private final String _serverName;
 
  private final String _tableName;

  private DataSource _dataSource;

  private String _loadQuery;

  private String _insertQuery;
  private String _updateSaveQuery;
  private String _updateUpdateTimeQuery;

  private String _updateVersionQuery;

  private String _expireQuery;

  private String _countQuery;
  private String _updatesSinceQuery;
  private String _globalUpdatesSinceQuery;
  private String _remoteUpdatesSinceQuery;

  private long _serverVersion;
  private long _startupLastUpdateTime;

  private Alarm _alarm;
 
  // private long _expireReaperTimeout = 60 * 60 * 1000L;
  private long _expireReaperTimeout = 15 * 60 * 1000L;

  public MnodeStore(DataSource dataSource,
                    String tableName,
                    String serverName)
    throws Exception
  {
    _dataSource = dataSource;
    _serverName = serverName;
    _tableName = tableName;

    if (dataSource == null)
      throw new NullPointerException();

    if (_tableName == null)
      throw new NullPointerException();

    _dataSource = dataSource;
  }

  /**
   * Returns the data source.
   */
  public DataSource getDataSource()
  {
    return _dataSource;
  }

  /**
   * Returns the data source.
   */
  public String getTableName()
  {
    return _tableName;
  }

  /**
   * Returns the max update time detected on startup.
   */
  public long getStartupLastUpdateTime()
  {
    return _startupLastUpdateTime;
  }

  /**
   * Returns the max update time detected on startup.
   */
  public long getStartupLastUpdateTime(HashKey cacheKey)
  {
    Connection conn = null;

    try {
      conn = _dataSource.getConnection();
     
      String sql = ("SELECT MAX(update_time)"
                    + " FROM " + _tableName
                    + " WHERE cache_id=?");
       
      PreparedStatement pStmt = conn.prepareStatement(sql);
     
      pStmt.setBytes(1, cacheKey.getHash());

      ResultSet rs = pStmt.executeQuery(sql);
     
      if (rs.next()) {
        return rs.getLong(1);
      }
     
      return 0;
    } catch (SQLException e) {
      log.log(Level.WARNING, e.toString(), e);
     
      return 0;
    } finally {
      JdbcUtil.close(conn);
    }
  }

  //
  // lifecycle
  //

  protected void init()
    throws Exception
  {
    _loadQuery = ("SELECT value,cache_id,flags,server_version,item_version,expire_timeout,idle_timeout,lease_timeout,local_read_timeout,update_time"
                  + " FROM " + _tableName
                  + " WHERE id=?");

    _insertQuery = ("INSERT into " + _tableName
                    + " (id,value,cache_id,flags,"
                    + "  item_version,server_version,"
                    + "  expire_timeout,idle_timeout,"
                    + "  lease_timeout,local_read_timeout,"
                    + "  update_time)"
                    + " VALUES (?,?,?,?,?,?,?,?,?,?,?)");

    _updateSaveQuery
      = ("UPDATE " + _tableName
         + " SET value=?,"
         + "     server_version=?,item_version=?,"
         + "     idle_timeout=?,update_time=?"
         + " WHERE id=? AND item_version<=?");

    _updateUpdateTimeQuery
      = ("UPDATE " + _tableName
         + " SET idle_timeout=?,update_time=?"
         + " WHERE id=? AND item_version=?");

    _updateVersionQuery = ("UPDATE " + _tableName
                           + " SET update_time=?, server_version=?"
                           + " WHERE id=? AND value=?");

    _expireQuery = ("DELETE FROM " + _tableName
                     + " WHERE update_time + 5 * idle_timeout / 4 < ?"
                     + " OR update_time + expire_timeout < ?");

    _countQuery = "SELECT count(*) FROM " + _tableName;

    _updatesSinceQuery = ("SELECT id,value,cache_id,flags,item_version,update_time,expire_timeout,idle_timeout,lease_timeout,local_read_timeout"
                          + " FROM " + _tableName
                          + " WHERE ? <= update_time"
                          + " LIMIT 1024");

    int global = CacheConfig.FLAG_GLOBAL;

    _globalUpdatesSinceQuery = ("SELECT id,value,cache_id,flags,item_version,update_time,"
                                + " expire_timeout,idle_timeout,lease_timeout,local_read_timeout"
                                + " FROM " + _tableName
                                + " WHERE ? <= update_time"
                                + "   AND bitand(flags, " + global + ") <> 0"
                                + " LIMIT 1024");

    _remoteUpdatesSinceQuery = ("SELECT id,value,cache_id,flags,item_version,update_time,expire_timeout,idle_timeout,lease_timeout,local_read_timeout"
                                + " FROM " + _tableName
                                + " WHERE ? = cache_id AND ? <= update_time"
                                + " LIMIT 1024");

    initDatabase();

    _serverVersion = initVersion();
    _startupLastUpdateTime = initLastUpdateTime();

    _alarm = new Alarm(this);
    handleAlarm(_alarm);
  }

  /**
   * Create the database, initializing if necessary.
   */
  protected void initDatabase()
    throws Exception
  {
    Connection conn = _dataSource.getConnection();

    try {
      Statement stmt = conn.createStatement();

      try {
        String sql = ("SELECT id, value, cache_id, flags,"
                      + "     expire_timeout, idle_timeout,"
                      + "     lease_timeout, local_read_timeout,"
                      + "     update_time,"
                      + "     server_version, item_version"
                      + " FROM " + _tableName + " WHERE 1=0");

        ResultSet rs = stmt.executeQuery(sql);
        rs.next();
        rs.close();

        return;
      } catch (Exception e) {
        log.log(Level.ALL, e.toString(), e);
        log.finer(this + " " + e.toString());
      }

      try {
        stmt.executeQuery("DROP TABLE " + _tableName);
      } catch (Exception e) {
        log.log(Level.ALL, e.toString(), e);
        log.finer(this + " " + e.toString());
      }

      String sql = ("CREATE TABLE " + _tableName + " (\n"
                    + "  id BINARY(32) PRIMARY KEY,\n"
                    + "  value BINARY(32),\n"
                    + "  cache_id BINARY(32),\n"
                    + "  expire_timeout BIGINT,\n"
                    + "  idle_timeout BIGINT,\n"
                    + "  lease_timeout BIGINT,\n"
                    + "  local_read_timeout BIGINT,\n"
                    + "  update_time BIGINT,\n"
                    + "  item_version BIGINT,\n"
                    + "  flags INTEGER,\n"
                    + "  server_version INTEGER)");

      log.fine(sql);

      stmt.executeUpdate(sql);
    } finally {
      conn.close();
    }
  }

  /**
   * Returns the version
   */
  private int initVersion()
    throws Exception
  {
    Connection conn = _dataSource.getConnection();

    try {
      Statement stmt = conn.createStatement();

      String sql = ("SELECT MAX(server_version)"
                    + " FROM " + _tableName);

      ResultSet rs = stmt.executeQuery(sql);
      if (rs.next())
        return rs.getInt(1) + 1;
    } finally {
      conn.close();
    }

    return 1;
  }

  /**
   * Returns the maximum update time on startup
   */
  private long initLastUpdateTime()
    throws Exception
  {
    Connection conn = _dataSource.getConnection();

    try {
      Statement stmt = conn.createStatement();

      String sql = ("SELECT MAX(update_time)"
                    + " FROM " + _tableName);

      ResultSet rs = stmt.executeQuery(sql);
      if (rs.next())
        return rs.getLong(1);
    } finally {
      conn.close();
    }

    return 0;
  }

  public void close()
  {
    Alarm alarm = _alarm;
    _alarm = null;

    if (alarm != null)
      alarm.close();
  }

  /**
   * Returns the maximum update time on startup
   */
  public ArrayList<CacheData> getUpdates(long updateTime, int offset)
  {
    return getUpdates(updateTime, offset, false);
  }

  /**
   * Returns the maximum update time on startup
   */
  public ArrayList<CacheData> getGlobalUpdates(long updateTime, int offset)
  {
    return getUpdates(updateTime, offset, true);
  }

  /**
   * Returns the maximum update time on startup
   */
  private ArrayList<CacheData> getUpdates(long updateTime,
                                          int offset,
                                          boolean isGlobal)
  {
    Connection conn = null;

    try {
      conn = _dataSource.getConnection();

      String sql;

      if (isGlobal)
        sql = _globalUpdatesSinceQuery;
      else
        sql = _updatesSinceQuery;
      /*
      sql = ("SELECT id,value,flags,item_version,update_time"
             + " FROM " + _tableName
             + " WHERE ?<=update_time"
             + " LIMIT 1024");
      */

      PreparedStatement pstmt = conn.prepareStatement(sql);

      pstmt.setLong(1, updateTime);

      ArrayList<CacheData> entryList = new ArrayList<CacheData>();

      ResultSet rs = pstmt.executeQuery();

      rs.relative(offset);
      while (rs.next()) {
        byte []keyHash = rs.getBytes(1);

        byte []valueHash = rs.getBytes(2);
        byte []cacheHash = rs.getBytes(3);
        int flags = rs.getInt(4);
        long version = rs.getLong(5);
        long itemUpdateTime = rs.getLong(6);
        long expireTimeout = rs.getLong(7);
        long idleTimeout = rs.getLong(8);
        long leaseTimeout = rs.getLong(9);
        long localReadTimeout = rs.getLong(10);

        HashKey value = valueHash != null ? new HashKey(valueHash) : null;
        HashKey cacheKey = cacheHash != null ? new HashKey(cacheHash) : null;

        if (keyHash == null)
          continue;

        entryList.add(new CacheData(new HashKey(keyHash),
                                    value,
                                    cacheKey,
                                    flags,
                                    version,
                                    itemUpdateTime,
                                    expireTimeout,
                                    idleTimeout,
                                    leaseTimeout,
                                    localReadTimeout));
      }

      if (entryList.size() > 0)
        return entryList;
      else
        return null;
    } catch (SQLException e) {
      log.log(Level.WARNING, e.toString(), e);
    } finally {
      JdbcUtil.close(conn);
    }

    return null;
  }

  /**
   * Returns the maximum update time on startup
   */
  public ArrayList<CacheData> getUpdates(HashKey cacheKey,
                                          long updateTime,
                                          int offset)
  {
    Connection conn = null;

    try {
      conn = _dataSource.getConnection();

      String sql;

      sql = _remoteUpdatesSinceQuery;

      PreparedStatement pstmt = conn.prepareStatement(sql);

      pstmt.setBytes(1, cacheKey.getHash());
      pstmt.setLong(2, updateTime);

      ArrayList<CacheData> entryList = new ArrayList<CacheData>();

      ResultSet rs = pstmt.executeQuery();

      rs.relative(offset);
      while (rs.next()) {
        byte []keyHash = rs.getBytes(1);

        byte []valueHash = rs.getBytes(2);
        byte []cacheHash = rs.getBytes(3);
        int flags = rs.getInt(4);
        long version = rs.getLong(5);
        long itemUpdateTime = rs.getLong(6);
        long expireTimeout = rs.getLong(7);
        long idleTimeout = rs.getLong(8);
        long leaseTimeout = rs.getLong(9);
        long localReadTimeout = rs.getLong(10);

        HashKey value = valueHash != null ? new HashKey(valueHash) : null;
        /*
        HashKey cacheKey = cacheHash != null ? new HashKey(cacheHash) : null;
        */

        if (keyHash == null)
          continue;

        entryList.add(new CacheData(new HashKey(keyHash),
                                    value,
                                    cacheKey,
                                    flags,
                                    version,
                                    itemUpdateTime,
                                    expireTimeout,
                                    idleTimeout,
                                    leaseTimeout,
                                    localReadTimeout));
      }

      if (entryList.size() > 0)
        return entryList;
      else
        return null;
    } catch (SQLException e) {
      log.log(Level.WARNING, e.toString(), e);
    } finally {
      JdbcUtil.close(conn);
    }

    return null;
  }

  /**
   * Reads the object from the data store.
   *
   * @param id the hash identifier for the data
   * @return true on successful load
   */
  public MnodeValue load(HashKey id)
  {
    CacheMapConnection conn = null;

    try {
      conn = getConnection();

      PreparedStatement pstmt = conn.prepareLoad();
      pstmt.setBytes(1, id.getHash());

      ResultSet rs = pstmt.executeQuery();

      if (rs.next()) {
        byte []valueHash = rs.getBytes(1);
        byte []cacheHash = rs.getBytes(2);
        int flags = rs.getInt(3);
        long serverVersion = rs.getLong(4);
        long itemVersion = rs.getLong(5);
        long expireTimeout = rs.getLong(6);
        long idleTimeout = rs.getLong(7);
        long leaseTimeout = rs.getLong(8);
        long localReadTimeout = rs.getLong(9);
        long updateTime = rs.getLong(10);
        long accessTime = Alarm.getExactTime();

        HashKey cacheHashKey
          = cacheHash != null ? new HashKey(cacheHash) : null;

        HashKey valueHashKey
          = valueHash != null ? new HashKey(valueHash) : null;

        if (log.isLoggable(Level.FINER))
          log.finer(this + " load " + id + " value=" + valueHashKey + " cache=" + cacheHashKey);
       
        return new MnodeValue(valueHashKey, null, cacheHashKey,
                              flags, itemVersion,
                              expireTimeout, idleTimeout,
                              leaseTimeout, localReadTimeout,
                              accessTime, updateTime,
                              serverVersion == _serverVersion,
                              false);
      }

      if (log.isLoggable(Level.FINEST))
        log.finest(this + " load: no mnode for cache key " + id);

      return null;
    } catch (SQLException e) {
      log.log(Level.FINE, e.toString(), e);
    } finally {
      if (conn != null)
        conn.close();
    }

    return null;
  }

  /**
   * Stores the data, returning true on success
   *
   * @param id the key hash
   * @param value the value hash
   * @param idleTimeout the item's timeout
   */
  public boolean insert(HashKey id,
                        HashKey value,
                        HashKey cacheId,
                        int flags,
                        long version,
                        long expireTimeout,
                        long idleTimeout,
                        long leaseTimeout,
                        long localReadTimeout)
  {
    CacheMapConnection conn = null;

    try {
      conn = getConnection();

      PreparedStatement stmt = conn.prepareInsert();
      stmt.setBytes(1, id.getHash());

      if (value != null)
        stmt.setBytes(2, value.getHash());
      else
        stmt.setBytes(2, null);

      if (cacheId != null)
        stmt.setBytes(3, cacheId.getHash());
      else
        stmt.setBytes(3, null);

      stmt.setLong(4, flags);
      stmt.setLong(5, version);
      stmt.setLong(6, _serverVersion);
      stmt.setLong(7, expireTimeout);
      stmt.setLong(8, idleTimeout);
      stmt.setLong(9, leaseTimeout);
      stmt.setLong(10, localReadTimeout);
      stmt.setLong(11, Alarm.getCurrentTime());

      int count = stmt.executeUpdate();

      if (log.isLoggable(Level.FINER))
        log.finer(this + " insert key=" + id + " value=" + value + " count=" + count);

      return true;
    } catch (SQLException e) {
      log.log(Level.FINER, e.toString(), e);
    } finally {
      if (conn != null)
        conn.close();
    }

    return false;
  }

  /**
   * Stores the data, returning true on success
   *
   * @param id the key hash
   * @param value the value hash
   * @param idleTimeout the item's timeout
   */
  public boolean updateSave(HashKey id,
                            HashKey value,
                            long itemVersion,
                            long idleTimeout)
  {
    CacheMapConnection conn = null;

    try {
      conn = getConnection();

      PreparedStatement stmt = conn.prepareUpdateSave();
      if (value != null)
        stmt.setBytes(1, value.getHash());
      else
        stmt.setBytes(1, null);
      stmt.setLong(2, _serverVersion);
      stmt.setLong(3, itemVersion);
      stmt.setLong(4, idleTimeout);
      stmt.setLong(5, Alarm.getCurrentTime());

      stmt.setBytes(6, id.getHash());
      stmt.setLong(7, itemVersion);

      int count = stmt.executeUpdate();

      if (log.isLoggable(Level.FINER))
        log.finer(this + " updateSave key=" + id + " value=" + value);

      return count > 0;
    } catch (SQLException e) {
      log.log(Level.FINER, e.toString(), e);
    } finally {
      if (conn != null)
        conn.close();
    }

    return false;
  }

  /**
   * Updates the update time, returning true on success
   *
   * @param id the key hash
   * @param itemVersion the value version
   * @param idleTimeout the item's timeout
   * @param updateTime the item's timeout
   */
  public boolean updateUpdateTime(HashKey id,
                                  long itemVersion,
                                  long idleTimeout,
                                  long updateTime)
  {
    CacheMapConnection conn = null;

    try {
      conn = getConnection();

      PreparedStatement stmt = conn.prepareUpdateUpdateTime();
      stmt.setLong(1, idleTimeout);
      stmt.setLong(2, updateTime);

      stmt.setBytes(3, id.getHash());
      stmt.setLong(4, itemVersion);

      int count = stmt.executeUpdate();

      if (log.isLoggable(Level.FINER))
        log.finer(this + " updateUpdateTime key=" + id);

      return count > 0;
    } catch (SQLException e) {
      log.log(Level.FINER, e.toString(), e);
    } finally {
      if (conn != null)
        conn.close();
    }

    return false;
  }

  /**
   * Clears the expired data
   */
  public void removeExpiredData()
  {
    CacheMapConnection conn = null;
    try {
      conn = getConnection();
      PreparedStatement pstmt = conn.prepareExpire();

      long now = Alarm.getCurrentTime();

      pstmt.setLong(1, now);
      pstmt.setLong(2, now);
     
      int count = pstmt.executeUpdate();

      if (count > 0)
        log.finer(this + " expired " + count + " old data");
    } catch (Exception e) {
      e.printStackTrace();
      log.log(Level.FINE, e.toString(), e);
    } finally {
      conn.close();
    }
  }

  //
  // statistics
  //

  public long getCount()
  {
    CacheMapConnection conn = null;

    try {
      conn = getConnection();
      PreparedStatement stmt = conn.prepareCount();

      ResultSet rs = stmt.executeQuery();

      if (rs != null && rs.next()) {
        long value = rs.getLong(1);

        rs.close();

        return value;
      }

      return -1;
    } catch (SQLException e) {
      log.log(Level.FINE, e.toString(), e);
    } finally {
      conn.close();
    }

    return -1;
  }

  public void handleAlarm(Alarm alarm)
  {
    if (_dataSource != null) {
      try {
        removeExpiredData();
      } finally {
        alarm.queue(_expireReaperTimeout);
      }
    }
  }

  public void destroy()
  {
    _dataSource = null;
    _freeConn = null;

    Alarm alarm = _alarm;
    _alarm = null;

    if (alarm != null)
      alarm.dequeue();
  }

  private CacheMapConnection getConnection()
    throws SQLException
  {
    CacheMapConnection cConn = _freeConn.allocate();

    if (cConn == null) {
      Connection conn = _dataSource.getConnection();
      cConn = new CacheMapConnection(conn);
    }

    return cConn;
  }

  @Override
  public String toString()
  {
    return getClass().getSimpleName() "[" + _serverName + "]";
  }

  class CacheMapConnection {
    private Connection _conn;

    private PreparedStatement _loadStatement;

    private PreparedStatement _insertStatement;
    private PreparedStatement _updateSaveStatement;
    private PreparedStatement _updateUpdateTimeStatement;

    private PreparedStatement _updateVersionStatement;

    private PreparedStatement _expireStatement;

    private PreparedStatement _countStatement;

    CacheMapConnection(Connection conn)
    {
      _conn = conn;
    }

    PreparedStatement prepareLoad()
      throws SQLException
    {
      if (_loadStatement == null)
        _loadStatement = _conn.prepareStatement(_loadQuery);

      return _loadStatement;
    }

    PreparedStatement prepareInsert()
      throws SQLException
    {
      if (_insertStatement == null)
        _insertStatement = _conn.prepareStatement(_insertQuery);

      return _insertStatement;
    }

    PreparedStatement prepareUpdateSave()
      throws SQLException
    {
      if (_updateSaveStatement == null)
        _updateSaveStatement = _conn.prepareStatement(_updateSaveQuery);

      return _updateSaveStatement;
    }

    PreparedStatement prepareUpdateUpdateTime()
      throws SQLException
    {
      if (_updateUpdateTimeStatement == null) {
        _updateUpdateTimeStatement
          = _conn.prepareStatement(_updateUpdateTimeQuery);
      }

      return _updateUpdateTimeStatement;
    }

    PreparedStatement prepareUpdateVersion()
      throws SQLException
    {
      if (_updateVersionStatement == null)
        _updateVersionStatement = _conn.prepareStatement(_updateVersionQuery);

      return _updateVersionStatement;
    }

    PreparedStatement prepareExpire()
      throws SQLException
    {
      if (_expireStatement == null)
        _expireStatement = _conn.prepareStatement(_expireQuery);

      return _expireStatement;
    }

    PreparedStatement prepareCount()
      throws SQLException
    {
      if (_countStatement == null)
        _countStatement = _conn.prepareStatement(_countQuery);

      return _countStatement;
    }

    void close()
    {
      if (_freeConn == null || ! _freeConn.freeCareful(this)) {
        try {
          _conn.close();
        } catch (SQLException e) {
        }
      }
    }
  }
}
TOP

Related Classes of com.caucho.server.distcache.MnodeStore$CacheMapConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.