java transactions event-handling neo4j versioning

TransactionEventHandler da javax.transaction.SystemException en Node.setProperty()



transactions event-handling (4)

Estoy tratando de implementar un TransactionEventHandler como el usado en neo4j-versioning para crear una base de datos Neo4j versionada y con estilo de máquina de tiempo, ahora usando Neo4j 2.x. Falla con la siguiente traza de pila infinita:

javax.transaction.SystemException: TM has encountered some problem, please perform necessary action (tx recovery/restart) at org.neo4j.kernel.impl.transaction.TxManager.assertTmOk(TxManager.java:349) at org.neo4j.kernel.impl.transaction.TxManager.setRollbackOnly(TxManager.java:758) at org.neo4j.kernel.TransactionEventHandlers.beforeCompletion(TransactionEventHandlers.java:120) at org.neo4j.kernel.impl.core.TransactionEventsSyncHook.beforeCompletion(TransactionEventsSyncHook.java:68) at org.neo4j.kernel.impl.transaction.TransactionImpl.doBeforeCompletion(TransactionImpl.java:368) at org.neo4j.kernel.impl.transaction.TxManager.commit(TxManager.java:398) at org.neo4j.kernel.impl.core.IsolatedTransactionTokenCreator.getOrCreate(IsolatedTransactionTokenCreator.java:61) at org.neo4j.kernel.impl.core.TokenHolder.createToken(TokenHolder.java:114) at org.neo4j.kernel.impl.core.TokenHolder.getOrCreateId(TokenHolder.java:102) at org.neo4j.kernel.impl.api.store.DiskLayer.propertyKeyGetOrCreateForName(DiskLayer.java:367) at org.neo4j.kernel.impl.api.store.CacheLayer.propertyKeyGetOrCreateForName(CacheLayer.java:370) at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.propertyKeyGetOrCreateForName(StateHandlingStatementOperations.java:939) at org.neo4j.kernel.impl.api.DataIntegrityValidatingStatementOperations.propertyKeyGetOrCreateForName(DataIntegrityValidatingStatementOperations.java:67) at org.neo4j.kernel.impl.api.OperationsFacade.propertyKeyGetOrCreateForName(OperationsFacade.java:397) at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:205) ...

Esta es mi prueba:

@Test public void test() { GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node referenceNode = null; try (Transaction transaction = graphDb.beginTx()) { referenceNode = graphDb.createNode(); transaction.success(); } VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode); graphDb.registerTransactionEventHandler(versioningTransactionEventHandler); try (Transaction tx = graphDb.beginTx()) { Node node = graphDb.createNode(); tx.success(); } graphDb.shutdown(); }

Este es el VersioningTransactionEventHandler :

public class VersioningTransactionEventHandler implements TransactionEventHandler<Object> { private final Node versionDataNode; public VersioningTransactionEventHandler(Node versionDataNode) { this.versionDataNode = versionDataNode; } @Override public Object beforeCommit(TransactionData data) throws Exception { versionDataNode.setProperty("foo", "bar"); // <- this causes the error return null; } @Override public void afterCommit(TransactionData data, Object state) { } @Override public void afterRollback(TransactionData data, Object state) { } }

Estoy usando org.neo4j.neo4j-2.0.1 y org.neo4j.neo4j-kernel-2.0.1 en mi aplicación.

¿Por qué el setProperty() causa este error? ¿Cómo puedo arreglarlo? Cualquier pista sobre lo que puede estar mal aquí es muy apreciada.

Actualizar

Como Michael Hunger sugirió que hice un setProperty() antes de pasar el nodo, pero ahora la prueba se cuelga silenciosamente para el infinito y no pasa nada. No importa qué par clave-valor de propiedad se establezca en el nodo:

... referenceNode = graphDb.createNode(); referenceNode.setProperty("foo", "bar"); // <- results in hang referenceNode.setProperty("herp", "derp"); // <- results in hang also ...

Todavía hay alguna pista? Solo quiero manipular un nodo mientras está dentro del controlador de eventos de transacción como se hizo en la versión 1.9 , pero Neo4j 2.x no tiene el GraphDatabaseService#getReferenceNode() que se pasa en el constructor allí.


Esto parece ser un error, causado por la creación sobre la marcha del token interno para el "nombre" de la propiedad.

¿Puedes intentar usar ese nombre de propiedad antes para que el token se cree antes?

Acabo de probarlo, si lo haces

@Test public void test() { GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node referenceNode = null; try (Transaction transaction = graphDb.beginTx()) { referenceNode = graphDb.createNode(); // use the "foo" property-name once before referenceNode.setProperty("foo", "bar"); transaction.success(); } VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode); graphDb.registerTransactionEventHandler(versioningTransactionEventHandler); try (Transaction tx = graphDb.beginTx()) { Node node = graphDb.createNode(); tx.success(); } graphDb.shutdown(); }


En primer lugar, parece que desea llamar a incrementTransactionVersion () en el método afterCommit () de su TransactionEventHandler. De esta forma, sabrá que la transacción se realizó correctamente antes de incrementar la versión. Supongo que la circularidad está causada por el hecho de que el incremento de la propiedad de versión de la versión del nodo causa otra transacción. Para solucionar esto, puede consultar TransactionData para ver si la transacción actual fue causada por el cambio al nodo de la versión. Esto no es tan sencillo como podría ser, pero aquí hay algunos pseudo-Java (ni siquiera he probado que compila) que ilustra cómo se podría hacer esto:

Iterable<PropertyEntry<Node>> ii = txData.assignedNodeProperties(); boolean doIncrementVersion = false; for (PropertyEntry<Node> pEntry : txData.assignedNodeProperties()) { if (pEntry.key().equals("transactionVersion") { doIncrementVersion = true; break; } } if (doIncrementVersion) { incrementTransactionVersion(); }

¡Espero que esto ayude!


Encontré una solución alternativa al último problema cuando llamé a setProperty() en beforeCommit() . Parece que solo ocurre en transacciones que no son de escritura (pensé que beforeCommit() se llamaba beforeCommit() solo en operaciones de escritura )?

Entonces, si compruebo que antes de hacer la configuración de la propiedad, el ahorcamiento desaparece:

... @Override public Object beforeCommit(TransactionData data) throws Exception { if (containsWriteChanges(data)) { versionDataNode.setProperty("foo", "bar"); } return null; } private boolean containsWriteChanges(TransactionData data) { return data.assignedNodeProperties().iterator().hasNext() || data.assignedRelationshipProperties().iterator().hasNext() || data.createdNodes().iterator().hasNext() || data.createdRelationships().iterator().hasNext() || data.deletedNodes().iterator().hasNext() || data.deletedRelationships().iterator().hasNext() || data.removedNodeProperties().iterator().hasNext() || data.removedRelationshipProperties().iterator().hasNext(); } ...


Logré deshacerme del error de error circular al agregar una línea al código de inicialización que crea el nodo de versión en la prueba:

@BeforeClass public static void setup() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node versionNode = null; try (Transaction tx = db.beginTx()) { versionNode = db.createNode(DynamicLabel.label("__TransactionVersion__")); versionNode.setProperty("transactionVersion", 0L); // <--- tx.success(); } transactionEventHandler = new MyTransactionEventHandler(versionNode); db.registerTransactionEventHandler(transactionEventHandler); }

Establecer la propiedad después de crear el nodo parece arreglarlo. Por lo tanto, parece que el error se debe a que se refiere a una transactionVersion "token" desconocida dentro del controlador de eventos de transacción en el método incrementVersionNumber() , que causa el error.

Aparentemente, esto es un error en Neo4j.