[Neo4j] REST, Transactions and Uniqueness

Linan Wang tali.wang at gmail.com
Fri Sep 23 01:57:14 CEST 2011


Hi,
i had the issue few days ago and thanks to McKinley I got a workable
solution. i think the best way to do is through unmanaged extension.
the overhead of multiple REST calls could make the matter more
complex.

here is part of my ObjectFactory class. in my situation it's an
external id needs to be uniq. feel free to correct my codes :) the
performance is not ideal though. on my imac I got around 50 insertions
per sec. the bottle neck is not memory.

	public T get(long externalId)
	{
		// try asynchronized read first;
		Index<Node> idx = getDefaultNodeIndex();
		
		IndexHits<Node> h = idx.get(IDX_KEY_EXTERNAL_ID, externalId);
		Node n = h.getSingle();
		h.close();
		
		if(n != null)
			return wrap(n);
		
		// if not found, try synchronized version;
		return null;
	}
	
	public T getOrCreate(long externalId)
	{
		
		T ret = get( externalId );
		if(ret != null)
			return ret;
		
		// if not found, try synchronized version;
		return synchronizedGetOrCreate(externalId);
	}
	
	private synchronized T synchronizedGetOrCreate(long externalId)
	{
                // Just in case!
		T ret = get( externalId );
		if(ret != null)
			return ret;

		Index<Node> idx = getDefaultNodeIndex();
		Node n = null;
		
		Transaction tx = db.beginTx();
		
		try{
			n = db.createNode();
			
			// set property
			n.setProperty(AbstractObject.EXTERNAL_ID_KEY, externalId);
			
			// add to default index;
			idx.add(n, IDX_KEY_EXTERNAL_ID, externalId);
			
			tx.success();
		}catch(Exception e){
			tx.failure();
		}finally{
			tx.finish();
		}
		
		return wrap(n);
	}

On Thu, Sep 22, 2011 at 9:33 PM, twooster <twooster at gmail.com> wrote:
> Hi,
>
> I've seen this come up a number of times in various forms on the mailing
> list, but -- while there are some scattered answers here and there -- there
> doesn't seem to be much of a definitive solution. The problem I'm looking to
> solve is basic uniqueness (e.g. for creating an account -- unique username
> or email address); to make matters more complicated, I'd like to do this
> over the REST interface.
>
> In the a non-REST use of Neo4j, it sounds like I could achieve this by doing
> the following:
>
> 1. Begin a transaction
> 2. Acquire a write lock on a UserRef supernode, say by attempting to delete
> the property __WRITE_LOCK__ (which will fail if the property doesn't exist,
> say)
> 3. Check if username is in users index
> 4. If it is, cancel transaction and fail
> 5. Otherwise, add a User node, relate it back to the UserRef node, and add
> it to the index
> 6. Release lock on UserRef supernode by re-adding __WRITE_LOCK__ property
> 7. Commit transaction
>
> First -- does this sound roughly sound? Assuming that any operation that
> ever touches the index agrees to first "acquire" a write lock in this
> manner, are there any tricky concurrency issues (maybe out-of-sync indexes,
> or the index.get(key,v).size() function being "almost correct") that I'm
> missing?
>
> To complicate matters, I'd _like_ to do this with REST, not the least reason
> because my main project code is in Python, and neo4j.py seems to be
> relatively unmaintained compared to the REST client. It's my understanding
> that transactions are handled using the batch interface. The only way to
> make the transaction fail is if any given operation returns a non-2xx status
> code. Thus, the 'if value is found in an index' thing is somewhat difficult
> to implement given the basic REST primitives.
>
> After cozying up with the code for a number of hours last night, my first
> real foray into Java programming, it appears that I can achieve the
> behaviour I want by introducing a REST plugin (an unmanaged extension would
> be cleaner, but the big warning and limited documentation dissuaded me) that
> throws an exception if a value _is_ found in a given index (which will cause
> the plugin invoker to return a 4xx response). Now my workflow over REST
> looks like this:
>
> 1. Begin transaction (e.g., start a batch request to the server), issuing
> the following commands:
> 2. Attempt to delete __WRITE_LOCK__ from UserRef node (will 4xx if property
> is non-existent)
> 3. POST to extension, to check if username is in users index (4xx if it
> exists)
> 3a. Transaction will fail at this point if user is in index
> 4. Add a User node
> 5. Relate it back to the UserRef node
> 5. Add it to the index
> 6. Release lock on UserRef supernode by re-adding __WRITE_LOCK__ property
> 7. Finish HTTP request, done
>
> This seems to work, but, again, are there any blind-spots that I'm unaware
> of? How about if this goes to a HA cluster?
>
> Finally, somewhat related, are some concerns with the batch API
> back-reference capability. This appears to manifest itself as a blind
> string-replace of '{[id]}' in provided fields. This _seems_ like it could
> have some security/annoying bug concerns relating to user-provided data
> (local portion of email address includes the substring '{1}', for example,
> which is valid per the email spec). I currently don't see any way around
> this except to restrict user input. Any thoughts?
>
> Anyway, thanks for any comments and responses!
>
> -Tony Wooster
>
> --
> View this message in context: http://neo4j-community-discussions.438527.n3.nabble.com/REST-Transactions-and-Uniqueness-tp3360054p3360054.html
> Sent from the Neo4j Community Discussions mailing list archive at Nabble.com.
> _______________________________________________
> Neo4j mailing list
> User at lists.neo4j.org
> https://lists.neo4j.org/mailman/listinfo/user
>



-- 
Best regards

Linan Wang


More information about the User mailing list