001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.persist;
032:
033: import java.io.IOException;
034:
035: import org.hsqldb.Trace;
036: import org.hsqldb.lib.Iterator;
037: import org.hsqldb.lib.ObjectComparator;
038: import org.hsqldb.lib.Sort;
039: import org.hsqldb.lib.StopWatch;
040: import org.hsqldb.store.ObjectCacheHashMap;
041:
042: /**
043: * New implementation of row caching for CACHED tables.<p>
044: *
045: * Manages memory for the cache map and its contents based on least recently
046: * used clearup.<p>
047: * Also provides services for selecting rows to be saved and passing them
048: * to DataFileCache.<p>
049: *
050: * @author fredt@users
051: * @version 1.8.0
052: */
053: public class Cache {
054:
055: final DataFileCache dataFileCache;
056: private int capacity; // number of Rows
057: private long bytesCapacity; // number of bytes
058: private final CachedObjectComparator rowComparator;
059:
060: //
061: private CachedObject[] rowTable;
062:
063: //
064: private final ObjectCacheHashMap cacheMap;
065: long cacheBytesLength;
066:
067: // for testing
068: StopWatch saveAllTimer = new StopWatch(false);
069: StopWatch makeRowTimer = new StopWatch(false);
070: StopWatch sortTimer = new StopWatch(false);
071: int makeRowCount = 0;
072: int saveRowCount = 0;
073:
074: Cache(DataFileCache dfc) {
075:
076: dataFileCache = dfc;
077: capacity = dfc.capacity();
078: bytesCapacity = dfc.bytesCapacity();
079: rowComparator = new CachedObjectComparator();
080: rowTable = new CachedObject[capacity];
081: cacheMap = new ObjectCacheHashMap(capacity);
082: cacheBytesLength = 0;
083: }
084:
085: /**
086: * Structural initialisations take place here. This allows the Cache to
087: * be resized while the database is in operation.
088: */
089: void init(int capacity, long bytesCapacity) {
090: }
091:
092: int size() {
093: return cacheMap.size();
094: }
095:
096: long getTotalCachedBlockSize() {
097: return cacheBytesLength;
098: }
099:
100: /**
101: * Returns a row if in memory cache.
102: */
103: synchronized CachedObject get(int pos) {
104: return (CachedObject) cacheMap.get(pos);
105: }
106:
107: /**
108: * Adds a row to the cache.
109: */
110: synchronized void put(int key, CachedObject row) throws IOException {
111:
112: int storageSize = row.getStorageSize();
113:
114: if (cacheMap.size() >= capacity
115: || storageSize + cacheBytesLength > bytesCapacity) {
116: cleanUp();
117: }
118:
119: cacheMap.put(key, row);
120:
121: cacheBytesLength += storageSize;
122: }
123:
124: /**
125: * Removes an object from memory cache. Does not release the file storage.
126: */
127: synchronized CachedObject release(int i) {
128:
129: CachedObject r = (CachedObject) cacheMap.remove(i);
130:
131: if (r == null) {
132: return null;
133: }
134:
135: cacheBytesLength -= r.getStorageSize();
136:
137: return r;
138: }
139:
140: /**
141: * Reduces the number of rows held in this Cache object. <p>
142: *
143: * Cleanup is done by checking the accessCount of the Rows and removing
144: * the rows with the lowest access count.
145: *
146: * Index operations require that up to 5 recently accessed rows remain
147: * in the cache.
148: *
149: */
150: private synchronized void cleanUp() throws IOException {
151:
152: int removeCount = cacheMap.size() / 2;
153: int accessTarget = cacheMap.getAccessCountCeiling(removeCount,
154: removeCount / 8);
155: ObjectCacheHashMap.ObjectCacheIterator it = cacheMap.iterator();
156: int savecount = 0;
157:
158: for (; it.hasNext();) {
159: CachedObject r = (CachedObject) it.next();
160:
161: if (it.getAccessCount() <= accessTarget) {
162: if (!r.isKeepInMemory()) {
163: if (r.hasChanged()) {
164: rowTable[savecount++] = r;
165: }
166:
167: it.remove();
168:
169: cacheBytesLength -= r.getStorageSize();
170: }
171: }
172: }
173:
174: cacheMap.setAccessCountFloor(accessTarget);
175: saveRows(savecount);
176: }
177:
178: private synchronized void saveRows(int count) throws IOException {
179:
180: if (count == 0) {
181: return;
182: }
183:
184: rowComparator.setType(rowComparator.COMPARE_POSITION);
185: sortTimer.start();
186: Sort.sort(rowTable, rowComparator, 0, count - 1);
187: sortTimer.stop();
188: saveAllTimer.start();
189: dataFileCache.saveRows(rowTable, 0, count);
190:
191: saveRowCount += count;
192:
193: /*
194: // not necessary if the full storage size of each object is written out
195: try {
196: dataFile.file.seek(fileFreePosition);
197: } catch (IOException e){}
198: */
199: saveAllTimer.stop();
200: }
201:
202: /**
203: * Writes out all modified cached Rows.
204: */
205: synchronized void saveAll() throws IOException {
206:
207: Iterator it = cacheMap.iterator();
208: int savecount = 0;
209:
210: for (; it.hasNext();) {
211: CachedObject r = (CachedObject) it.next();
212:
213: if (r.hasChanged()) {
214: rowTable[savecount++] = r;
215: }
216: }
217:
218: saveRows(savecount);
219: Trace
220: .printSystemOut(saveAllTimer
221: .elapsedTimeToMessage("Cache.saveRow() total row save time"));
222: Trace.printSystemOut("Cache.saveRow() total row save count = "
223: + saveRowCount);
224: Trace
225: .printSystemOut(makeRowTimer
226: .elapsedTimeToMessage("Cache.makeRow() total row load time"));
227: Trace.printSystemOut("Cache.makeRow() total row load count = "
228: + makeRowCount);
229: Trace.printSystemOut(sortTimer
230: .elapsedTimeToMessage("Cache.sort() total time"));
231: }
232:
233: /**
234: * clears out the memory cache
235: */
236: synchronized void clear() {
237:
238: cacheMap.clear();
239:
240: cacheBytesLength = 0;
241: }
242:
243: static class CachedObjectComparator implements ObjectComparator {
244:
245: static final int COMPARE_LAST_ACCESS = 0;
246: static final int COMPARE_POSITION = 1;
247: static final int COMPARE_SIZE = 2;
248: private int compareType;
249:
250: CachedObjectComparator() {
251: }
252:
253: void setType(int type) {
254: compareType = type;
255: }
256:
257: public int compare(Object a, Object b) {
258:
259: switch (compareType) {
260:
261: case COMPARE_POSITION:
262: return ((CachedObject) a).getPos()
263: - ((CachedObject) b).getPos();
264:
265: case COMPARE_SIZE:
266: return ((CachedObject) a).getStorageSize()
267: - ((CachedObject) b).getStorageSize();
268:
269: default:
270: return 0;
271: }
272: }
273: }
274: }
|