This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/master by this push:
     new 08ca0b8  JENA-1817: Flush NodeTableCache in CommitFinish
     new 03f5253  Merge pull request #672 from afs/jena1817_tdb2_node_cache
08ca0b8 is described below

commit 08ca0b8321766779978c56961d4c0820b11432d2
Author: Andy Seaborne <[email protected]>
AuthorDate: Thu Jan 16 11:21:52 2020 +0000

    JENA-1817: Flush NodeTableCache in CommitFinish
---
 .../transaction/txn/TransactionCoordinator.java    | 16 ++-----
 .../jena/tdb2/store/nodetable/NodeTableCache.java  |  3 +-
 .../tdb2/store/nodetable/ThreadBufferingCache.java | 22 ++-------
 .../test/java/org/apache/jena/tdb2/TestTDB2.java   | 54 +++++++++++++++++++++-
 4 files changed, 61 insertions(+), 34 deletions(-)

diff --git 
a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionCoordinator.java
 
b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionCoordinator.java
index 42d2697..761c975 100644
--- 
a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionCoordinator.java
+++ 
b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionCoordinator.java
@@ -122,21 +122,16 @@ public class TransactionCoordinator {
         this(Journal.create(location));
     }
 
-    /** Create a TransactionCoordinator, initially with no associated {@link 
TransactionalComponent}s */
+    /** Create a TransactionCoordinator, initially with no associated {@link 
TransactionalComponent TransactionalComponents}. */
     public TransactionCoordinator(Journal journal) {
         this(journal, null , new ArrayList<>());
     }
 
-    /** Create a TransactionCoordinator, initially with {@link 
TransactionalComponent} in the ComponentGroup */
+    /** Create a TransactionCoordinator, initially with {@link 
TransactionalComponent TransactionalComponents} in the ComponentGroup */
     public TransactionCoordinator(Journal journal, 
List<TransactionalComponent> components) {
         this(journal, components , new ArrayList<>());
     }
 
-    //    /** Create a TransactionCoordinator, initially with no associated 
{@link TransactionalComponent}s */
-//    public TransactionCoordinator(Location journalLocation) {
-//        this(Journal.create(journalLocation), new ArrayList<>() , new 
ArrayList<>());
-//    }
-
     private TransactionCoordinator(Journal journal, 
List<TransactionalComponent> txnComp, List<ShutdownHook> shutdownHooks) {
         this.journal = journal;
         this.shutdownHooks = new ArrayList<>(shutdownHooks);
@@ -467,7 +462,7 @@ public class TransactionCoordinator {
     /**
      * Block until no writers are active, optionally blocking or returning if 
can't at the moment.
      * <p>
-     * Unlike a write transction, there is no associated transaction.
+     * Unlike a write transaction, there is no associated transaction.
      * <p>
      * If it returns true, the application must call {@link #enableWriters} 
later.
      * @param canBlock
@@ -732,11 +727,6 @@ public class TransactionCoordinator {
     /*package*/ void executeCommit(Transaction transaction, Runnable commit, 
Runnable finish, Runnable sysabort) {
         notifyCommitStart(transaction);
         if ( transaction.getMode() == ReadWrite.READ ) {
-
-            //[1746]
-            //executeCommitReader();
-            // No commit on components, all "end".
-            // Make abort the same?
             finish.run();
             notifyCommitFinish(transaction);
             return;
diff --git 
a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/NodeTableCache.java
 
b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/NodeTableCache.java
index aa02c25..ef18e84 100644
--- 
a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/NodeTableCache.java
+++ 
b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/NodeTableCache.java
@@ -326,7 +326,7 @@ public class NodeTableCache implements NodeTable, 
TransactionListener {
     }
 
     @Override
-    public void notifyCompleteFinish(Transaction transaction) {
+    public void notifyCommitFinish(Transaction transaction) {
         if(transaction.isWriteTxn()) {
             updateCommit();
         }
@@ -363,7 +363,6 @@ public class NodeTableCache implements NodeTable, 
TransactionListener {
 
     private void updateCommit() {
         writingThread = null;
-        // Write to main caches.
         node2id_Cache.flushBuffer();
         id2node_Cache.flushBuffer();
     }
diff --git 
a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/ThreadBufferingCache.java
 
b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/ThreadBufferingCache.java
index b683671..6837d95 100644
--- 
a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/ThreadBufferingCache.java
+++ 
b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/nodetable/ThreadBufferingCache.java
@@ -32,8 +32,8 @@ import org.apache.jena.tdb2.TDBException;
 /**
  * A cache that buffers changes.
  * <p>
- * It has two modes, when active it captures updates and the underlying main 
cache is
- * only updated when {@link #flushBuffer} is called. When not active, it passes
+ * It has two modes: when active, it captures updates and the underlying main 
cache is
+ * only updated when {@link #flushBuffer} is called; when not active, it passes
  * updates straight through.
  * <p>
  * For access operations, it looks in the buffered cache and the underlying 
cache as
@@ -65,8 +65,8 @@ public class ThreadBufferingCache<Key,Value> implements 
Cache<Key,Value> {
     private boolean buffering() {
         if ( ! BUFFERING )
             return false;
-        // Changes are sync'ed and the only way to change this value is via a 
sync'ed method.
-        if ( bufferingThread == null )
+        // Changes are sync'ed externally and the only way to change this 
value is via a sync'ed method.
+        if ( bufferingThread.get() == null )
             return false;
         Thread currentThread = Thread.currentThread();
         return bufferingThread.get() == currentThread;
@@ -93,16 +93,11 @@ public class ThreadBufferingCache<Key,Value> implements 
Cache<Key,Value> {
     public void flushBuffer() {
         if ( ! buffering() )
             return ;
-        //System.out.println(label+": Flush:1 L: "+localCache().size());
-        //System.out.println(label+": Flush:1 M: "+baseCache.size());
-
         localCache().keys().forEachRemaining(k->{
             Value value = localCache().getIfPresent(k);
             baseCache.put(k, value);
         });
         localCache().clear();
-        //System.out.println(label+": Flush:2 L: "+localCache().size());
-        //System.out.println(label+": Flush:2 M: "+baseCache.size());
         bufferingThread.set(null);
     }
 
@@ -110,8 +105,6 @@ public class ThreadBufferingCache<Key,Value> implements 
Cache<Key,Value> {
     public void dropBuffer() {
         if ( ! buffering() )
             return ;
-        //System.out.println(label+": Drop: L: "+localCache().size());
-        //System.out.println(label+": Drop: M: "+baseCache.size());
         localCache().clear();
         bufferingThread.set(null);
     }
@@ -164,12 +157,6 @@ public class ThreadBufferingCache<Key,Value> implements 
Cache<Key,Value> {
         return item;
     }
 
-    // ---- Flush changes, reset.
-
-
-
-    // ---- Updates to buffering, local cache.
-
     /** Goes into local cache. */
     @Override
     public void put(Key key, Value value) {
@@ -189,7 +176,6 @@ public class ThreadBufferingCache<Key,Value> implements 
Cache<Key,Value> {
         localCache().remove(key);
     }
 
-
     @Override
     public Iterator<Key> keys() {
         if ( ! buffering() )
diff --git a/jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/TestTDB2.java 
b/jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/TestTDB2.java
index 211c6cf..d1af13c 100644
--- a/jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/TestTDB2.java
+++ b/jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/TestTDB2.java
@@ -19,8 +19,12 @@
 package org.apache.jena.tdb2;
 
 import java.io.ByteArrayOutputStream;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.jena.atlas.lib.FileOps;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.atlas.lib.ThreadLib;
 import org.apache.jena.query.TxnType;
 import org.apache.jena.riot.Lang;
 import org.apache.jena.riot.RDFDataMgr;
@@ -58,7 +62,6 @@ public class TestTDB2 {
     // Errors that can occur:
     //   One common term -> no conversion.
     //   Two common terms -> bad read.
-    // Feature control: ThreadBufferingCache.BUFFERING
 
     @Test public void abort1() {
         Quad q1 = SSE.parseQuad("(:g :s :p :o)");
@@ -98,4 +101,53 @@ public class TestTDB2 {
     private static void output(DatasetGraph dsg) {
         Txn.executeRead(dsg, ()->RDFDataMgr.write(new ByteArrayOutputStream(), 
 dsg, Lang.NQUADS));
     }
+    
+    //JENA-1817: Two W txn, where the second queues on entry.  
+    @Test public void multiple_writers() {
+        Quad q1 = SSE.parseQuad("(:g :s :p :o1)");
+        Quad q2 = SSE.parseQuad("(:g :s :p :o2)");
+        DatasetGraph dsg = DatabaseMgr.createDatasetGraph();
+        
+        // Test controls
+        Semaphore sema =  new Semaphore(0);
+        Semaphore semaTestFinished =  new Semaphore(0);
+        
+        // Setup writers.
+        Runnable r1 = ()->{
+            Txn.executeWrite(dsg,  ()->{
+                // Allow thread 2 run and try to enter the W txn 
+                sema.release(1);
+                dsg.add(q1);
+                // Gives thread2 a chance to enter (can't do this by lock).
+                // It is unfortunate that it's a timeout.
+                Lib.sleep(250);
+            });
+            // Finished.
+            semaTestFinished.release(1);
+        };
+        
+        Runnable r2 = ()->{
+            acquire(sema,1);
+            // Thread 1 is now inside its W txn.
+            Txn.executeWrite(dsg, () -> dsg.add(q2));
+            semaTestFinished.release(1);
+        };
+        ThreadLib.async(r2);
+        ThreadLib.async(r1);
+        
+        // Trigger writers. 
+        sema.release(2);
+        // Wait until test threads have finished
+        acquire(semaTestFinished, 2);
+    }
+    
+    private static void acquire(Semaphore semaphore, int permits) {
+        try {
+            boolean b = semaphore.tryAcquire(permits, 1000, 
TimeUnit.MILLISECONDS);
+            if ( !b )
+                throw new RuntimeException("Test failure - did not get permits 
in the time allowed");
+        } catch (InterruptedException ex) {
+            ex.printStackTrace();
+        }
+    }
 }

Reply via email to