2016-01-18 34 views
0

我創建了一個類,其中包裝Graph。例如:泰坦卡桑德拉Multithreaded交易鎖定

public class GraphManager(){ 
    Graph graph; 
    public GraphManager(Graph graph){ 
     this.graph = graph; 
    } 
    public void commitGraph(){ 
     graph.commit(); 
    } 
} 

GraphManager讓我與預定義的方式圖形交互。我構建此GraphManager使用工廠:

public class GraphManagerFactory(){ 
    public static GraphManager getGraphManager(){ 
     return new GraphManager(TitanFactory.open("conf/titan-cassandra.properties")); 
    } 
} 

這是基本的框架。現在解決問題,使用休息控制器我收到一個JSON文件。這導致實例化一個GraphManager,該文件將該文件轉換爲圖形並提交。基本範例如下:

public class Controller(){ 
    public List<String> handleRequest(){ 
     GraphManager manager = GraphManagerFactory.getGraphManager(); 
     //Do some work with graph manager 
     synchronised(Controller.class){ 
      manager.commitGraph(); 
     } 
    } 
} 

通過上面的代碼,我確保只有一個線程可以隨時向圖提交。然而,儘管我仍然獲得了PermanentLockingException

com.thinkaurelius.titan.diskstorage.locking.PermanentLockingException: Local lock contention 
at com.thinkaurelius.titan.diskstorage.locking.AbstractLocker.writeLock(AbstractLocker.java:313) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.diskstorage.locking.consistentkey.ExpectedValueCheckingStore.acquireLock(ExpectedValueCheckingStore.java:89) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.diskstorage.keycolumnvalue.KCVSProxy.acquireLock(KCVSProxy.java:40) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.diskstorage.BackendTransaction.acquireIndexLock(BackendTransaction.java:240) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.graphdb.database.StandardTitanGraph.prepareCommit(StandardTitanGraph.java:554) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.graphdb.database.StandardTitanGraph.commit(StandardTitanGraph.java:683) ~[titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.graphdb.transaction.StandardTitanTx.commit(StandardTitanTx.java:1352) [titan-core-1.0.0.jar:na] 
at com.thinkaurelius.titan.graphdb.tinkerpop.TitanBlueprintsGraph$GraphTransaction.doCommit(TitanBlueprintsGraph.java:263) [titan-core-1.0.0.jar:na] 
at org.apache.tinkerpop.gremlin.structure.util.AbstractTransaction.commit(AbstractTransaction.java:94) [gremlin-core-3.0.2-incubating.jar:3.0.2-incubating] 
at io.mindmaps.core.accessmanager.GraphAccessManagerImpl.commit(GraphAccessManagerImpl.java:811) [mindmaps-core-0.0.5-SNAPSHOT.jar:na] 
at io.mindmaps.graphmanager.listener.TransactionController.commitGraph(TransactionController.java:98) [classes/:na] 
at io.mindmaps.graphmanager.listener.TransactionController.validateAndCommit(TransactionController.java:84) [classes/:na] 
at io.mindmaps.graphmanager.listener.TransactionController.loadData(TransactionController.java:66) [classes/:na] 
at io.mindmaps.graphmanager.listener.TransactionController.lambda$postTransaction$0(TransactionController.java:43) [classes/:na] 
at io.mindmaps.graphmanager.loader.QueueManager.handleJob(QueueManager.java:76) ~[classes/:na] 
at io.mindmaps.graphmanager.loader.QueueManager.lambda$addJob$3(QueueManager.java:24) ~[classes/:na] 
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_66] 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_66] 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_66] 
at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_66] 

這怎麼只有一個時提交是否允許在同一時間發生的呢?

回答

2

首先,我建議您不要爲每個請求(昂貴)創建一個TitanGraph實例。創建一個TitanGraph並跨請求共享。接下來,您需要非常小心Web應用程序事務不會在請求之間泄漏(事務綁定到當前線程)。您可以通過確保請求在其完成後自動完成(錯誤或成功根據需要)始終發出rollback()commit()來確保這一點。您可以通過在新請求開始時發出rollback()加倍保證。

考慮到這一切,讓我們回答你的問題。僅僅因爲您將commit()操作限制爲單個線程並不妨礙其他線程打開事務。由另一個線程處理的另一個請求可能很容易嘗試使用grab a lock作爲同一個鍵,並在提交結束時鎖定在您看到的鎖定異常處。

Transaction will eventually fail in sufficiently large systems.PermanentLockingException必須被視爲使用鎖的預期副作用,處理它們的典型方法是通過重試整個事務處理遇到一個鎖。你應該在這個前提下設計你的基礎架構。

在這個領域的一些其他提示:

  1. 你應該限制使用鎖在可能的情況,因爲他們可能成爲瓶頸。如果有什麼方法可以讓你在沒有他們的情況下工作 - 這樣做。
  2. 當您發出一些鎖定更改時,保持事務處理的簡短。他們保持開放的時間越長,與另一個請求爭用的機會就越大。
2

雖然我接受的答案是100%正確的。我想強調的更清楚我做了什麼,從鎖競爭逃脫(這在很大程度上是基於/感謝接受的答案):

第1步:作爲建議,而不是包裹圖我包一個實例每個新交易GraphManager。即我做了如下工廠:

public class GraphManagerFactory(){ 
    TitanGraph instance; 
    public static GraphManager getGraphManager(){ 
     if(instance = null){ 
      instance = TitanFactory.open("conf/titan-cassandra.properties"); 
     } 
     return new GraphManager(instance.newTransaction()); 
    } 
} 

這一步導致了很多改進。我仍然有爭議,但他們解決得更快。

第2步:第一次構建圖時,我還提前提供了該圖。具體而言,不要讓泰坦隱式地構建頂點屬性和邊,我在添加第一個頂點之前明確地構建它們。這是使用邊緣標籤management.makeEdgeLabel(label).make();和頂點屬性使用management.makePropertyKey(label).dataType(String.class).make();的簡單方法。另外一個好處是我可以更容易地執行批量加載。這意味着再次擴大Factory爲:

public class GraphManagerFactory(){ 
    TitanGraph instance; 
    public static GraphManager getGraphManager(){ 
     if(instance = null){ 
      instance = TitanFactory.open("conf/titan-cassandra.properties"); 
      TitanManagement management = instance.openManagement(); 
      //Check if the labels exist before creating explicitly. 
      //If they don't exist do the following: 
      management.makeEdgeLabel("EdgeLabel").make(); 
      management.makePropertyKey("property").dataType(String.class).make(); 
      management.commit(); 
     } 
     return new GraphManager(instance.newTransaction()); 
    } 
} 

步驟3:其中除去爭論幾乎完全是增加ID塊大小graph.configuration().setProperty("ids.block-size", 100000);的最後一步。儘管我正在同時執行大型加載操作,但這最後一步可能只適用於我。