001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tcclient.cache;
006:
007: import com.tc.exception.TCRuntimeException;
008: import com.tc.logging.TCLogger;
009: import com.tc.object.bytecode.Manager;
010: import com.tc.util.Assert;
011: import com.tc.util.DebugUtil;
012:
013: import java.util.Collection;
014: import java.util.Date;
015:
016: /**
017: * The invalidator watches a portion of the CacheDataStore (a range of the submaps) and is run in a loop, periodically
018: * running an eviction of all keys existent in this node. Remote keys are ignored to minimize faulting over the network.
019: * Additionally, at the beginning of each run, the cache attempts to become the "global evictor". For each chunk of the
020: * cache, only one of the local evictors in the cluster will become the global evictor. That thread is responsible for
021: * both its own local eviction and also the global eviction. Global eviction entails checking "orphan" keys (those not
022: * currently faulted into any node's cache) for eviction. The process of doing this will cause those keys to be loaded
023: * into this node such that they are no longer orphans. This class should be completely local and no state should be
024: * shared across the cluster. This is done by indicating transient on the array holding these objects in the
025: * CacheDataStore.
026: */
027: class CacheEntryInvalidator {
028:
029: // Configuration
030: private final CacheConfig config;
031: private final int startEvictionIndex;
032: private final int lastEvictionIndex;
033: private final int localCyclesToWaitDuringGlobal = 2; // hard-coded, could be configurable if needed
034:
035: // Resources
036: private final TCLogger logger;
037: private final CacheDataStore store;
038: private final GlobalKeySet globalKeySet;
039:
040: // Local eviction state
041: private final Lock localInvalidationLock;
042:
043: // Global eviction state
044: private boolean isGlobalInvalidator;
045: private final Lock globalInvalidationLock;
046: private int evictionCount; // when this == globalEvictionFrequency, do global
047: // eviction
048: private int globalWaitCount; // number of local eviction cycles we've waited during
049:
050: // global eviction
051:
052: public CacheEntryInvalidator(GlobalKeySet globalKeySet,
053: int startEvictionIndex, int lastEvictionIndex,
054: CacheConfig config, Manager manager, CacheDataStore store) {
055:
056: // Copy configuration
057: this .config = config;
058: if (config.isGlobalEvictionEnabled()) {
059: Assert.eval(config.getGlobalEvictionFrequency() > 0);
060: }
061: this .startEvictionIndex = startEvictionIndex;
062: this .lastEvictionIndex = lastEvictionIndex;
063:
064: // Copy resources
065: this .globalKeySet = globalKeySet;
066: this .store = store;
067:
068: // Initialize state
069: logger = manager
070: .getLogger("com.tc.cache.CacheEntryInvalidator");
071: this .localInvalidationLock = new Lock(createLockName(
072: "tc:local_time_expiry_cache_invalidator_lock_", config
073: .getCacheName(), startEvictionIndex), manager);
074: this .globalInvalidationLock = new Lock(createLockName(
075: "tc:global_time_expiry_cache_invalidator_lock_", config
076: .getCacheName(), startEvictionIndex), manager);
077: }
078:
079: private String createLockName(String prefix, String cacheName,
080: int startIndex) {
081: return prefix + cacheName + ":" + startIndex;
082: }
083:
084: /**
085: * Runnable run method
086: */
087: public void run() {
088: try {
089: tryToBeGlobalInvalidator();
090:
091: // Do local eviction
092: for (int i = startEvictionIndex; i < lastEvictionIndex; i++) {
093: localInvalidationLock.writeLock();
094: try {
095: evictLocalElements(i);
096: } finally {
097: localInvalidationLock.commitLock();
098: }
099: }
100:
101: // Do global eviction if global evictor
102: globalEvictionIfNecessary();
103:
104: } catch (Throwable t) {
105: t.printStackTrace(System.err);
106: throw new TCRuntimeException(t);
107: }
108: }
109:
110: /**
111: * Perform local eviction on this invalidator's portion of the store
112: */
113: protected void evictLocalElements(int index) {
114: log("Local eviction started");
115: store.evictExpiredElements(index, index + 1);
116: log("Local eviction finished");
117: }
118:
119: /**
120: * Attempt to obtain global invalidator write lock. If successful, set isGlobalInvalidator to true
121: */
122: private void tryToBeGlobalInvalidator() {
123: if (config.isGlobalEvictionEnabled() && !isGlobalInvalidator) {
124: if (globalInvalidationLock.tryWriteLock()) {
125: isGlobalInvalidator = true;
126: }
127: }
128: }
129:
130: /**
131: * Perform global eviction
132: */
133: private void globalEvictionIfNecessary() {
134: if (!config.isGlobalEvictionEnabled()) {
135: return;
136: }
137:
138: evictionCount++;
139: boolean isTimeForGlobalEviction = evictionCount >= config
140: .getGlobalEvictionFrequency();
141: if (isTimeForGlobalEviction) {
142: evictionCount = 0;
143: }
144:
145: boolean inGlobalEviction = globalKeySet.inGlobalEviction();
146:
147: if (isGlobalInvalidator) {
148: if (!inGlobalEviction && isTimeForGlobalEviction) {
149: // Start global eviction
150: log("Global eviction started");
151: globalKeySet.globalEvictionStart(getLocalKeys());
152: globalWaitCount = 0;
153:
154: } else if (inGlobalEviction) {
155: globalWaitCount++;
156: if (globalWaitCount >= localCyclesToWaitDuringGlobal) {
157: // End global eviction
158: Collection remoteKeys = globalKeySet
159: .globalEvictionEnd();
160: store.evictAllExpiredElements(remoteKeys,
161: startEvictionIndex, lastEvictionIndex);
162: log("Global eviction finished");
163: }
164: }
165: } else if (inGlobalEviction) {
166: // Non-global evictor participating in global eviction
167: globalKeySet.addLocalKeySet(getLocalKeys());
168: }
169: }
170:
171: private Object[] getLocalKeys() {
172: return store.getAllLocalKeys(startEvictionIndex,
173: lastEvictionIndex);
174: }
175:
176: public void postRun() {
177: if (isGlobalInvalidator) {
178: globalInvalidationLock.commitLock();
179: isGlobalInvalidator = false;
180: }
181: }
182:
183: private void log(String msg) {
184: if (config.isEvictorLoggingEnabled()) {
185: logger.debug(msg);
186: if (DebugUtil.DEBUG) {
187: System.err
188: .println((new Date(System.currentTimeMillis()))
189: .toString()
190: + msg);
191: }
192: }
193: }
194:
195: }
|