问题
We were wishfully thinking that with Neo4j 2.0, concurrently querying and deleting nodes will no longer throw NotFoundException. Our previous system using Neo4j 1.9.3 was full of checks to handle this exception(very ugly code). Is there any better way to handle NotFoundException(if not eliminate) in neo4j 2.0 or, in the pipeline?
Stack Trace:
org.neo4j.graphdb.NotFoundException: Node 2432 not found
at org.neo4j.kernel.impl.core.NodeManager.getNodeForProxy(NodeManager.java:425)
at org.neo4j.kernel.impl.api.state.OldTxStateBridgeImpl.deleteNode(OldTxStateBridgeImpl.java:111)
at org.neo4j.kernel.impl.api.state.TxStateImpl.nodeDoDelete(TxStateImpl.java:250)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.nodeDelete(StateHandlingStatementOperations.java:100)
at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.nodeDelete(ConstraintEnforcingEntityOperations.java:140)
at org.neo4j.kernel.impl.api.LockingStatementOperations.nodeDelete(LockingStatementOperations.java:196)
at org.neo4j.kernel.impl.api.OperationsFacade.nodeDelete(OperationsFacade.java:428)
at org.neo4j.cypher.internal.spi.v2_0.TransactionBoundExecutionContext$NodeOperations.delete(TransactionBoundExecutionContext.scala:132)
at org.neo4j.cypher.internal.spi.v2_0.TransactionBoundExecutionContext$NodeOperations.delete(TransactionBoundExecutionContext.scala:130)
at org.neo4j.cypher.internal.compiler.v2_0.spi.DelegatingOperations.delete(DelegatingQueryContext.scala:92)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$ExceptionTranslatingOperations.org$neo4j$cypher$internal$compiler$v2_0$spi$ExceptionTranslatingQueryContext$ExceptionTranslatingOperations$$super$delete(ExceptionTranslatingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$ExceptionTranslatingOperations$$anonfun$delete$1.apply$mcV$sp(ExceptionTranslatingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$ExceptionTranslatingOperations$$anonfun$delete$1.apply(ExceptionTranslatingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$ExceptionTranslatingOperations$$anonfun$delete$1.apply(ExceptionTranslatingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext.org$neo4j$cypher$internal$compiler$v2_0$spi$ExceptionTranslatingQueryContext$$translateException(ExceptionTranslatingQueryContext.scala:149)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$ExceptionTranslatingOperations.delete(ExceptionTranslatingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.spi.UpdateCountingQueryContext$CountingOps.delete(UpdateCountingQueryContext.scala:118)
at org.neo4j.cypher.internal.compiler.v2_0.mutation.DeleteEntityAction.org$neo4j$cypher$internal$compiler$v2_0$mutation$DeleteEntityAction$$delete(DeleteEntityAction.scala:50)
at org.neo4j.cypher.internal.compiler.v2_0.mutation.DeleteEntityAction.exec(DeleteEntityAction.scala:36)
at org.neo4j.cypher.internal.compiler.v2_0.pipes.ExecuteUpdateCommandsPipe.org$neo4j$cypher$internal$compiler$v2_0$pipes$ExecuteUpdateCommandsPipe$$exec(ExecuteUpdateCommandsPipe.scala:56)
at org.neo4j.cypher.internal.compiler.v2_0.pipes.ExecuteUpdateCommandsPipe$$anonfun$org$neo4j$cypher$internal$compiler$v2_0$pipes$ExecuteUpdateCommandsPipe$$executeMutationCommands$1$$anonfun$apply$2.apply(ExecuteUpdateCommandsPipe.scala:45)
at org.neo4j.cypher.internal.compiler.v2_0.pipes.ExecuteUpdateCommandsPipe$$anonfun$org$neo4j$cypher$internal$compiler$v2_0$pipes$ExecuteUpdateCommandsPipe$$executeMutationCommands$1$$anonfun$apply$2.apply(ExecuteUpdateCommandsPipe.scala:45)
at scala.collection.Iterator$$anon$13.hasNext(Iterator.scala:371)
at scala.collection.Iterator$$anon$13.hasNext(Iterator.scala:371)
at org.neo4j.cypher.internal.compiler.v2_0.pipes.EmptyResultPipe.internalCreateResults(EmptyResultPipe.scala:28)
at org.neo4j.cypher.internal.compiler.v2_0.pipes.PipeWithSource.createResults(Pipe.scala:71)
at org.neo4j.cypher.internal.compiler.v2_0.executionplan.ExecutionPlanBuilder.org$neo4j$cypher$internal$compiler$v2_0$executionplan$ExecutionPlanBuilder$$prepareStateAndResult(ExecutionPlanBuilder.scala:149)
at org.neo4j.cypher.internal.compiler.v2_0.executionplan.ExecutionPlanBuilder$$anonfun$3.apply(ExecutionPlanBuilder.scala:136)
at org.neo4j.cypher.internal.compiler.v2_0.executionplan.ExecutionPlanBuilder$$anonfun$3.apply(ExecutionPlanBuilder.scala:135)
at org.neo4j.cypher.internal.compiler.v2_0.executionplan.ExecutionPlanBuilder$$anon$6.execute(ExecutionPlanBuilder.scala:50)
at org.neo4j.cypher.internal.ExecutionPlanWrapperForV2_0.execute(CypherCompiler.scala:93)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:61)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:55)
at org.neo4j.cypher.javacompat.ExecutionEngine.execute(ExecutionEngine.java:65)
at net.ahm.graph.ConcurrDeleteLab$1.run(ConcurrDeleteLab.java:65)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744
Source code to simulate the issue:
package net.ahm.graph;
import java.io.File;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.graphdb.schema.Schema;
import org.neo4j.kernel.impl.util.FileUtils;
import org.neo4j.kernel.impl.util.StringLogger;
public class ConcurrDeleteLab {
private static final Logger LOG = Logger.getLogger(CypherLab.class);
private final static int CONCURRENCY = 4;
public static void main(String[] args) throws Exception {
FileUtils.deleteRecursively(new File("graphdb"));
final GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder("graphdb")
.setConfig(GraphDatabaseSettings.use_memory_mapped_buffers, "true").setConfig(GraphDatabaseSettings.cache_type, "strong")
.newGraphDatabase();
registerShutdownHook(graphDb);
LOG.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> NUMBER OF PARALLEL CYPHERS: " + CONCURRENCY);
LOG.info(">>>> STARTED GRAPHDB");
createIndex("Entity", "name", graphDb);
try (Transaction tx = graphDb.beginTx()) {
for (int i = 0; i < 100000; i++) {
Node child = graphDb.createNode(DynamicLabel.label("Entity"));
child.setProperty("name", "entity" + i);
}
tx.success();
}
LOG.info(">>>> CREATED NODES");
final ExecutionEngine engine = new ExecutionEngine(graphDb, StringLogger.SYSTEM);
for (int i = 0; i < 4; i++) {
try (Transaction tx = graphDb.beginTx()) {
ExecutionResult result = engine.execute("match (n:Entity) return n.name");
for (Map<String, Object> row : result) {
assert ((String) row.get("n.name") != null);
}
tx.success();
}
}
LOG.info(">>>> WARMED UP");
ExecutorService es = Executors.newFixedThreadPool(CONCURRENCY);
final CountDownLatch cdl = new CountDownLatch(CONCURRENCY);
for (int i = 0; i < CONCURRENCY; i++) {
if (i % 2 == 0) {
es.execute(new Runnable() {
@Override
public void run() {
try (Transaction tx = graphDb.beginTx()) {
long time = System.currentTimeMillis();
engine.execute("match (n:Entity) delete n");
LOG.info(">>>> CYPHER TOOK: " + (System.currentTimeMillis() - time) + " m-secs");
tx.success();
} catch (Throwable t) {
LOG.error(t);
t.printStackTrace();
} finally {
cdl.countDown();
}
}
});
} else {
es.execute(new Runnable() {
@Override
public void run() {
try (Transaction tx = graphDb.beginTx()) {
long time = System.currentTimeMillis();
ExecutionResult result = engine.execute("match (n:Entity) return n.name");
LOG.info(">>>> CYPHER TOOK: " + (System.currentTimeMillis() - time) + " m-secs");
int count = 0;
time = System.currentTimeMillis();
for (Map<String, Object> row : result) {
assert ((String) row.get("n.name") != null);
count++;
}
LOG.info(">>>> GETTING RESULTS TOOK: " + (System.currentTimeMillis() - time) + " m-secs");
tx.success();
LOG.info(">>>> CYPHER RETURNED ROWS: " + count);
} catch (Throwable t) {
LOG.error(t);
t.printStackTrace();
} finally {
cdl.countDown();
}
}
});
}
}
cdl.await();
es.shutdown();
}
private static void createIndex(String label, String propertyName, GraphDatabaseService graphDb) {
IndexDefinition indexDefinition;
try (Transaction tx = graphDb.beginTx()) {
Schema schema = graphDb.schema();
indexDefinition = schema.indexFor(DynamicLabel.label(label)).on(propertyName).create();
tx.success();
}
try (Transaction tx = graphDb.beginTx()) {
Schema schema = graphDb.schema();
schema.awaitIndexOnline(indexDefinition, 10, TimeUnit.SECONDS);
tx.success();
}
}
private static void registerShutdownHook(final GraphDatabaseService graphDb) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
LOG.info("### GRAPHDB SHUTDOWNHOOK INVOKED !!!");
graphDb.shutdown();
}
});
}
}
回答1:
It seems there is currently not a better option. This issue was reported here in 2012: https://github.com/neo4j/neo4j/issues/37
来源:https://stackoverflow.com/questions/21385630/how-to-handle-notfoundexception-elegantly-in-neo4j-2-0