KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > storagemanager > LRUStorageCache


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.storagemanager;
13
14 import com.versant.core.common.State;
15 import com.versant.core.metadata.FetchGroup;
16 import com.versant.core.metadata.ClassMetaData;
17 import com.versant.core.metadata.ModelMetaData;
18 import com.versant.core.common.OID;
19 import com.versant.core.common.*;
20 import com.versant.core.server.CachedQueryResult;
21 import com.versant.core.server.CompiledQuery;
22 import com.versant.core.metric.*;
23
24 import java.util.*;
25 import java.io.PrintStream JavaDoc;
26
27 /**
28  * Count limited StorageCache implementation that uses an LRU algorithm to
29  * limit the number of cached instances and query results.
30  */

31 public final class LRUStorageCache implements StorageCache, HasMetrics {
32
33     private final Map stateMap; // OID -> StateEntry
34
private final Map queryMap; // QueryEntry -> QueryEntry
35

36     private ModelMetaData jmd;
37     private boolean enabled = true;
38     private boolean queryCacheEnabled = true;
39     private int maxObjects = 10000;
40     private int maxQueries = 1000;
41     private int objectCount;
42     private int queryCount;
43
44     private long now;
45     private long evictAllTimestamp;
46
47     // single linked active Tx list
48
private Tx txTail, txHead;
49
50     // double linked LRU State list
51
private StateEntry stateTail; // least recently accessed State
52
private StateEntry stateHead; // most recently accessed State
53

54     // double linked list of StateEntry's for each class
55
private StateEntry[] classStateHead;
56
57     // time each class was last evicted
58
private long[] classEvictionTimestamp;
59
60     // double linked LRU query key list
61
private QueryEntry queryHead;
62     private QueryEntry queryTail;
63
64     // double linked list for each class containing the queries that depend it
65
private QueryEntryNode[] classQueryHead;
66
67     private int hitCount;
68     private int missCount;
69     private int queryHitCount;
70     private int queryMissCount;
71
72     private static final Tx DISABLED_TX = new Tx(0);
73
74     private static final String JavaDoc CAT_CACHE = "L2Cache";
75
76     private final BaseMetric metricCacheSize =
77             new BaseMetric("CacheSize", "Cache Size", CAT_CACHE,
78                     "Number of objects in the level 2 cache", 0,
79                     Metric.CALC_AVERAGE);
80     private final BaseMetric metricCacheMaxSize =
81             new BaseMetric("CacheMaxSize", "Cache Max Size", CAT_CACHE,
82                     "Max number of objects to store in the level 2 cache", 0,
83                     Metric.CALC_AVERAGE);
84     private final BaseMetric metricCacheHit =
85             new BaseMetric("CacheHit", "Cache Hit", CAT_CACHE,
86                     "Number of times data was found in cache", 3,
87                     Metric.CALC_DELTA_PER_SECOND);
88     private final BaseMetric metricCacheMiss =
89             new BaseMetric("CacheMiss", "Cache Miss", CAT_CACHE,
90                     "Number of times data was not found in cache", 3,
91                     Metric.CALC_DELTA_PER_SECOND);
92
93     private final BaseMetric metricQueryCacheSize =
94             new BaseMetric("QueryCacheSize", "Query Cache Size", CAT_CACHE,
95                     "Number of queries in the cache", 0, Metric.CALC_AVERAGE);
96     private final BaseMetric metricQueryCacheMaxSize =
97             new BaseMetric("QueryCacheMaxSize", "Query Cache Max Size",
98                     CAT_CACHE,
99                     "Max number of queries to store in the cache", 0,
100                     Metric.CALC_AVERAGE);
101     private final BaseMetric metricQueryCacheHit =
102             new BaseMetric("QueryCacheHit", "Query Cache Hit", CAT_CACHE,
103                     "Number of times query results were found in cache", 3,
104                     Metric.CALC_DELTA_PER_SECOND);
105     private final BaseMetric metricQueryCacheMiss =
106             new BaseMetric("QueryCacheMiss", "Query Cache Miss", CAT_CACHE,
107                     "Number of times query results were not found in cache", 3,
108                     Metric.CALC_DELTA_PER_SECOND);
109
110     private final PercentageMetric metricCacheFullPercent =
111             new PercentageMetric("CacheFullPercent", "Cache Full %", CAT_CACHE,
112                     "Number of objects in the cache as a percentage of the max",
113                     metricCacheSize, metricCacheMaxSize);
114     private final PercentageSumMetric metricCacheHitPercent =
115             new PercentageSumMetric("CacheHitPercent", "Cache Hit %", CAT_CACHE,
116                     "Cache hit rate percentage",
117                     metricCacheHit, metricCacheMiss);
118     private final PercentageMetric metricQueryCacheFullPercent =
119             new PercentageMetric("QueryCacheFullPercent", "Query Cache Full %", CAT_CACHE,
120                     "Number of queries in the cache as a percentage of the max",
121                     metricQueryCacheSize, metricQueryCacheMaxSize);
122     private final PercentageSumMetric metricQueryCacheHitPercent =
123             new PercentageSumMetric("QueryCacheHitPercent", "Query Cache Hit %", CAT_CACHE,
124                     "Query Cache hit rate percentage",
125                     metricQueryCacheHit, metricQueryCacheMiss);
126
127     public LRUStorageCache() {
128         stateMap = new HashMap();
129         queryMap = new HashMap();
130     }
131
132     public void setJDOMetaData(ModelMetaData jmd) {
133         this.jmd = jmd;
134         int n = jmd.classes.length;
135         classStateHead = new StateEntry[n];
136         classEvictionTimestamp = new long[n];
137         classQueryHead = new QueryEntryNode[n];
138     }
139
140     public boolean isEnabled() {
141         return enabled;
142     }
143
144     public synchronized void setEnabled(boolean enabled) {
145         this.enabled = enabled;
146         if (!enabled) {
147             evictAll(null);
148         }
149     }
150
151     public boolean isQueryCacheEnabled() {
152         return queryCacheEnabled;
153     }
154
155     public void setQueryCacheEnabled(boolean queryCacheEnabled) {
156         this.queryCacheEnabled = queryCacheEnabled;
157     }
158
159     public synchronized Object JavaDoc beginTx() {
160         if (!enabled) {
161             return DISABLED_TX;
162         }
163         Tx tx = new Tx(++now);
164         if (txHead == null) {
165             txTail = txHead = tx;
166         } else {
167             txHead.next = tx;
168             txHead = tx;
169         }
170         return tx;
171     }
172
173     public synchronized void endTx(Object JavaDoc o) {
174         if (o == DISABLED_TX) {
175             return;
176         }
177         Tx tx = (Tx)o;
178         tx.finished = true;
179         // remove all eviction markers for each finished tx that is the oldest
180
// tx i.e. there is no older tx that has not finished yet
181
for (; txTail.finished; ) {
182             for (int i = txTail.evictedCount - 1; i >= 0; i--) {
183                 OID oid = txTail.evicted[i];
184                 StateEntry e = (StateEntry)stateMap.get(oid);
185                 if (e != null && e.timestamp == tx.started) {
186                     stateMap.remove(oid);
187                 }
188             }
189             if (txTail.next == null) {
190                 txTail = txHead = null;
191                 break;
192             }
193             txTail = txTail.next;
194         }
195     }
196
197     public synchronized State getState(OID oid, FetchGroup fetchGroup) {
198         if (!enabled) {
199             return null;
200         }
201         StateEntry e = (StateEntry)stateMap.get(oid);
202         if (e != null && e.state != null) {
203             removeFromStateList(e);
204             addToHeadOfStateList(e);
205             if (fetchGroup == null || e.state.containsFetchGroup(fetchGroup)) {
206                 ++hitCount;
207                 return e.state.getCopy();
208             }
209         }
210         ++missCount;
211         return null;
212     }
213
214     public synchronized boolean contains(OID oid) {
215         if (!enabled) {
216             return false;
217         }
218         StateEntry e = (StateEntry)stateMap.get(oid);
219         return e != null && e.state != null;
220     }
221
222     public synchronized CachedQueryResult getQueryResult(CompiledQuery cq,
223             Object JavaDoc[] params) {
224         if (!enabled || !queryCacheEnabled) {
225             return null;
226         }
227         QueryEntry e = (QueryEntry)queryMap.get(new QueryEntry(cq, params));
228         if (e != null && e.res != null) {
229             ++queryHitCount;
230             removeFromQueryList(e);
231             addToHeadOfQueryList(e);
232             return e.res;
233         } else {
234             ++queryMissCount;
235             return null;
236         }
237     }
238
239     public synchronized int getQueryResultCount(CompiledQuery cq,
240             Object JavaDoc[] params) {
241         if (!enabled || !queryCacheEnabled) {
242             return -1;
243         }
244         QueryEntry e = (QueryEntry)queryMap.get(new QueryEntry(cq, params));
245         if (e != null) {
246             ++queryHitCount;
247             removeFromQueryList(e);
248             addToHeadOfQueryList(e);
249             return e.res != null ? e.res.results.size() : e.resultCount;
250         } else {
251             ++queryMissCount;
252             return -1;
253         }
254     }
255
256     public synchronized void evict(Object JavaDoc tx, CompiledQuery cq,
257             Object JavaDoc[] params) {
258         if (!enabled) {
259             return;
260         }
261         QueryEntry e = (QueryEntry)queryMap.get(new QueryEntry(cq, params));
262         if (e != null) {
263             removeFromQueryList(e);
264             removeFromClassQueryLists(e);
265             queryMap.remove(e);
266         }
267     }
268
269     public synchronized void add(Object JavaDoc otx, StatesReturned container) {
270         if (!enabled) {
271             return;
272         }
273         Tx tx = (Tx)otx;
274         if (tx.started <= evictAllTimestamp) {
275             // cannot accept any data from tx as evict all was done after
276
// it started
277
return;
278         }
279         for (Iterator i = container.iterator(); i.hasNext(); ) {
280             EntrySet.Entry me = (EntrySet.Entry)i.next();
281             OID oid = (OID)me.getKey();
282             State state = (State)me.getValue();
283             if (!state.isCacheble()
284                     || tx.started <= classEvictionTimestamp[state.getClassIndex()]) {
285                 continue; // class was evicted after tx started so dont add
286
}
287             StateEntry e = (StateEntry)stateMap.get(oid);
288             if (e != null) {
289                 if (tx.started <= e.timestamp || e.state == null) {
290                     continue; // data in cache is newer or evicted so dont add
291
}
292                 e.state.updateFrom(state);
293                 e.timestamp = now;
294             } else {
295                 e = new StateEntry(now, oid, state);
296                 stateMap.put(oid, e);
297                 addToHeadOfStateList(e);
298                 addToClassStateList(e);
299                 discardExcessStates();
300             }
301         }
302     }
303
304     public synchronized void add(Object JavaDoc tx, CompiledQuery cq, Object JavaDoc[] params,
305             CachedQueryResult queryData) {
306         if (!enabled) {
307             return;
308         }
309         addImp((Tx)tx, cq, params, queryData,
310                 queryData.results == null ? 0 : queryData.results.size());
311     }
312
313     private void addImp(Tx tx, CompiledQuery cq, Object JavaDoc[] params,
314             CachedQueryResult queryData, int resultCount) {
315         if (tx.started <= evictAllTimestamp) {
316             // cannot accept any data from tx as evict all was done after
317
// it started
318
return;
319         }
320         QueryEntry e = new QueryEntry(cq, params);
321         QueryEntry existing = (QueryEntry)queryMap.get(e);
322         if (existing != null) {
323             if (tx.started <= existing.timestamp) {
324                 return; // data in cache is newer so dont add
325
}
326             e = existing;
327         } else {
328             int[] indexes = e.cq.getClassIndexes();
329             // dont add to cache if any of the classes involved have been
330
// evicted since we started as the query data may be stale
331
for (int i = indexes.length - 1; i >= 0; i--) {
332                 if (classEvictionTimestamp[indexes[i]] >= tx.started) {
333                     return;
334                 }
335             }
336             queryMap.put(e, e);
337             addToHeadOfQueryList(e);
338             discardExcessQueries();
339             addToClassQueryLists(e);
340         }
341         e.res = queryData;
342         e.resultCount = resultCount;
343         e.timestamp = tx.started;
344     }
345
346     public synchronized void add(Object JavaDoc tx, CompiledQuery cq, Object JavaDoc[] params,
347             int count) {
348         if (!queryCacheEnabled) {
349             return;
350         }
351         addImp((Tx)tx, cq, params, null, count);
352     }
353
354     public synchronized void evict(Object JavaDoc otx, OID[] oids, int offset,
355             int length, int expected) {
356         if (!enabled) {
357             return;
358         }
359         Tx tx = (Tx)otx;
360         OID[] a;
361         int evictedCount = tx.evictedCount;
362         if (evictedCount > 0) {
363             if (tx.evicted.length - evictedCount >= length) {
364                 a = tx.evicted;
365             } else {
366                 a = new OID[evictedCount + length];
367                 System.arraycopy(tx.evicted, 0, a, 0, evictedCount);
368                 tx.evicted = a;
369             }
370         } else {
371             tx.evicted = a = new OID[expected < length ? length : expected];
372         }
373         System.arraycopy(oids, offset, a, evictedCount, length);
374         long started = tx.started;
375         for (int i = 0; i < length; i++) {
376             OID oid = a[i + evictedCount];
377             StateEntry e = (StateEntry)stateMap.get(oid);
378             if (e == null) { // create eviction marker
379
stateMap.put(oid, new StateEntry(started, oid, null));
380             } else if (e.state != null) {
381                 removeFromStateList(e);
382                 removeFromClassStateList(e);
383                 e.state = null;
384                 e.timestamp = started;
385             } else if (e.timestamp < started) {
386                 e.timestamp = started;
387                 continue;
388             }
389             int ci = oid.getClassIndex();
390             classEvictionTimestamp[ci] = now;
391             removeQueriesForClass(ci);
392         }
393         tx.evictedCount += length;
394     }
395
396     public synchronized void evict(Object JavaDoc tx, ClassMetaData[] classes,
397             int classCount) {
398         if (!enabled) {
399             return;
400         }
401         for (int i = 0; i < classCount; i++) {
402             int ci = classes[i].index;
403             classEvictionTimestamp[ci] = now;
404             // evict all states for class
405
for (StateEntry e = classStateHead[ci]; e != null; ) {
406                 removeFromStateList(e);
407                 stateMap.remove(e.oid);
408                 e.state = null;
409                 e.oid = null;
410                 StateEntry prev = e.classPrev;
411                 e.classNext = null;
412                 e.classPrev = null;
413                 e = prev;
414             }
415             classStateHead[ci] = null;
416             // evict all queries for the class
417
removeQueriesForClass(ci);
418             classes[i].cacheStrategyAllDone = false;
419         }
420     }
421
422     public synchronized void evictAll(Object JavaDoc tx) {
423         evictAllTimestamp = now;
424         stateMap.clear();
425         objectCount = 0;
426         queryMap.clear();
427         queryCount = 0;
428         for (int i = classQueryHead.length - 1; i >= 0; i--) {
429             classQueryHead[i] = null;
430         }
431         for (int i = jmd.classes.length - 1; i >= 0; i--) {
432             jmd.classes[i].cacheStrategyAllDone = false;
433         }
434         stateHead = stateTail = null;
435     }
436
437     public int getObjectCount() {
438         return objectCount;
439     }
440
441     public int getMaxObjects() {
442         return maxObjects;
443     }
444
445     public synchronized void setMaxObjects(int maxObjects) {
446         this.maxObjects = maxObjects;
447         discardExcessStates();
448     }
449
450     public int getMaxQueries() {
451         return maxQueries;
452     }
453
454     public synchronized void setMaxQueries(int maxQueries) {
455         this.maxQueries = maxQueries;
456         discardExcessQueries();
457     }
458
459     public int getHitCount() {
460         return hitCount;
461     }
462
463     public int getMissCount() {
464         return missCount;
465     }
466
467     public int getQueryHitCount() {
468         return queryHitCount;
469     }
470
471     public int getQueryMissCount() {
472         return queryMissCount;
473     }
474
475     private void discardExcessStates() {
476         for (; objectCount > maxObjects && stateTail != null; --objectCount) {
477             StateEntry e = stateTail;
478             stateMap.remove(e.oid);
479             stateTail = e.lruNext;
480             e.lruNext = null;
481             if (stateTail != null) {
482                 stateTail.lruPrev = null;
483             } else {
484                 stateHead = null;
485             }
486         }
487     }
488
489     private void discardExcessQueries() {
490         for (; queryCount > maxQueries && queryTail != null; --queryCount) {
491             QueryEntry e = queryTail;
492             queryMap.remove(e);
493             queryTail = e.next;
494             e.next = null;
495             if (queryTail != null) {
496                 queryTail.prev = null;
497             } else {
498                 queryHead = null;
499             }
500             removeFromClassQueryLists(e);
501         }
502     }
503
504     /**
505      * Remove e from the double linked LRU list and dec objectCount.
506      */

507     private void removeFromStateList(StateEntry e) {
508         if (e.lruPrev != null) {
509             e.lruPrev.lruNext = e.lruNext;
510         } else {
511             stateTail = e.lruNext;
512         }
513         if (e.lruNext != null) {
514             e.lruNext.lruPrev = e.lruPrev;
515         } else {
516             stateHead = e.lruPrev;
517         }
518         e.lruNext = e.lruPrev = null;
519         --objectCount;
520     }
521
522     /**
523      * Add ps to the head of the double linked LRU list and inc objectCount.
524      * This will make it the most recently accessed object.
525      */

526     private void addToHeadOfStateList(StateEntry e) {
527         e.lruNext = null;
528         e.lruPrev = stateHead;
529         if (stateHead != null) {
530             stateHead.lruNext = e;
531         }
532         stateHead = e;
533         if (stateTail == null) {
534             stateTail = e;
535         }
536         ++objectCount;
537     }
538
539     /**
540      * Add e to the double linked class state list for its class.
541      */

542     private void addToClassStateList(StateEntry e) {
543         int i = e.state.getClassIndex();
544         e.classPrev = classStateHead[i];
545         if (classStateHead[i] != null) {
546             classStateHead[i].classNext = e;
547         }
548         e.classNext = null;
549         classStateHead[i] = e;
550     }
551
552     /**
553      * Remove e from the double linked class state list for its class.
554      */

555     private void removeFromClassStateList(StateEntry e) {
556         if (e.classPrev != null) {
557             e.classPrev.classNext = e.classNext;
558         } else {
559             classStateHead[e.state.getClassIndex()] = e.classNext;
560         }
561         if (e.classNext != null) {
562             e.classNext.classPrev = e.classPrev;
563             e.classNext = null;
564         }
565         e.classPrev = null;
566     }
567
568     /**
569      * Remove cps from the double linked LRU list.
570      */

571     private void removeFromQueryList(QueryEntry e) {
572         if (e.prev != null) {
573             e.prev.next = e.next;
574         } else {
575             queryTail = e.next;
576         }
577         if (e.next != null) {
578             e.next.prev = e.prev;
579         } else {
580             queryHead = e.prev;
581         }
582         e.next = e.prev = null;
583         --queryCount;
584     }
585
586     /**
587      * Add ps to the head of the double linked LRU list. This will make it
588      * the most recently accessed object.
589      */

590     private void addToHeadOfQueryList(QueryEntry e) {
591         e.next = null;
592         e.prev = queryHead;
593         if (queryHead != null) {
594             queryHead.next = e;
595         }
596         queryHead = e;
597         if (queryTail == null) {
598             queryTail = e;
599         }
600         ++queryCount;
601     }
602
603     /**
604      * Add e to the class query lists of all of the classes that it depends
605      * on.
606      */

607     private void addToClassQueryLists(QueryEntry e) {
608         int[] indexes = e.cq.getClassIndexes();
609         QueryEntryNode sibling = null;
610         for (int i = 0; i < indexes.length; i++) {
611             int classIndex = indexes[i];
612             QueryEntryNode head = classQueryHead[classIndex];
613             QueryEntryNode n = new QueryEntryNode(e);
614             n.prev = head;
615             if (head != null) {
616                 head.next = n;
617             }
618             if (sibling != null) {
619                 sibling.nextSibling = n;
620             } else {
621                 e.queryNodeTail = n;
622             }
623             classQueryHead[classIndex] = sibling = n;
624         }
625     }
626
627     /**
628      * Remove e from the class query lists of all of the classes it depends
629      * on.
630      */

631     private void removeFromClassQueryLists(QueryEntry e) {
632         int i = 0;
633         int[] indexes = e.cq.getClassIndexes();
634         for (QueryEntryNode n = e.queryNodeTail; n != null; i++) {
635             QueryEntryNode nextSibling = n.nextSibling;
636             n.nextSibling = null;
637             if (n.prev != null) {
638                 n.prev.next = n.next;
639             }
640             if (n.next != null) {
641                 n.next.prev = n.prev;
642             } else { // n is current head of list
643
classQueryHead[indexes[i]] = n.prev;
644             }
645             n.next = n.prev = null;
646             n = nextSibling;
647         }
648     }
649
650     /**
651      * Remove all queries for the class from the LRU query list, the query
652      * lists for all classes each query depends on and the queryMap itself.
653      */

654     private void removeQueriesForClass(int classIndex) {
655         for (QueryEntryNode n = classQueryHead[classIndex]; n != null; ) {
656             removeFromQueryList(n.e);
657             QueryEntryNode prev = n.prev;
658             removeFromClassQueryLists(n.e);
659             queryMap.remove(n.e);
660             n = prev;
661         }
662     }
663
664     public void dump(PrintStream JavaDoc out) {
665         out.println("stateMap.size() = " + stateMap.size());
666         out.println("objectCount = " + objectCount +
667                 ", maxObjects = " + maxObjects);
668         int c = 0;
669         StateEntry p = null;
670         for (StateEntry e = stateTail; e != null; p = e, e = e.lruNext, ++c) {
671             asst(e.lruPrev == p);
672         }
673         out.println("LRU StateList length = " + c + " stateTail = " +
674                 stateTail + " stateHead = " + stateHead);
675         HashSet oids = new HashSet();
676         for (Tx e = txTail; e != null; e = e.next) {
677             if (e.evicted != null) {
678                 oids.addAll(Arrays.asList(e.evicted));
679             }
680         }
681         asst(stateHead == p);
682         asst(objectCount <= maxObjects);
683         asst(objectCount + oids.size() == stateMap.size());
684         out.println("--- Tx list ---");
685         c = 0;
686         int totEvictedCount = 0;
687         for (Tx e = txTail; e != null; e = e.next, ++c) {
688             out.println(e + " started " + e.started + " finished " + e.finished +
689                     " evictedCount " + e.evictedCount);
690             totEvictedCount += e.evictedCount;
691         }
692         out.println("--- count " + c + " totEvictedCount " + totEvictedCount);
693     }
694
695     private void asst(boolean bool) {
696         if (!bool) {
697             throw BindingSupportImpl.getInstance().internal("assertion failed");
698         }
699     }
700
701     public void addMetrics(List list) {
702         list.add(metricCacheSize);
703         list.add(metricCacheMaxSize);
704         list.add(metricCacheHit);
705         list.add(metricCacheMiss);
706         list.add(metricQueryCacheSize);
707         list.add(metricQueryCacheMaxSize);
708         list.add(metricQueryCacheHit);
709         list.add(metricQueryCacheMiss);
710         list.add(metricCacheFullPercent);
711         list.add(metricCacheHitPercent);
712         list.add(metricQueryCacheFullPercent);
713         list.add(metricQueryCacheHitPercent);
714     }
715
716     public void sampleMetrics(int[][] buf, int pos) {
717         buf[metricCacheSize.getIndex()][pos] = objectCount;
718         buf[metricCacheMaxSize.getIndex()][pos] = maxObjects;
719         buf[metricCacheHit.getIndex()][pos] = hitCount;
720         buf[metricCacheMiss.getIndex()][pos] = missCount;
721         buf[metricQueryCacheSize.getIndex()][pos] = queryCount;
722         buf[metricQueryCacheMaxSize.getIndex()][pos] = maxQueries;
723         buf[metricQueryCacheHit.getIndex()][pos] = queryHitCount;
724         buf[metricQueryCacheMiss.getIndex()][pos] = queryMissCount;
725     }
726
727     /**
728      * Info about a cache transaction.
729      */

730     private static final class Tx {
731         final long started;
732         Tx next;
733         boolean finished;
734         OID[] evicted;
735         int evictedCount;
736
737         public Tx(long timestamp) {
738             this.started = timestamp;
739         }
740     }
741
742     /**
743      * Stuff we associate with each State in stateMap. The state field is
744      * null if this is an eviction marker.
745      */

746     private static final class StateEntry {
747         long timestamp; // data read in a tx with started <= timestamp will
748
// not go in cache
749
OID oid;
750         State state;
751         StateEntry lruPrev, lruNext;
752         StateEntry classPrev, classNext;
753
754         public StateEntry(long txId, OID oid, State state) {
755             this.timestamp = txId;
756             this.oid = oid;
757             this.state = state;
758         }
759     }
760
761     /**
762      * This is the key and value for our query map. These form a double linked
763      * LRU list of query results.
764      */

765     private static final class QueryEntry {
766         final CompiledQuery cq;
767         final Object JavaDoc[] params;
768         final int hashCode;
769         long timestamp; // data read in a tx with started <= timestamp will
770
// not go in cache
771
CachedQueryResult res;
772         int resultCount;
773         QueryEntry prev, next;
774         QueryEntryNode queryNodeTail;
775             // single linked list of our nodes on QueryEntryNode.nextSibling
776

777         public QueryEntry(CompiledQuery cq, Object JavaDoc[] params) {
778             this.cq = cq;
779             this.params = params;
780             int hc = cq.hashCode();
781             if (params != null) {
782                 hc = cq.hashCode();
783                 for (int i = params.length - 1; i >= 0; i--) {
784                     Object JavaDoc o = params[i];
785                     if (o != null) {
786                         hc = hc * 29 + o.hashCode();
787                     }
788                 }
789             }
790             hashCode = hc;
791         }
792
793         public int hashCode() {
794             return hashCode;
795         }
796
797         public boolean equals(Object JavaDoc o) {
798             QueryEntry k = (QueryEntry)o;
799             if (hashCode != k.hashCode) {
800                 return false;
801             }
802             if (!cq.equals(k.cq)) {
803                 return false;
804             }
805             if (params != null) {
806                 for (int i = params.length - 1; i >= 0; i--) {
807                     Object JavaDoc a = params[i];
808                     Object JavaDoc b = k.params[i];
809                     if (a == null) {
810                         if (b != null) return false;
811                     } else if (b == null || !a.equals(b)) {
812                         return false;
813                     }
814                 }
815             }
816             return true;
817         }
818     }
819
820     /**
821      * Node in a double linked list of QueryEntry's. All the nodes for a
822      * single QueryEntry are also linked together starting from the
823      * QueryEntry.queryNodeTail. This makes it easy to remove all of the
824      * nodes for a QueryEntry.
825      */

826     private static final class QueryEntryNode {
827         QueryEntry e;
828         QueryEntryNode prev, next, nextSibling;
829
830         public QueryEntryNode(QueryEntry e) {
831             this.e = e;
832         }
833
834     }
835
836 }
837
Popular Tags