10 Replies Latest reply: Jun 21, 2011 5:36 PM by EJP RSS

    Providing synchronous method execution through an interface..

    870161
      I have a situation where multiple threads may come in placing an order that can only be executed once. I have no real object to synchronize on, besides a String identifier which may or may not be the same reference. To add to the requirement, there are different applications that can share this order placement code, some which may be distributed, some which may not be.

      I want to avoid file locking or database locking, specifically because file locking is frowned upon and database locking isnt entirely elegant with breaking apart transactions and catching insert violation constraints. I'd much prefer to have a reliable synchronization implementation that I can inject through spring to handle single or multiple jvms. Here is the architecture I am considering:

      This is the interface used to execute methods through implementations of the ISynchronousMethod.
      public interface ISynchronousMethodExecutor {
      
           /**
            * Executes a method synchronously, using a String id as a mutex, regardless of its identity. 
            * The domain is used to prevent accidental collisions from other threads using the executor. 
            * 
            * @param domain
            * @param id
            * @param synchronousMethod
            * @return Object
            */
           public Object doSynchronousMethod(String domain, String id, ISynchronousMethod synchronousMethod);
            
      }
      public interface ISynchronousMethod {
      
           /**
            * Executes the method to be run synchronously.
            * 
            * @return Object
            */
           public Object execute();
            
      }
      I have two implementations in mind, one for a single jvm using regular synchronized semantics, and one for a distributed jvm. I wanted to primarily ask your opinions on the single jvm implementation:
      public class SynchronousMethodExecutorSynchronizedImpl implements ISynchronousMethodExecutor {
           
           // mutex map to provide string references
           private final Map<String, String> mutexMap = new MapMaker()
                .weakValues()
                .makeComputingMap(
                     new Function<String, String>() {
                     @Override
                     public String apply(String id) {
                          return id;
                     }
                });
      
           @Override
           public Object doSynchronousMethod(String domain, String id,     ISynchronousMethod synchronousMethod) {
                synchronized(mutexMap.get(domain + "." + id))
                {
                     return synchronousMethod.execute();
                } 
           }
      
      }
      The idea is to use a mutex map in case different String objects are passed in, they will resolve to the same lock. The computing map simply executes that function whenever the key is empty. Am I correct in using weakValues here? I want them to expire immediately after they are used.

      Finally here is the distributed implementation, using Hazelcast's distributed lock:
      public class SynchronousMethodExecutorDistributedImpl implements ISynchronousMethodExecutor {
      
           @Override
           public Object doSynchronousMethod(String domain, String id,     ISynchronousMethod synchronousMethod) {
                Lock lock = Hazelcast.getLock(domain + "." + id);
                lock.lock();
      
                try
                {
                     return synchronousMethod.execute();
                }
                finally
                {
                     lock.unlock();
                }   
           }
      
      }
      Finally, the code will be executed like this, with the implementation injected through Spring:
      public static void main(String[] a) {
                ISynchronousMethodExecutor executor = getFromSpringContext(); // injected usually
                executor.doSynchronousMethod("Test", "1", new ISynchronousMethod() {
                     @Override
                     public Object execute() {
                          System.out.println("This should only execute once");
                          return null;
                     }
                });
           }
      How does that look? Specifically the single jvm implementation?

      Edited by: user6481026 on Jun 20, 2011 4:50 PM

      Edited by: user6481026 on Jun 20, 2011 4:50 PM
        • 1. Re: Providing synchronous method execution through an interface..
          EJP
          I would let the database do it, no two ways about it. Databases are seriously good at this kind of thing, your stated objections notwithstanding. In fact I don't know what you mean by 'not elegant'.
          • 2. Re: Providing synchronous method execution through an interface..
            870161
            If I insert a record into the db symbolizing a lock, I have to do that outside of a transaction. Then I do the work of placing the order and saving the order details. If it fails, I have to start another transaction to remove the lock. If a duplicate request comes in, if say a user submits a form twice, his second request can come in and see that the row is inserted before the second transaction can remove the lock. His thread has no choice but to fail, or do some random retry logic. Its much easy to guarantee only one thread can come in for one order.
            • 3. Re: Providing synchronous method execution through an interface..
              EJP
              If I insert a record into the db symbolizing a lock, I have to do that outside of a transaction.
              I didn't say anything about inserting a record symbolizing a lock. I am referring to using the DBMS locking and transactions themselves.
              If a duplicate request comes in, if say a user submits a form twice
              ... then you associate a unique client-generated transaction sequence number with the transaction, and use INSERT IGNORE or whatever your local SQL dialect equivalent is to ignore the duplicates.

              The basic idea is to define the transactions so that they are idempotent.*

              Not to write your own locking and transaction handling.
              • 4. Re: Providing synchronous method execution through an interface..
                802316
                EJP wrote:
                I would let the database do it, no two ways about it. Databases are seriously good at this kind of thing, your stated objections notwithstanding. In fact I don't know what you mean by 'not elegant'.
                If you have multiple JVMs and a single database, this really is the only sensible answer. Databases have builtin support for locking and transactions.

                If you have a single JVM and multiple databases, then I would suggest locking in Java.

                If you really want to do locking in Java, I suggest you have one and only one JVM which accesses the database and every other JVM connects via that service.
                This is likely to be simpler and faster than attempting to maintain a distributed lock.
                • 5. Re: Providing synchronous method execution through an interface..
                  EJP
                  The OP has already mentioned 'single or multiple JVMs'.
                  • 6. Re: Providing synchronous method execution through an interface..
                    870161
                    I have run some unit tests and discovered that although the concurrency works correctly, there is a subtle memory leak in the single jvm implementation:
                         
                         // mutex map to provide string references
                         private final Map<String, String> mutexMap = new MapMaker()
                              .weakValues()
                              .makeComputingMap(
                                   new Function<String, String>() {
                                   @Override
                                   public String apply(String id) {
                                        return id;
                                   }
                              });
                    The problem with this computing map is that the key and value are the same object, which means the map will always have a strong reference to the key, and the values will never clear. The correct solution is:
                         
                         // mutex map to provide string references
                         private final Map<String, String> mutexMap = new MapMaker()
                              .weakValues()
                              .makeComputingMap(
                                   new Function<String, String>() {
                                   @Override
                                   public String apply(String id) {
                                        return new String(id);
                                   }
                              });
                    This works as desired.
                    • 7. Re: Providing synchronous method execution through an interface..
                      EJP
                      This works as desired in the single JVM case, which is entirely trivial, as a 'synchronized' block would have had the same effect, or something from java.util.concurrent. It hasn't got a snowball's chance in hell of scaling to the multi-JVM case.

                      Unlike my suggestion.
                      • 8. Re: Providing synchronous method execution through an interface..
                        870161
                        I already included a distributed implementation in my initial post which will certainly scale to multiple jvm architectures. Indeed its trivial for a single jvm, the benefit is I can swap the synchronization details out through spring configuration for different apps.
                        • 9. Re: Providing synchronous method execution through an interface..
                          870161
                          But just for the sake of discussion, if you need to charge a customer, how can you possibly do that in an elegant way with a database and still handle duplicate transactions? Its not like you can rollback the charge on the credit card if the transaction fails. If there is an elegant way to use the database to synchronize, I could just as easily plug that as an implementation to the interface above.
                          • 10. Re: Providing synchronous method execution through an interface..
                            EJP
                            I have answered that question above. It is called idempotency. Look it up. This is how the banks do it. No need to reinvent the wheel.