From 0ce8111bcd7c23226fa1fe7469baae9b1840eae4 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Sun, 19 Aug 2007 23:28:49 +0000
Subject: [PATCH] - implement new toVerboseString() method. - fix bugs found by entry cache unit tests. - sanitize backend map maintenance.

---
 opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java    |   61 +++++++++++++++
 opends/src/server/org/opends/server/extensions/DefaultEntryCache.java       |   11 ++
 opends/src/server/org/opends/server/extensions/FIFOEntryCache.java          |   62 +++++++++++++++
 opends/src/server/org/opends/server/api/EntryCache.java                     |   13 +++
 opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java |   37 +++++++++
 5 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/EntryCache.java b/opends/src/server/org/opends/server/api/EntryCache.java
index 09c7d0c..085c5c0 100644
--- a/opends/src/server/org/opends/server/api/EntryCache.java
+++ b/opends/src/server/org/opends/server/api/EntryCache.java
@@ -526,6 +526,19 @@
 
 
   /**
+   * Return a verbose string representation of the current cache maps.
+   * This is useful primary for debugging and diagnostic purposes such
+   * as in the entry cache unit tests.
+   * @return String verbose string representation of the current cache
+   *                maps in the following format: dn:id:backend
+   *                one cache entry map representation per line
+   *                or <CODE>null</CODE> if all maps are empty.
+   */
+  public abstract String toVerboseString();
+
+
+
+  /**
    * Retrieves the maximum length of time in milliseconds to wait for
    * a lock before giving up.
    *
diff --git a/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java b/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
index 7633e06..e66f9ca 100644
--- a/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
+++ b/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
@@ -210,6 +210,17 @@
   /**
    * {@inheritDoc}
    */
+  public String toVerboseString()
+  {
+    // This implementation does not store entries.
+    return null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
   public boolean isConfigurationChangeAcceptable(
       EntryCacheCfg configuration,
       List<Message> unacceptableReasons
diff --git a/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java b/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
index a500207..201df37 100644
--- a/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
+++ b/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
@@ -562,9 +562,10 @@
         return;
       }
 
+      Backend backend = entry.getBackend();
 
       // Try to remove the entry from the ID list as well.
-      Map<Long,CacheEntry> map = idMap.get(entry.getBackend());
+      Map<Long,CacheEntry> map = idMap.get(backend);
       if (map == null)
       {
         // This should't happen, but the entry isn't cached in the ID map so
@@ -573,6 +574,12 @@
       }
 
       map.remove(entry.getEntryID());
+
+      // If this backend becomes empty now remove it from the idMap map.
+      if (map.isEmpty())
+      {
+        idMap.remove(backend);
+      }
     }
     catch (Exception e)
     {
@@ -858,6 +865,59 @@
   /**
    * {@inheritDoc}
    */
+  public String toVerboseString()
+  {
+    String verboseString = new String();
+
+    Map<DN,CacheEntry> dnMapCopy;
+    Map<Backend,HashMap<Long,CacheEntry>> idMapCopy;
+
+    // Grab cache lock to prevent any modifications
+    // to the cache maps until a snapshot is taken.
+    cacheLock.lock();
+    try {
+      // Examining the real maps will hold the lock and can cause map
+      // modifications in case of any access order maps, make copies
+      // instead.
+      dnMapCopy = new LinkedHashMap<DN,CacheEntry>(dnMap);
+      idMapCopy = new HashMap<Backend,HashMap<Long,CacheEntry>>(idMap);
+    } finally {
+      cacheLock.unlock();
+    }
+
+    // Check dnMap first.
+    for(DN dn : dnMapCopy.keySet()) {
+      verboseString = verboseString + dn.toString() + ":" +
+        (dnMapCopy.get(dn) != null ?
+          Long.toString(dnMapCopy.get(dn).getEntryID()) : null) +
+        ":" + (dnMapCopy.get(dn) != null ?
+          dnMapCopy.get(dn).getBackend().getBackendID() : null) +
+        "\n";
+    }
+
+    // See if there is anything on idMap that isnt reflected on
+    // dnMap in case maps went out of sync.
+    for (Backend backend : idMapCopy.keySet()) {
+      for (Long id : idMapCopy.get(backend).keySet()) {
+        if ((idMapCopy.get(backend).get(id) == null) ||
+            !dnMapCopy.containsKey(
+              idMapCopy.get(backend).get(id).getDN())) {
+          verboseString = verboseString +
+            (idMapCopy.get(backend).get(id) != null ?
+              idMapCopy.get(backend).get(id).getDN().toString() : null) +
+            ":" + id.toString() + ":" + backend.getBackendID() + "\n";
+        }
+      }
+    }
+
+    return (verboseString.length() > 0 ? verboseString : null);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
   @Override()
   public boolean isConfigurationAcceptable(EntryCacheCfg configuration,
                                            List<Message> unacceptableReasons)
diff --git a/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java b/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
index 75f38a8..816a658 100644
--- a/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
+++ b/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
@@ -662,7 +662,8 @@
     Entry entry = null;
     cacheReadLock.lock();
     try {
-      if (dnMap.containsKey(entryDN)) {
+      // Use get to generate entry access.
+      if (dnMap.get(entryDN) != null) {
         entry = getEntryFromDB(entryDN);
       }
     } finally {
@@ -1017,6 +1018,64 @@
   /**
    * {@inheritDoc}
    */
+  public String toVerboseString()
+  {
+    String verboseString = new String();
+
+    Map<DN,Long> dnMapCopy;
+    Map<Backend,Map<Long,DN>> backendMapCopy;
+
+    // Grab write lock to prevent any modifications
+    // to the cache maps until a snapshot is taken.
+    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 instead.
+      dnMapCopy = new LinkedHashMap<DN,Long>(dnMap);
+      backendMapCopy =
+        new LinkedHashMap<Backend,Map<Long,DN>>
+          (backendMap);
+    } finally {
+      cacheWriteLock.unlock();
+    }
+
+    // Check dnMap first.
+    for (DN dn : dnMapCopy.keySet()) {
+      Backend backend = null;
+      Iterator<Backend> backendIterator = backendMapCopy.keySet().iterator();
+      while (backendIterator.hasNext()) {
+        backend = backendIterator.next();
+        Map<Long, DN> map = backendMapCopy.get(backend);
+        if ((map.get(dnMapCopy.get(dn)) != null) &&
+            (map.get(dnMapCopy.get(dn)).equals(dn))) {
+          break;
+        }
+      }
+    }
+
+    // See if there is anything on backendMap that isnt reflected on dnMap
+    // in case maps went out of sync.
+    Backend backend = null;
+    Iterator<Backend> backendIterator = backendMapCopy.keySet().iterator();
+    while (backendIterator.hasNext()) {
+      backend = backendIterator.next();
+      Map<Long, DN> map = backendMapCopy.get(backend);
+      for (Long id : map.keySet()) {
+        if (!dnMapCopy.containsKey(map.get(id)) || map.get(id) == null) {
+          verboseString = verboseString + (map.get(id) != null ?
+            map.get(id) : null) + ":" + id.toString() + ":" +
+          backend.getBackendID() + "\n";
+        }
+      }
+    }
+
+    return (verboseString.length() > 0 ? verboseString : null);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override()
   public boolean isConfigurationAcceptable(EntryCacheCfg configuration,
                                            List<Message> unacceptableReasons)
diff --git a/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java b/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
index 85f9f34..1cf9825 100644
--- a/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
+++ b/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
@@ -282,6 +282,7 @@
     {
       map = new ConcurrentHashMap<Long,SoftReference<CacheEntry>>();
       map.put(entryID, ref);
+      idMap.put(backend, map);
     }
     else
     {
@@ -361,6 +362,12 @@
           {
             ref.clear();
           }
+          // If this backend becomes empty now remove
+          // it from the idMap map.
+          if (map.isEmpty())
+          {
+            idMap.remove(backend);
+          }
         }
       }
     }
@@ -441,6 +448,28 @@
   /**
    * {@inheritDoc}
    */
+  public String toVerboseString()
+  {
+    String verboseString = new String();
+
+    // There're no locks in this cache to keep dnMap and idMap in
+    // sync. Examine dnMap only since its more likely to be up to
+    // date than idMap. Dont bother with copies either since this
+    // is SoftReference based implementation.
+    for(SoftReference<CacheEntry> ce : dnMap.values()) {
+      verboseString = verboseString + ce.get().getDN().toString() +
+        ":" + Long.toString(ce.get().getEntryID()) + ":" +
+        ce.get().getBackend().getBackendID() + "\n";
+    }
+
+    return (verboseString.length() > 0 ? verboseString : null);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
   @Override()
   public boolean isConfigurationAcceptable(EntryCacheCfg configuration,
                                            List<Message> unacceptableReasons)
@@ -611,8 +640,9 @@
             {
               ref.clear();
 
+              Backend backend = freedEntry.getBackend();
               ConcurrentHashMap<Long,SoftReference<CacheEntry>> map =
-                   idMap.get(freedEntry.getBackend());
+                   idMap.get(backend);
               if (map != null)
               {
                 ref = map.remove(freedEntry.getEntryID());
@@ -620,6 +650,11 @@
                 {
                   ref.clear();
                 }
+                // If this backend becomes empty now remove
+                // it from the idMap map.
+                if (map.isEmpty()) {
+                  idMap.remove(backend);
+                }
               }
             }
           }

--
Gitblit v1.10.0