package er.neo4jadaptor.storage.neo4j; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; import er.extensions.foundation.ERXProperties; import er.neo4jadaptor.storage.neo4j.NodeSpaceManager.Space; /** * <p> * Provides temporary node instances. This class has been created due to performance requirements, as * nodes can be created only within transaction, which is costy to create each new node within one transaction. * This class creates a set of nodes within single transaction, which then can be passed to class' client * and if it runs out of nodes then will create new batch again in one transaction. * </p> * * <p> * Batches are created at various size, growing exponentially * </p> * * @author Jedrzej Sobanski */ public class TemporaryNodePool { private static final int INITIAL_POOL_SIZE = ERXProperties.intForKeyWithDefault("TemporaryNodePool.initialPoolSize", 4); private static final int MAX_POOL_SIZE = ERXProperties.intForKeyWithDefault("TemporaryNodePool.maxPoolSize", 256); private final GraphDatabaseService db; private final NodeSpaceManager spaceManager; private List<Node> pool; Integer previousPoolSize = null; public TemporaryNodePool(GraphDatabaseService db, NodeSpaceManager spaceManager) { this.db = db; this.spaceManager = spaceManager; } private void createPool(int poolSize) { List<Node> nodes = new ArrayList<>(); Transaction tx = db.beginTx(); try { for (int i=0; i<poolSize; i++) { Node node = db.createNode(); spaceManager.setIsTemporary(node); nodes.add(node); } tx.success(); } finally { tx.finish(); } pool = nodes; } public Node getNextTemporaryNode() { if (pool == null || pool.isEmpty()) { int poolSize = previousPoolSize != null ? 2*previousPoolSize : INITIAL_POOL_SIZE; poolSize = Math.min(poolSize, MAX_POOL_SIZE); poolSize = Math.min(poolSize, INITIAL_POOL_SIZE); createPool(poolSize); previousPoolSize = poolSize; } if (pool.isEmpty()) { throw new IllegalStateException(); } Node ret = pool.get(0); pool.remove(ret); return ret; } public void cleanup() { Node root = db.getReferenceNode(); Iterator<Relationship> it = root.getRelationships(Space.TEMPORARY, Direction.OUTGOING).iterator(); while (it.hasNext()) { Relationship r = it.next(); Node endNode = r.getEndNode(); r.delete(); endNode.delete(); } pool = null; previousPoolSize = null; } }