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

com.caucho.db.table.TableRowAllocator Maven / Gradle / Ivy

/*
 * Copyright (c) 1998-2018 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.table;

import java.io.IOException;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.db.block.Block;
import com.caucho.db.block.BlockStore;
import com.caucho.db.xa.DbTransaction;
import com.caucho.env.thread.AbstractTaskWorker;
import com.caucho.inject.Module;
import com.caucho.util.Friend;

/**
 * Table format:
 *
 * 
 * Block 0: allocation table
 * Block 1: fragment table
 * Block 2: table definition
 *   0    - store data
 *   1024 - table data
 *    1024 - index pointers
 *   2048 - CREATE text
 * Block 3: first data
 * 
*/ @Module class TableRowAllocator extends AbstractTaskWorker { private final static Logger log = Logger.getLogger(TableRowAllocator.class.getName()); private static final int FREE_ROW_BLOCK_SIZE = 256; public final static long ROW_CLOCK_MIN = 1024; private final Table _table; private final int _rowLength; private final int _rowsPerBlock; private final int _rowEnd; private final AtomicLongArray _insertFreeRowBlockArray = new AtomicLongArray(FREE_ROW_BLOCK_SIZE); private final AtomicLong _insertFreeRowBlockHead = new AtomicLong(); private final AtomicLong _insertFreeRowBlockTail = new AtomicLong(); private long _rowTailTop = BlockStore.BLOCK_SIZE * FREE_ROW_BLOCK_SIZE; private final AtomicLong _rowTailOffset = new AtomicLong(); // clock counters for row insert allocation private long _rowClockTop; private long _rowClockOffset; private long _clockRowFree; private long _clockRowUsed; private long _clockBlockFree; private long _clockRowDeleteCount; TableRowAllocator(Table table) { _table = table; _rowsPerBlock = table.getRowsPerBlock(); _rowLength = table.getRowLength(); _rowEnd = table.getRowEnd(); } @Friend(Table.class) int allocateRow(Block block, DbTransaction xa) throws IOException, SQLException, InterruptedException { Lock blockLock = block.getWriteLock(); if (! blockLock.tryLock(xa.getTimeout(), TimeUnit.MILLISECONDS)) { log.warning("Unable to lock allocate table: " + xa.getTimeout() + "ms"); return -1; } try { block.read(); byte []buffer = block.getBuffer(); int rowOffset = 0; for (; rowOffset < _rowEnd; rowOffset += _rowLength) { if (buffer[rowOffset] == 0) { buffer[rowOffset] = Table.ROW_ALLOC; block.setDirty(rowOffset, rowOffset + 1); return rowOffset; } } } finally { blockLock.unlock(); } return -1; } // // row allocation // @Friend(Table.class) long allocateInsertRowBlock() throws IOException { long blockId = allocateRowBlockId(); if (blockId != 0) { return blockId; } long rowTailOffset = _rowTailOffset.get(); blockId = _table.firstRowBlock(rowTailOffset); if (blockId <= 0) { Block block = _table.allocateRow(); blockId = block.getBlockId(); // System.out.println("ALLOC: " + blockId + " " + _rowTailOffset.get() + " " + _rowTailTop); block.free(); } _rowTailOffset.compareAndSet(rowTailOffset, blockId + BlockStore.BLOCK_SIZE); return blockId; } // // allocator worker/single threaded // private void fillFreeRows() { if (_rowTailOffset.get() < _rowTailTop || isClosed()) { return; } while (scanClock()) { if (! resetClock()) { return; } } } private boolean scanClock() { while (! isClosed () && isFreeRowBlockIdAvailable()) { long clockBlockId = _rowClockOffset; try { clockBlockId = _table.firstRowBlock(clockBlockId); if (clockBlockId < 0) { _rowClockOffset = _rowClockTop; return true; } if (isRowBlockFree(clockBlockId)) { _clockBlockFree++; freeRowBlockId(clockBlockId); } } catch (IOException e) { log.log(Level.FINE, e.toString(), e); clockBlockId = _rowClockTop; } finally { _rowClockOffset = clockBlockId + BlockStore.BLOCK_SIZE; } } return false; } private boolean resetClock() { // force 50% free rows before clock starts again long newRowCount = (_clockRowUsed - _clockRowFree) / _rowsPerBlock; long tableRowDeleteCount = _table.getRowDeleteCount(); long deleteRowCount = (tableRowDeleteCount - _clockRowDeleteCount) / _rowsPerBlock; if (newRowCount > 0) { newRowCount = Math.max(newRowCount, ROW_CLOCK_MIN); } else if (_clockRowFree < ROW_CLOCK_MIN && _rowClockOffset > 0) { // minimum 256 blocks of free rows newRowCount = ROW_CLOCK_MIN; } else if (deleteRowCount < ROW_CLOCK_MIN) { // if none deleted, don't clock newRowCount = ROW_CLOCK_MIN; } _rowClockOffset = 0; _rowClockTop = _rowTailOffset.get(); _clockRowUsed = 0; _clockRowFree = 0; _clockRowDeleteCount = tableRowDeleteCount; if (newRowCount > 0) { _rowTailTop = _rowTailOffset.get() + newRowCount * _rowLength; // _rowClockOffset = _rowTailTop; return false; } else { // System.out.println("RESET: used=" + _clockRowUsed + " free=" + _clockRowFree + " top=" + _rowTailTop); return true; } } /** * Test if any row in the block is free */ private boolean isRowBlockFree(long blockId) throws IOException { if (isClosed()) { return false; } Block block = _table.readBlock(blockId); try { int rowOffset = 0; byte []buffer = block.getBuffer(); boolean isFree = false; for (; rowOffset < _rowEnd; rowOffset += _rowLength) { if (buffer[rowOffset] == 0) { isFree = true; _clockRowFree++; } else { _clockRowUsed++; } } return isFree; } finally { block.free(); } } private boolean isFreeRowBlockIdAvailable() { long head = _insertFreeRowBlockHead.get(); long tail = _insertFreeRowBlockTail.get(); return (head + 1) % FREE_ROW_BLOCK_SIZE != tail % FREE_ROW_BLOCK_SIZE; } private long allocateRowBlockId() { while (true) { long tail = _insertFreeRowBlockTail.get(); long head = _insertFreeRowBlockHead.get(); if (head == tail) { allocateNewRows(); return 0; } int index = (int) (tail % FREE_ROW_BLOCK_SIZE); long blockId = _insertFreeRowBlockArray.getAndSet(index, 0); long nextTail = tail + 1; _insertFreeRowBlockTail.compareAndSet(tail, nextTail); if (blockId > 0) { int size = (int) (head - tail); if (2 * size < FREE_ROW_BLOCK_SIZE) { allocateNewRows(); } return blockId; } } } @Friend(Table.class) void freeRowBlockId(long blockId) { while (true) { long head = _insertFreeRowBlockHead.get(); long tail = _insertFreeRowBlockTail.get(); long nextHead = head + 1; if (nextHead % FREE_ROW_BLOCK_SIZE == tail % FREE_ROW_BLOCK_SIZE) return; _insertFreeRowBlockHead.compareAndSet(head, nextHead); int index = (int) (head % FREE_ROW_BLOCK_SIZE); if (_insertFreeRowBlockArray.compareAndSet(index, 0, blockId)) { return; } } } private void allocateNewRows() { // wake(); synchronized (this) { fillFreeRows(); } } @Override public long runTask() { if (true) { Thread.dumpStack(); return -1; } fillFreeRows(); return -1; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy