You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2007/10/10 11:50:39 UTC

svn commit: r583413 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/data/FileDataStore.java test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java

Author: thomasm
Date: Wed Oct 10 02:50:38 2007
New Revision: 583413

URL: http://svn.apache.org/viewvc?rev=583413&view=rev
Log:
JCR-1138: Data store garbage collection deleted transient objects

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java?rev=583413&r1=583412&r2=583413&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java Wed Oct 10 02:50:38 2007
@@ -21,11 +21,13 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.ref.WeakReference;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.WeakHashMap;
 
 /**
  * Simple file-based data store. Data records are stored as normal files
@@ -88,6 +90,11 @@
      * The minimum size of an object that should be stored in this data store.
      */
     private int minRecordLength = DEFAULT_MIN_RECORD_LENGTH;
+    
+    /**
+     * All data identifiers that are currently in use are in this set until they are garbage collected.
+     */
+    private WeakHashMap inUse = new WeakHashMap();
 
     /**
      * Creates a uninitialized data store.
@@ -127,8 +134,13 @@
                 file.setLastModified(System.currentTimeMillis());
             }
         }
+        usesIdentifier(identifier);
         return new FileDataRecord(identifier, file);
     }
+    
+    private void usesIdentifier(DataIdentifier identifier) {
+        inUse.put(identifier, new WeakReference(identifier));
+    }
 
     /**
      * Creates a new data record. 
@@ -164,6 +176,7 @@
             // Check if the same record already exists, or
             // move the temporary file in place if needed
             DataIdentifier identifier = new DataIdentifier(digest.digest());
+            usesIdentifier(identifier);
             File file = getFile(identifier);
             File parent = file.getParentFile();
             if (!parent.isDirectory()) {
@@ -208,6 +221,7 @@
      * @return identified file
      */
     private File getFile(DataIdentifier identifier) {
+        usesIdentifier(identifier);
         String string = identifier.toString();
         File file = directory;
         file = new File(file, string.substring(0, 2));
@@ -249,8 +263,11 @@
         int count = 0;
         if (file.isFile() && file.exists() && file.canWrite()) {
             if (file.lastModified() < min) {
-                file.delete();
-                count++;
+                DataIdentifier id = new DataIdentifier(file.getName());
+                if (!inUse.containsKey(id)) {
+                    file.delete();
+                    count++;
+                }
             }
         } else if (file.isDirectory()) {
             File[] list = file.listFiles();
@@ -289,6 +306,14 @@
             }
         }
         return identifiers.iterator();
+    }
+    
+    /**
+     * Clear the in-use list. This is only used for testing to make the the garbage collection
+     * think that objects are no longer in use.
+     */
+    public void clearInUse() {
+        inUse.clear();
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java?rev=583413&r1=583412&r2=583413&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/GarbageCollectorTest.java Wed Oct 10 02:50:38 2007
@@ -22,8 +22,10 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Iterator;
 
+import javax.jcr.Credentials;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
@@ -48,8 +50,8 @@
         }
         
         deleteMyNodes();
-        runGC(session);
-        runGC(session);
+        runGC(session, true);
+        runGC(session, true);
         
         root.addNode("node1");
         Node node2 = root.addNode("node2");
@@ -61,16 +63,18 @@
         
         n.remove();
         session.save();
+        Thread.sleep(1000);
         
         GarbageCollector gc = new GarbageCollector(this, 0);
-        gc.setTestDelay(100);
+        gc.setTestDelay(1000);
         
         LOG.debug("scanning...");
         gc.scan(session);
         int count = listIdentifiers(gc);
-        LOG.debug("stop scanning...");
+        LOG.debug("stop scanning; currently " + count + " identifiers");
         gc.stopScan();
         LOG.debug("deleting...");
+        ((FileDataStore) gc.getDataStore()).clearInUse();
         assertTrue(gc.deleteUnused() > 0);
         int count2 = listIdentifiers(gc);
         assertEquals(count - 1, count2);
@@ -78,11 +82,14 @@
         deleteMyNodes();
     }
     
-    private void runGC(Session session) throws RepositoryException, IOException {
+    private void runGC(Session session, boolean all) throws RepositoryException, IOException {
         GarbageCollector gc = new GarbageCollector(this, 0);
-        gc.setTestDelay(100);
+        gc.setTestDelay(1000);
         gc.scan(session);
         gc.stopScan();
+        if (all) {
+            ((FileDataStore) gc.getDataStore()).clearInUse();
+        }
         gc.deleteUnused();
     }
     
@@ -96,6 +103,47 @@
             count++;
         }
         return count;
+    }
+    
+    public void testTransientObjects() throws Exception {
+        
+        Node root = testRootNode;
+        Session session = root.getSession();
+        
+        RepositoryImpl rep = (RepositoryImpl) session.getRepository();
+        if (rep.getDataStore() == null) {
+            LOG.info("testTransientObjects skipped. Data store is not used.");
+            return;
+        }
+        
+        deleteMyNodes();
+        runGC(session, true);
+        runGC(session, true);
+
+        Credentials cred = helper.getSuperuserCredentials();
+        Session s2 = helper.getRepository().login(cred);
+        root = s2.getRootNode();
+        Node node2 = root.addNode("node3");
+        Node n = node2.addNode("nodeWithBlob");
+        n.setProperty("test", new RandomInputStream(10, 10000));
+        Thread.sleep(1000);
+
+        runGC(session, false);
+
+        s2.save();
+        
+        InputStream in = n.getProperty("test").getStream();
+        InputStream in2 = new RandomInputStream(10, 10000);
+        while (true) {
+            int a = in.read();
+            int b = in2.read();
+            assertEquals(a, b);
+            if (a < 0) {
+                break;
+            }
+        }
+        
+        deleteMyNodes();
     }
 
     public void afterScanning(Node n) throws RepositoryException {