cancel
Showing results for 
Search instead for 
Did you mean: 

Counter action giving duplicate numbers

ovirtanen
Champ in-the-making
Champ in-the-making
I have a piece of code that uses a central space as the location of a counter aspect, then injects the next value to a given object as a sequence number, like so:

synchronized public void injectIdentifier(NodeRef nodeRef) {
   NodeRef rootSpace = locateRootSpace();
   Action countAction = serviceRegistry.getActionService().createAction("counter");
   serviceRegistry.getActionService().executeAction(countAction, rootSpace);
   Integer count = (Integer) nodeService.getProperty(arubaHome, ContentModel.PROP_COUNTER);
   count += 9999;
   nodeService.setProperty(nodeRef, MyModel.PROP_IDENTIFIER, count.toString());
}

Now, this seemed to be working perfectly, until it started returning duplicate numbers or possibly even jumped backwards a couple of times – not exactly sure which. The locateRootSpace method looks for the company home using lucene, then resolves by name a space directly under it. This code has since been replaced with a direct MySQL sequence query, which solved the manifested problem.

But I'm left wondering what could have caused this. Any ideas?
3 REPLIES 3

derek
Star Contributor
Star Contributor
Hi,
Even though the code is synchronized, it still cannot see data written by uncommitted transactions and might even not see data by committed transactions due to its caches.  In theory, the setProperty should fall out with ConcurrentModificationException (and trigger transaction retrying) if there are concurrent count transactions taking place.
What are your transaction boundaries w.r.t. this method?
Who is reading the counter value (how did you detect the behaviour)?
What does the action do?

Regards

ovirtanen
Champ in-the-making
Champ in-the-making
The code is usually called from an asynchronously executed action, which then wraps the actual code like so:

serviceRegistry.getTransactionService().getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Boolean>() { 
    @Override
    public Boolean execute() throws Throwable {
        // code called here
        return true;
    }
}, false, true);

Separate calls to the code above are in turn queued. The duplicate counter values ended up being committed to the repository (i.e. visible via the Alfresco web UI). Essentially this is part of a drop folder logic where an incoming ZIP file is extracted to a newly created space. The counter's value does not affect the destination space's name, since a GUID is used for that – it's just stored in a property field.

derek
Star Contributor
Star Contributor
Hi,

If you want your counter value to be consistent (i.e. behave predictably) then you need to get the next value using either the same transaction or a nested transaction.  Depending on how the "counter" action has been coded and configured, inconsistency could very well occur.

You could just stick your counter logic in a regular bean so that you get this pseudo code:

start new transaction (as you did in your last post)
call injectIdentifier(NodeRef nodeRef);
   Integer nextCount = call counterBean.nextCount();
      start new, non-propagating transaction IFF you don't mind holes in the counter sequence (I'd suggest this for better concurrency)
         Get the value from the root space, increment it, set it and return the new value
   Set the property on your nodeRef (the folder that you're unzipping to, I presume)

You can also use a new node (perhaps the folder or any node that has changed during the import) and call NodeService.getNodeStatus().getDbTxnId() to get a sequential number that will be unique for your import.

Regards