store.java

来自「RESIN 3.2 最新源码」· Java 代码 · 共 1,996 行 · 第 1/4 页

JAVA
1,996
字号
/* * Copyright (c) 1998-2008 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.db.store;import com.caucho.db.Database;import com.caucho.lifecycle.Lifecycle;import com.caucho.log.Log;import com.caucho.sql.SQLExceptionWrapper;import com.caucho.util.L10N;import com.caucho.vfs.Path;import com.caucho.vfs.RandomAccessStream;import java.io.IOException;import java.lang.ref.SoftReference;import java.sql.SQLException;import java.util.logging.Level;import java.util.logging.Logger;/** * The store manages the block-based persistent store file.  Each table * will have its own store file, table.db. * * The store is block-based, where each block is 64k.  Block allocation * is tracked by a free block, block 0.  Each block is represented as a * two-byte value.  The first byte is the allocation code: free, row, * or used.  The second byte is a fragment allocation mask. * * Since 64k stores 32k entries, the allocation block can handle * a 2G database size.  If the database is larger, another free block * occurs at block 32k handling another 2G. * * The blocks are marked as free (00), row (01), used (10) or fragment(11). * Row-blocks are table rows, so a table iterator will only look at * the row blocks.   Used blocks are for special blocks like the * free list.  Fragments are for blobs. * * Each store has a unique id in the database.  The store id is merged with * the block number in the store to create a unique block id.  There are * 64k allowed stores (and therefore 64k tables), leaving 64 - 16 = 48 bits * for the blocks in a table, i.e. 2 ^ 48 blocks = 256T blocks. * * block index: the block number in the file. * * address: the address of a byte within the store, treating the file as a * flat file. * * block id: the unique id of the block in the database. * * <h3>Blobs and fragments</h3> * * Fragments are stored in 8k chunks with a single byte prefix indicating * its use. * * <h3>Transactions</h3> * * Fragments are not associated with transactions.  The rollback is * associated with a transaction. */public class Store {  private final static Logger log = Log.open(Store.class);  private final static L10N L = new L10N(Store.class);    public final static int BLOCK_BITS = 16;  public final static int BLOCK_SIZE = 1 << BLOCK_BITS;  public final static long BLOCK_INDEX_MASK = BLOCK_SIZE - 1;  public final static long BLOCK_MASK = ~ BLOCK_INDEX_MASK;  public final static long BLOCK_OFFSET_MASK = BLOCK_SIZE - 1;  private final static int ALLOC_BYTES_PER_BLOCK = 2;  private final static int ALLOC_CHUNK_SIZE = 1024 * ALLOC_BYTES_PER_BLOCK;    public final static int ALLOC_FREE      = 0x00;  public final static int ALLOC_ROW       = 0x01;  public final static int ALLOC_USED      = 0x02;  public final static int ALLOC_FRAGMENT  = 0x03;  public final static int ALLOC_INDEX     = 0x04;  public final static int ALLOC_MINI_FRAG = 0x05;  public final static int ALLOC_MASK      = 0x0f;  public final static int FRAGMENT_SIZE = 8 * 1024;  public final static int FRAGMENT_PER_BLOCK    = (int) (BLOCK_SIZE / FRAGMENT_SIZE);    public final static int MINI_FRAG_SIZE = 256;  public final static int MINI_FRAG_PER_BLOCK    = (int) ((BLOCK_SIZE - 64) / MINI_FRAG_SIZE);  public final static int MINI_FRAG_ALLOC_OFFSET    = MINI_FRAG_PER_BLOCK * MINI_FRAG_SIZE;    public final static long DATA_START = BLOCK_SIZE;    public final static int STORE_CREATE_END = 1024;    protected final Database _database;  protected final BlockManager _blockManager;  private final String _name;    private int _id;  private Path _path;  // If true, dirty blocks are written at the end of the transaction.  // Otherwise, they are buffered  private boolean _isFlushDirtyBlocksOnCommit = true;  private long _fileSize;  private long _blockCount;  private final Object _allocationLock = new Object();  private byte []_allocationTable;    private final Object _allocationWriteLock = new Object();  private int _allocDirtyMin = Integer.MAX_VALUE;  private int _allocDirtyMax;    private final Object _fragmentLock = new Object();  private final Object _miniFragLock = new Object();  private final Object _statLock = new Object();  // number of fragments currently used  private long _fragmentUseCount;    // number of minifragments currently used  private long _miniFragmentUseCount;  private Object _fileLock = new Object();  private SoftReference<RandomAccessWrapper> _cachedRowFile;    private Lock _rowLock;  private boolean _isCorrupted;    private final Lifecycle _lifecycle = new Lifecycle();  public Store(Database database, String name, Lock tableLock)  {    this(database, name, tableLock, database.getPath().lookup(name + ".db"));  }  /**   * Creates a new store.   *   * @param database the owning database.   * @param name the store name   * @param lock the table lock   * @param path the path to the files   */  public Store(Database database, String name, Lock rowLock, Path path)  {    _database = database;    _blockManager = _database.getBlockManager();        _name = name;    _path = path;    if (path == null)      throw new NullPointerException();    _id = _blockManager.allocateStoreId();    if (rowLock == null)      rowLock = new Lock("row-lock:" + _name + ":" + _id);    _rowLock = rowLock;  }  /**   * Creates an independent store.   */  public static Store create(Path path)    throws IOException, SQLException  {    Database db = new Database();    db.init();    Store store = new Store(db, "temp", null, path);    if (path.canRead())      store.init();    else      store.create();    return store;  }  /**   * If true, dirty blocks are written at commit time.   */  public void setFlushDirtyBlocksOnCommit(boolean flushOnCommit)  {    _isFlushDirtyBlocksOnCommit = flushOnCommit;  }  /**   * If true, dirty blocks are written at commit time.   */  public boolean isFlushDirtyBlocksOnCommit()  {    return _isFlushDirtyBlocksOnCommit;  }  /**   * Returns the store's name.   */  public String getName()  {    return _name;  }  /**   * Returns the store's id.   */  public int getId()  {    return _id;  }  /**   * Returns the table's lock.   */  public Lock getLock()  {    return _rowLock;  }  /**   * Returns the block manager.   */  public BlockManager getBlockManager()  {    return _blockManager;  }  public void setCorrupted(boolean isCorrupted)  {    _isCorrupted = isCorrupted;  }  public boolean isCorrupted()  {    return _isCorrupted;  }  /**   * Returns the file size.   */  public long getFileSize()  {    return _fileSize;  }  /**   * Returns the block count.   */  public long getBlockCount()  {    return _blockCount;  }  /**   * Converts from the block index to the address for database   * storage.   */  private static long blockIndexToAddr(long blockIndex)  {    return blockIndex << BLOCK_BITS;  }  /**   * Converts from the block index to the unique block id.   */  private final long blockIndexToBlockId(long blockIndex)  {    return (blockIndex << BLOCK_BITS) + _id;  }  /**   * Converts from the block index to the address for database   * storage.   */  private static long blockIdToIndex(long blockId)  {    return blockId >> BLOCK_BITS;  }  /**   * Converts from the block index to the unique block id.   */  public final long addressToBlockId(long address)  {    return (address & BLOCK_MASK) + _id;  }  /**   * Converts from the block index to the unique block id.   */  public static long blockIdToAddress(long blockId)  {    return (blockId & BLOCK_MASK);  }  /**   * Converts from the block index to the unique block id.   */  public static long blockIdToAddress(long blockId, int offset)  {    return (blockId & BLOCK_MASK) + offset;  }  /**   * Returns the current number of fragments used.   */  public long getTotalFragmentSize()  {    return _fragmentUseCount * FRAGMENT_SIZE;  }  /**   * Creates the store.   */  public void create()    throws IOException, SQLException  {    if (! _lifecycle.toActive())      return;        log.finer(this + " create");    _path.getParent().mkdirs();    if (_path.exists())      throw new SQLException(L.l("Table '{0}' already exists.  CREATE can not override an existing table.", _name));    _allocationTable = new byte[ALLOC_CHUNK_SIZE];    // allocates the allocation table itself    setAllocation(0, ALLOC_USED);    // allocates the header information    setAllocation(1, ALLOC_USED);    byte []buffer = new byte[BLOCK_SIZE];    writeBlock(0, buffer, 0, BLOCK_SIZE);    writeBlock(BLOCK_SIZE, buffer, 0, BLOCK_SIZE);        writeBlock(0, _allocationTable, 0, _allocationTable.length);    _blockCount = 2;  }    public void init()    throws IOException  {    if (! _lifecycle.toActive())      return;        log.finer(this + " init");    RandomAccessWrapper wrapper = openRowFile();    try {      RandomAccessStream file = wrapper.getFile();            _fileSize = file.getLength();      _blockCount = ((_fileSize + BLOCK_SIZE - 1) / BLOCK_SIZE);      int allocCount = (int) (_blockCount * ALLOC_BYTES_PER_BLOCK);      allocCount += ALLOC_CHUNK_SIZE - allocCount % ALLOC_CHUNK_SIZE;      _allocationTable = new byte[allocCount];      for (int i = 0; i < allocCount; i += BLOCK_SIZE) {	int len = allocCount - i;		if (BLOCK_SIZE < len)	  len = BLOCK_SIZE;		readBlock((long) i / ALLOC_BYTES_PER_BLOCK * BLOCK_SIZE,		  _allocationTable, i, len);      }    } finally {      wrapper.close();    }  }  public void remove()    throws SQLException  {    try {      Path path = _path;      _path = null;      close();      if (path != null)	path.remove();    } catch (IOException e) {      throw new SQLExceptionWrapper(e);    }  }  /**   * Returns the first block id which contains a row.   *   * @return the block id of the first row block   */  public long firstRow(long blockId)    throws IOException  {    return firstBlock(blockId, ALLOC_ROW);  }  /**   * Returns the first block id which contains a fragment.   *   * @return the block id of the first row block   */  public long firstFragment(long blockId)    throws IOException  {    return firstBlock(blockId, ALLOC_FRAGMENT);  }  /**   * Returns the first block id which contains a row.   *   * @return the block id of the first row block   */  public long firstBlock(long blockId, int type)    throws IOException  {    if (blockId <= BLOCK_SIZE)      blockId = BLOCK_SIZE;        long blockIndex = blockId >> BLOCK_BITS;    synchronized (_allocationLock) {      for (; blockIndex < _blockCount; blockIndex++) {	if (getAllocation(blockIndex) == type)	  return blockIndexToBlockId(blockIndex);      }    }    return -1;  }  /**   * Returns the matching block.   */  public final Block readBlock(long blockAddress)    throws IOException  {    long blockId = addressToBlockId(blockAddress);        Block block = _blockManager.getBlock(this, blockId);    try {      block.read();          return block;    } catch (IOException e) {      block.free();      throw e;    } catch (RuntimeException e) {      block.free();      throw e;    }  }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?