jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dpfis...@apache.org
Subject svn commit: r1376976 - in /jackrabbit/oak/trunk/oak-mk: ./ src/main/java/org/apache/jackrabbit/mk/persistence/ src/main/java/org/apache/jackrabbit/mk/store/ src/test/java/org/apache/jackrabbit/mk/
Date Fri, 24 Aug 2012 15:54:52 GMT
Author: dpfister
Date: Fri Aug 24 15:54:51 2012
New Revision: 1376976

URL: http://svn.apache.org/viewvc?rev=1376976&view=rev
Log:
OAK-276 - potential clash of commit id's after restart
- added test case

Added:
    jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
  (with props)
Modified:
    jackrabbit/oak/trunk/oak-mk/pom.xml
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/InMemPersistence.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/Persistence.java
    jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/store/DefaultRevisionStore.java

Modified: jackrabbit/oak/trunk/oak-mk/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/pom.xml?rev=1376976&r1=1376975&r2=1376976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-mk/pom.xml Fri Aug 24 15:54:51 2012
@@ -134,6 +134,12 @@
       <version>1.0.1</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>1.4</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
 

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java?rev=1376976&r1=1376975&r2=1376976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
(original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/H2Persistence.java
Fri Aug 24 15:54:51 2012
@@ -70,6 +70,7 @@ public class H2Persistence implements GC
         try {
             Statement stmt = con.createStatement();
             stmt.execute("create table if not exists REVS(ID binary primary key, DATA binary,
TIME timestamp)");
+            stmt.execute("create table if not exists NODES(ID binary primary key, DATA binary,
TIME timestamp)");
             stmt.execute("create table if not exists HEAD(ID binary) as select null");
             stmt.execute("create sequence if not exists DATASTORE_ID");
 /*
@@ -86,7 +87,16 @@ public class H2Persistence implements GC
         cp.dispose();
     }
 
-    public Id readHead() throws Exception {
+    public Id[] readIds() throws Exception {
+        Id lastCommitId = null;
+        Id headId = readHead();
+        if (headId != null) {
+            lastCommitId = readLastCommitId();
+        }
+        return new Id[] { headId, lastCommitId };
+    }
+    
+    private Id readHead() throws Exception {
         Connection con = cp.getConnection();
         try {
             PreparedStatement stmt = con.prepareStatement("select * from HEAD");
@@ -102,6 +112,22 @@ public class H2Persistence implements GC
         }
     }
 
+    private Id readLastCommitId() throws Exception {
+        Connection con = cp.getConnection();
+        try {
+            PreparedStatement stmt = con.prepareStatement("select MAX(ID) from REVS");
+            ResultSet rs = stmt.executeQuery();
+            byte[] rawId = null;
+            if (rs.next()) {
+                rawId = rs.getBytes(1);
+            }
+            stmt.close();
+            return rawId == null ? null : new Id(rawId); 
+        } finally {
+            con.close();
+        }
+    }
+
     public void writeHead(Id id) throws Exception {
         Connection con = cp.getConnection();
         try {
@@ -118,7 +144,7 @@ public class H2Persistence implements GC
         Id id = node.getId();
         Connection con = cp.getConnection();
         try {
-            PreparedStatement stmt = con.prepareStatement("select DATA from REVS where ID
= ?");
+            PreparedStatement stmt = con.prepareStatement("select DATA from NODES where ID
= ?");
             try {
                 stmt.setBytes(1, id.getBytes());
                 ResultSet rs = stmt.executeQuery();
@@ -148,7 +174,7 @@ public class H2Persistence implements GC
         try {
             PreparedStatement stmt = con
                     .prepareStatement(
-                            "insert into REVS (ID, DATA, TIME) select ?, ?, ? where not exists
(select 1 from REVS where ID = ?)");
+                            "insert into NODES (ID, DATA, TIME) select ?, ?, ? where not
exists (select 1 from NODES where ID = ?)");
             try {
                 stmt.setBytes(1, rawId);
                 stmt.setBytes(2, bytes);
@@ -212,7 +238,7 @@ public class H2Persistence implements GC
     public ChildNodeEntriesMap readCNEMap(Id id) throws NotFoundException, Exception {
         Connection con = cp.getConnection();
         try {
-            PreparedStatement stmt = con.prepareStatement("select DATA from REVS where ID
= ?");
+            PreparedStatement stmt = con.prepareStatement("select DATA from NODES where ID
= ?");
             try {
                 stmt.setBytes(1, id.getBytes());
                 ResultSet rs = stmt.executeQuery();
@@ -241,7 +267,7 @@ public class H2Persistence implements GC
         try {
             PreparedStatement stmt = con
                     .prepareStatement(
-                            "insert into REVS (ID, DATA, TIME) select ?, ?, ? where not exists
(select 1 from REVS where ID = ?)");
+                            "insert into NODES (ID, DATA, TIME) select ?, ?, ? where not
exists (select 1 from NODES where ID = ?)");
             try {
                 stmt.setBytes(1, rawId);
                 stmt.setBytes(2, bytes);
@@ -264,7 +290,7 @@ public class H2Persistence implements GC
     
     @Override
     public boolean markCommit(Id id) throws Exception {
-        return touch(id, gcStart);
+        return touch("REVS", id, gcStart);
     }
 
     @Override
@@ -292,22 +318,23 @@ public class H2Persistence implements GC
     
     @Override
     public boolean markNode(Id id) throws Exception {
-        return touch(id, gcStart);
+        return touch("NODES", id, gcStart);
     }
 
     @Override
     public boolean markCNEMap(Id id) throws Exception {
-        return touch(id, gcStart);
+        return touch("NODES", id, gcStart);
     }
     
-    private boolean touch(Id id, long timeMillis) throws Exception {
+    private boolean touch(String table, Id id, long timeMillis) throws Exception {
         Timestamp ts = new Timestamp(timeMillis);
 
         Connection con = cp.getConnection();
         try {
-            PreparedStatement stmt = con
-                    .prepareStatement(
-                            "update REVS set TIME = ? where ID = ? and TIME < ?");
+            PreparedStatement stmt = con.prepareStatement(
+                    String.format("update %s set TIME = ? where ID = ? and TIME < ?",
+                            table));
+                                    
             try {
                 stmt.setTimestamp(1, ts);
                 stmt.setBytes(2, id.getBytes());
@@ -324,20 +351,29 @@ public class H2Persistence implements GC
     @Override
     public int sweep() throws Exception {
         Timestamp ts = new Timestamp(gcStart);
+        int swept = 0;
 
         Connection con = cp.getConnection();
         try {
-            PreparedStatement stmt = con
-                    .prepareStatement(
-                            "delete REVS where TIME < ?");
+            PreparedStatement stmt = con.prepareStatement("delete REVS where TIME < ?");
+            try {
+                stmt.setTimestamp(1, ts);
+                swept += stmt.executeUpdate();
+            } finally {
+                stmt.close();
+            }
+
+            stmt = con.prepareStatement("delete NODES where TIME < ?");
+            
             try {
                 stmt.setTimestamp(1, ts);
-                return stmt.executeUpdate();
+                swept += stmt.executeUpdate();
             } finally {
                 stmt.close();
             }
         } finally {
             con.close();
         }
+        return swept;
      }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/InMemPersistence.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/InMemPersistence.java?rev=1376976&r1=1376975&r2=1376976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/InMemPersistence.java
(original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/InMemPersistence.java
Fri Aug 24 15:54:51 2012
@@ -42,7 +42,6 @@ public class InMemPersistence implements
     private final Map<Id, byte[]> objects = Collections.synchronizedMap(new HashMap<Id,
byte[]>());
     private final Map<Id, byte[]> marked = Collections.synchronizedMap(new HashMap<Id,
byte[]>());
 
-    private Id head;
     private long gcStart;
 
     // TODO: make this configurable
@@ -53,12 +52,13 @@ public class InMemPersistence implements
         // nothing to initialize
     }
     
-    public Id readHead() {
-        return head;
+    @Override
+    public Id[] readIds() throws Exception {
+        return new Id[2];
     }
 
     public void writeHead(Id id) {
-        head = id;
+        
     }
 
     public void readNode(StoredNode node) throws NotFoundException, Exception {

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/Persistence.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/Persistence.java?rev=1376976&r1=1376975&r2=1376976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/Persistence.java
(original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/persistence/Persistence.java
Fri Aug 24 15:54:51 2012
@@ -36,7 +36,19 @@ public interface Persistence extends Clo
 
     public void initialize(File homeDir) throws Exception;
     
-    Id readHead() throws Exception;
+    /**
+     * Return an array of ids, where the first is the head id (as stored
+     * with {@link #writeHead(Id)}) and the second is the highest commit
+     * id found or {@code null}.
+     * <p/>
+     * This method is not guaranteed to deliver "live" results, after
+     * something is written to the storage, so it should better be used
+     * once after initialization. 
+     * 
+     * @return array of ids
+     * @throws Exception if an error occurs
+     */
+    Id[] readIds() throws Exception;
 
     void writeHead(Id id) throws Exception;
 

Modified: jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/store/DefaultRevisionStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/store/DefaultRevisionStore.java?rev=1376976&r1=1376975&r2=1376976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/store/DefaultRevisionStore.java
(original)
+++ jackrabbit/oak/trunk/oak-mk/src/main/java/org/apache/jackrabbit/mk/store/DefaultRevisionStore.java
Fri Aug 24 15:54:51 2012
@@ -134,7 +134,8 @@ public class DefaultRevisionStore extend
         cache = Collections.synchronizedMap(SimpleLRUCache.<Id, Object> newInstance(initialCacheSize));
 
         // make sure we've got a HEAD commit
-        head = pm.readHead();
+        Id[] ids = pm.readIds();
+        head = ids[0];
         if (head == null || head.getBytes().length == 0) {
             // assume virgin repository
             byte[] rawHead = Id.fromLong(commitCounter.incrementAndGet())
@@ -148,7 +149,11 @@ public class DefaultRevisionStore extend
             pm.writeCommit(head, initialCommit);
             pm.writeHead(head);
         } else {
-            commitCounter.set(Long.parseLong(head.toString(), 16));
+            Id lastCommitId = head;
+            if (ids[1] != null && ids[1].compareTo(lastCommitId) > 0) {
+                lastCommitId = ids[1];
+            }
+            commitCounter.set(Long.parseLong(lastCommitId.toString(), 16));
         }
 
         if (gcpm != null) {

Added: jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java?rev=1376976&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
(added)
+++ jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
Fri Aug 24 15:54:51 2012
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mk;
+
+import java.io.File;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.mk.core.MicroKernelImpl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class MicroKernelImplTest {
+    
+    private File homeDir;
+    private MicroKernelImpl mk;
+    
+    @Before
+    public void setup() throws Exception {
+        homeDir = new File("target/mk");
+        if (homeDir.exists()) {
+            FileUtils.cleanDirectory(homeDir);
+        }
+        mk = new MicroKernelImpl(homeDir.getPath());
+    }
+    
+    @After
+    public void tearDown() throws Exception {
+        if (mk != null) {
+            mk.dispose();
+        }
+    }
+    
+    /**
+     * OAK-276: potential clash of commit id's after restart.
+     */
+    @Test
+    public void potentialClashOfCommitIds() {
+        String headRev = mk.commit("/", "+\"a\" : {}", mk.getHeadRevision(), null);
+        String branchRev = mk.branch(mk.getHeadRevision());
+        
+        mk.dispose();
+        mk = new MicroKernelImpl(homeDir.getPath());
+        assertEquals("Stored head should be equal", headRev, mk.getHeadRevision());
+
+        headRev = mk.commit("/", "+\"b\" : {}", mk.getHeadRevision(), null);
+        assertFalse("Commit must not have same id as branch", headRev.equals(branchRev));
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-mk/src/test/java/org/apache/jackrabbit/mk/MicroKernelImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message