| | |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.locks.Lock; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | import java.util.concurrent.locks.ReentrantReadWriteLock; |
| | | |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.std.server.EntryCacheCfg; |
| | |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.messages.ExtensionMessages.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | |
| | | |
| | | |
| | |
| | | |
| | | // The lock used to provide threadsafe access when changing the contents of |
| | | // the cache. |
| | | private Lock cacheLock; |
| | | private ReentrantReadWriteLock cacheLock; |
| | | private Lock cacheWriteLock; |
| | | private Lock cacheReadLock; |
| | | |
| | | // The maximum amount of memory in bytes that the JVM will be allowed to use |
| | | // before we need to start purging entries. |
| | |
| | | // Initialize the cache structures. |
| | | idMap = new HashMap<Backend,HashMap<Long,CacheEntry>>(); |
| | | dnMap = new LinkedHashMap<DN,CacheEntry>(); |
| | | cacheLock = new ReentrantLock(); |
| | | |
| | | // Initialize locks. |
| | | cacheLock = new ReentrantReadWriteLock(true); |
| | | cacheWriteLock = cacheLock.writeLock(); |
| | | cacheReadLock = cacheLock.readLock(); |
| | | |
| | | // Read configuration and apply changes. |
| | | boolean applyChanges = true; |
| | |
| | | */ |
| | | public void finalizeEntryCache() |
| | | { |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | try { |
| | | registeredConfiguration.removeFIFOChangeListener(this); |
| | |
| | | } |
| | | } |
| | | } finally { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // Indicate whether the DN map contains the specified DN. |
| | | return dnMap.containsKey(entryDN); |
| | | cacheReadLock.lock(); |
| | | try { |
| | | return dnMap.containsKey(entryDN); |
| | | } finally { |
| | | cacheReadLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | public Entry getEntry(DN entryDN) |
| | | { |
| | | // Simply return the entry from the DN map. |
| | | CacheEntry e = dnMap.get(entryDN); |
| | | if (e == null) |
| | | { |
| | | // Indicate cache miss. |
| | | cacheMisses.getAndIncrement(); |
| | | return null; |
| | | } |
| | | else |
| | | { |
| | | // Indicate cache hit. |
| | | cacheHits.getAndIncrement(); |
| | | return e.getEntry(); |
| | | cacheReadLock.lock(); |
| | | try { |
| | | CacheEntry e = dnMap.get(entryDN); |
| | | if (e == null) { |
| | | // Indicate cache miss. |
| | | cacheMisses.getAndIncrement(); |
| | | return null; |
| | | } else { |
| | | // Indicate cache hit. |
| | | cacheHits.getAndIncrement(); |
| | | return e.getEntry(); |
| | | } |
| | | } finally { |
| | | cacheReadLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | public long getEntryID(DN entryDN) |
| | | { |
| | | // Simply return the ID from the DN map. |
| | | CacheEntry e = dnMap.get(entryDN); |
| | | if (e == null) |
| | | { |
| | | return -1; |
| | | } |
| | | else |
| | | { |
| | | return e.getEntryID(); |
| | | cacheReadLock.lock(); |
| | | try { |
| | | CacheEntry e = dnMap.get(entryDN); |
| | | if (e == null) { |
| | | return -1; |
| | | } else { |
| | | return e.getEntryID(); |
| | | } |
| | | } finally { |
| | | cacheReadLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | public DN getEntryDN(Backend backend, long entryID) |
| | | { |
| | | // Locate specific backend map and return the entry DN by ID. |
| | | HashMap<Long,CacheEntry> backendMap = idMap.get(backend); |
| | | if (backendMap != null) { |
| | | CacheEntry e = backendMap.get(entryID); |
| | | if (e != null) { |
| | | return e.getDN(); |
| | | cacheReadLock.lock(); |
| | | try { |
| | | HashMap<Long, CacheEntry> backendMap = idMap.get(backend); |
| | | if (backendMap != null) { |
| | | CacheEntry e = backendMap.get(entryID); |
| | | if (e != null) { |
| | | return e.getDN(); |
| | | } |
| | | } |
| | | return null; |
| | | } finally { |
| | | cacheReadLock.unlock(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | |
| | | // Obtain a lock on the cache. If this fails, then don't do anything. |
| | | try |
| | | { |
| | | if (! cacheLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) |
| | | if (!cacheWriteLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) |
| | | { |
| | | return; |
| | | } |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | // Obtain a lock on the cache. If this fails, then don't do anything. |
| | | try |
| | | { |
| | | if (! cacheLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) |
| | | if (!cacheWriteLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) |
| | | { |
| | | // We can't rule out the possibility of a conflict, so return false. |
| | | return false; |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | // FIXME -- An alternate approach could be to block for a maximum length of |
| | | // time and then if it fails then put it in a queue for processing by some |
| | | // other thread before it releases the lock. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | |
| | | // At this point, it is absolutely critical that we always release the lock |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | // Acquire a lock on the cache. We should not return until the cache has |
| | | // been cleared, so we will block until we can obtain the lock. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | |
| | | // At this point, it is absolutely critical that we always release the lock |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | // Acquire a lock on the cache. We should not return until the cache has |
| | | // been cleared, so we will block until we can obtain the lock. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | |
| | | // At this point, it is absolutely critical that we always release the lock |
| | |
| | | |
| | | if ((entriesDeleted % 1000) == 0) |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | Thread.currentThread().yield(); |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // Acquire a lock on the cache. We should not return until the cache has |
| | | // been cleared, so we will block until we can obtain the lock. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | |
| | | // At this point, it is absolutely critical that we always release the lock |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | entriesExamined++; |
| | | if ((entriesExamined % 1000) == 0) |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | Thread.currentThread().yield(); |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | } |
| | | } |
| | | |
| | |
| | | public void handleLowMemory() |
| | | { |
| | | // Grab the lock on the cache and wait until we have it. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | |
| | | |
| | | // At this point, it is absolutely critical that we always release the lock |
| | |
| | | } |
| | | finally |
| | | { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // Grab cache lock to prevent any modifications |
| | | // to the cache maps until a snapshot is taken. |
| | | cacheLock.lock(); |
| | | cacheWriteLock.lock(); |
| | | try { |
| | | // Examining the real maps will hold the lock and can cause map |
| | | // modifications in case of any access order maps, make copies |
| | |
| | | dnMapCopy = new LinkedHashMap<DN,CacheEntry>(dnMap); |
| | | idMapCopy = new HashMap<Backend,HashMap<Long,CacheEntry>>(idMap); |
| | | } finally { |
| | | cacheLock.unlock(); |
| | | cacheWriteLock.unlock(); |
| | | } |
| | | |
| | | // Check dnMap first. |