I mentioned last week that I'd follow up with some pointers to our LockService implementation once it was done in Rave (see earlier messages in this thread for more details) -- here are those pointers:
++ LockService interface/implementation http://svn.apache.org/viewvc/incubator/rave/trunk/rave-commons/src/main/java/org/apache/rave/service/LockService.java?view=markup http://svn.apache.org/viewvc/incubator/rave/trunk/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultLockService.java?view=markup ++ Unit tests showing demo usage http://svn.apache.org/viewvc/incubator/rave/trunk/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultLockServiceTest.java?view=markup ++ An annotation and aspect for annotating methods that should be synchronize using this strategy (which we'll probably try to contribute back to Spring at some point): http://svn.apache.org/viewvc/incubator/rave/trunk/rave-commons/src/main/java/org/apache/rave/synchronization/annotation/Synchronized.java?view=markup http://svn.apache.org/viewvc/incubator/rave/trunk/rave-commons/src/main/java/org/apache/rave/synchronization/SynchronizingAspect.java?view=markup ++ Sample usage (search for @Synchronized): http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal/src/main/java/org/apache/rave/portal/service/impl/DefaultRegionWidgetService.java?view=markup >-----Original Message----- >From: Ciancetta, Jesse E. >Sent: Wednesday, September 14, 2011 11:11 AM >To: shindig; [email protected] >Subject: RE: osapi services and asychronous calls > >>We¹ve implemented our own userprefs osapi service (yes, we dumped the >>whole >>idea of sitting on top of appdata). As I understand it the calls to our >>service will be asynchronous. Recently Dan Dumont made some changes to >>the >>common container layer to support a callback for GET_PREFERENCES so that >>we >>didn¹t try to render the gadget before the GET had completed. >> >>I need some help understanding the SET side of things. A gadget could call >>SET for each userpref in rapid succession. It¹s possible 2 of these could >>be handled simultaneously at the service layer (it¹s not only possible, we >>are hitting it EVERYTIME with the horoscope gadget). Thus it¹s possible we >>could get a constraint violation on the database side (we are creating a >>master record that references the userpref values). Two values could try to >>create the master record simultaneously. Even if we flattened that out and >>didn¹t do the master record, the gadget COULD still set the same value twice >>in rapid succession and we¹d run into the same problem. >> >>So, it the right solution to return a 409 (conflict) from the service and >>have our container userprefs layer retry? > >Huh -- while reading through your question and thinking through how I might >solve it, I realized that I have this same exact problem in at least two >places! :- >) > >I think the way I'm planning to solve it is to introduce some intelligent >synchronization. So within Rave we have a method in our >RegionWidgetService layer to save a RegionWidgetPreference which looks >something like this: > > public RegionWidgetPreference saveRegionWidgetPreference(long >regionWidgetId, RegionWidgetPreference preference) { > //fetch the region widget from the database > //pull the preferences out of it and add or update the >preference we were passed > //save the region widget with its updated preferences back to >the database > } > >So I think my solution is going to look something like this: > > public RegionWidgetPreference saveRegionWidgetPreference(long >regionWidgetId, RegionWidgetPreference preference) { > //fetch a lock object for this RegionWidget instance using the >regionWidgetId as a key (I'm looking at the java.util.concurrent.locks.Lock >interface) > //lock on the lock object > //fetch the region widget from the database > //pull the preferences out of it and add or update the >preference we were passed > //save the region widget with its updated preferences back to >the database > //release the lock on the lock object > } > >Fetching/releasing the lock will be delegated off to a generic LockService >which will have synchronized methods for fetching/releasing the locks -- this >way the only synchronization overhead that *all* threads will incur is >fetching/releasing the locks -- and the only time two (or more) threads will >need to wait for each others database operations is if they are trying to set >preferences for the same RegionWidget instance. > >I'll post a follow up when I have this implemented in Rave with pointers to the >implementation for reference. > >> And doesn¹t this same issue exist >>from gadgets who are calling appdata (or any osapi write service for that >>matter)? Should our services be trying to prevent this or only reporting >>errors? Is there some shindig construct that helps with this? > >I think all the code in Shindig that manipulates persistent data ends up doing >so by delegating the actual data manipulation work off to an implementation >of one of their SPI interfaces -- so I think it would be up to the implementer >of >those interfaces to do something like what I've outlined above. > >>Your help is appreciated. >> >>Thanks, >>Doug
