cancel
Showing results for 
Search instead for 
Did you mean: 

behaviour threading doesn't make sense

aweber1nj
Champ in-the-making
Champ in-the-making
I am baffled as to how to synchronize methods in a behaviour class.  I have read everything I can find about ejb and spring beans.  The EJB3 annotations don't work (or aren't obeyed).  And from what I can tell, I should be able to use traditional "synchronized" blocks within the class, but they keep halting everything.

I'm using some Alfresco code I found to check if the parent folder has the "Countable" aspect.  If not, add it and set the counter to 1.  If it does, try and increment the counter using nodeService.setProperty().

The main problem is when you upload multiple objects at once, a second (or third) thread seems to start before the first one completes (log4j shows the thread names).  Alfresco doesn't like that, because the write/update of the parent folder (which would be the same parent for all the remaining objects being uploaded and updated at this time) causes a block/deadlock situation as subsequent threads try to read the parent's properties (and they, too, may update the folder).

So basically the code works great if you only upload one document at a time.  I can't find a locking (not transaction) construct to put anywhere that'll let me synchronize access to the parent, and keep things moving at all.

I'm sure this kind of thing has been addressed.  Can someone please help me figure out how to synchronize this code?  It's the only thing I can think of that Alfresco is complaining about right now…and I'm a little surprised it is at all.  It's not like I'm testing with 10 or 100 concurrent users.  I'm just testing an import of 2 documents at a time and I am not allowed to put any concurrency constructs in the code! Smiley Sad
6 REPLIES 6

mrogers
Star Contributor
Star Contributor
Alfresco is not an EJB implementation so its hardly surprising that the annotations don't work.

In general you shouldn't try to synchronize in a behaviour,  that will cause problems if not done carefully and won't work in a cluster. 
Alfresco uses "optimistic" locking so the approach is attempt to update and if there is conflict, fail fast and retry.

Attempting to put a count on a parent folder is going to be problematic, do you really need that?  And how accurate does it have to be?

aweber1nj
Champ in-the-making
Champ in-the-making
The counter is being used to keep child names (all documents in this case) unique.  Unfortunately, my client has a strange need to have the documents re-named according to a strict convention…but this frequently results in duplicate names.  Thus, I was trying to use the Countable Aspect on the parent folder to keep a counter and pre-pend the integer to the name as it's reformulated.

There are obvious, other inconveniences using that approach (such as when you delete documents, the sequence will be "broken"), but technically I'd need it to be fairly accurate (to answer your question).

If I cached the value temporarily, is there some way to defer the update to the parent?  Like after some timeout of inactivity for that behaviour class, it could go do some work on to flush the cached values back to the repository.  I kind of doubt this, though maybe a "timed action" that picks up the same cache and does its work?  I assume spawning a background-worker thread from the behaviour class is a bad idea?

The countable aspect (and counter property) is really what would "fit", but if it can't work technologically, then I guess I have to go another route.  Maybe go directly to a custom/separate db table and use JDBC?  (I noticed there wasn't a service to help get a JDBC connection/session!)  But even doing that, and the relatively quick lookup and update of the DB table should be "synchronized"…but as I said, none of the synchronization constructs seem to work as designed.

mrogers
Star Contributor
Star Contributor
If you retry on conflict with the out of the box retrying transaction handler, then the following will work.    increment and store the "last number" on the parent node.   And on create create the assoc  with last number. 

Its not suitable for larger volumes but is a simple solution.   For larger volumes you probably need to change your approach to use the job lock service and allocate sequences.

Or another solution is to use the attribute service for your counter.

aweber1nj
Champ in-the-making
Champ in-the-making
Very much appreciate the advice.  I will look into both "alternate" approaches you mention.

Again, thanks for taking the time to reply.

-AJ

aweber1nj
Champ in-the-making
Champ in-the-making
So I switched to the AttributeService to store the "counter".
            String[] keys = new String[2];
            keys[0] = clName;
            keys[1] = parentID;

            // check if the attribute exists…
            Integer curCounter = (Integer) attributeService.getAttribute(keys);
            if (curCounter == null) {
                curCounter = 0;
                logger.debug("No value stored yet for attribute: " + parentID);
            }
            else {
                logger.debug("Got value from attribute: " + curCounter);
            }

            //increment the counter and put it back in the database/repository
            resultValue = curCounter + 1;
            attributeService.setAttribute(new Integer(resultValue), keys);
I tried importing one document.  Worked fine.  (Must have created the attribute in "setAttribute" as the javadocs indicate.)

I tried importing another (single) document.  Worked fine.  Got the existing attribute, incremented it, etc.

Then I tried dragging two docs in at once.  The exception thrown seems to indicate that Alfresco tried to create the attribute again, because it threw a DB "unique constraint" error?  It should've (obviously) just updated the existing attribute again (twice in a row).
org.springframework.dao.DuplicateKeyException: 
### Error updating database.  Cause: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "idx_alf_propv_act"
  Detail: Key (actual_type_id, long_value)=(6, 3) already exists.
### The error may involve alfresco.propval.insert.insert_PropertyValue-Inline
### The error occurred while setting parameters
### Cause: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "idx_alf_propv_act"
  Detail: Key (actual_type_id, long_value)=(6, 3) already exists.
; SQL []; ERROR: duplicate key value violates unique constraint "idx_alf_propv_act"
  Detail: Key (actual_type_id, long_value)=(6, 3) already exists.; nested exception is org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "idx_alf_propv_act"
  Detail: Key (actual_type_id, long_value)=(6, 3) already exists.
   at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:241)
   at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
   at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:71)
   at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:346)
   at $Proxy6.insert(Unknown Source)
   at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:231)
   at org.alfresco.repo.domain.propval.ibatis.PropertyValueDAOImpl.createPropertyValue(PropertyValueDAOImpl.java:440)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl$PropertyValueCallbackDAO.createValue(AbstractPropertyValueDAOImpl.java:762)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl$PropertyValueCallbackDAO.createValue(AbstractPropertyValueDAOImpl.java:712)
   at org.alfresco.repo.cache.lookup.EntityLookupCache.getOrCreateByValue(EntityLookupCache.java:469)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.getOrCreatePropertyValue(AbstractPropertyValueDAOImpl.java:705)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.createPropertyImpl(AbstractPropertyValueDAOImpl.java:998)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.access$700(AbstractPropertyValueDAOImpl.java:54)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl$PropertyCallbackDAO.createValue(AbstractPropertyValueDAOImpl.java:846)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl$PropertyCallbackDAO.createValue(AbstractPropertyValueDAOImpl.java:840)
   at org.alfresco.repo.cache.lookup.EntityLookupCache.getOrCreateByValue(EntityLookupCache.java:447)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.createProperty(AbstractPropertyValueDAOImpl.java:823)
   at org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.updatePropertyUniqueContext(AbstractPropertyValueDAOImpl.java:1233)
   at org.alfresco.repo.attributes.AttributeServiceImpl.setAttribute(AttributeServiceImpl.java:177)
Thoughts?

mrogers
Star Contributor
Star Contributor
Your code should be in a retrying transaction helper.  The conflicting update will retry - both docs go in.
Getting started

Tags


Find what you came for

We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.