Page 1 of 2 12 LastLast
Results 1 to 10 of 15

Thread: Moving a transaction to another thread

  1. #1
    Join Date
    Aug 2004
    Posts
    229

    Default Moving a transaction to another thread

    Short version: does Spring provide any easy way to move a transaction to another thread?

    Long version: Spring's transaction management is all great if you always stay on the same thread, but I need to jump around to different threads. I'm not talking about concurrent execution in the same transaction, I'm talking about actually moving processing of a particular transaction to another thread (thread A transfers the transaction to thread B after which point thread A no longer does anything with the transaction). The transaction will still only ever be accessed by a single thread at a time, but that single thread might change.
    I'm attempting to implement a "poor man's distributed transaction" in a very specific and special case, a case which does not require a full ACID guarantee. Basically, I have a remote service (via httpinvoker) sitting on the network. I want this service to have its own local transaction. Something like this (conceptually):
    Code:
      interface MyService {
        TransactionId startTransaction();
        void commitTransaction(TransactionId);
        void rollbackTransaction(TransactionId);
        void doYourThing(TransactionId, ...);
        void doAnotherThing(TransactionId, ...);
      }
    The client of this service (which will be accessing it remotely via http invoker) will start a transaction via its 'startTransaction' method and then pass the transaction id into its other methods. Both the client and server will be using Spring's transaction management internally, but they won't know this of each other. In the end, I'm going to synchronize MyService's transaction with the client's transaction using Spring's TransactionSynchronizationManager. I'd like MyService to be able to start a Spring transaction (local to its server) when 'startTransaction' is invoked, keep that local transaction around so that it survives remote method invocation boundaries, and then commit/rollback the local transaction when either 'commitTransaction(...)' or 'rollbackTransaction(...)' are invoked on MyService. Of course, when the service is invoked via HttpInvoker (and RMI is no better), each method invocation can happen on a different thread. Since Spring manages transactions on a per-thread basis, this can be a problem. If Spring provided some way for me to detach a transaction from the current thread and later reattach it to another thread, then I could easily find a way for MyService to handle this use case.

  2. #2
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    So you basically want a stateful session bean, is that right?

    Federico.

  3. #3
    Join Date
    Aug 2004
    Posts
    229

    Default

    Quote Originally Posted by fschroder View Post
    So you basically want a stateful session bean, is that right?
    In a sense, but I don't have the luxury of a full J2EE container, distributed transactions, and other related conveniences. This service will be running in a container provided by a third party over which I have little control. It will be running in another VM. The third party components happen to be based on Spring and happen to use Spring managed transactions. The contained services do export a SOAP API, but this API does not provide a way to perform several operations within a single transaction. I'm attempting to remedy this by slipping in my own remote interface and modifying the contained Spring config. I can't put use cases within this remoted interface that would encapsulate the whole transactions we need, as it will be invoked by use cases on the client (the client here happens to be another server - one over which we do have control). We cannot embed our own services or use cases into this 3rd party container, as it would defeat the reason we went with separating out the 3rd party container. So, I need some way to 1) extend the life of the 3rd party's transaction to survive remote invocation boundaries, and 2) synchronize it with the client's transaction. If #1 could work as I've described, then #2 would be easy.

  4. #4
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    I really don't know if what you need is possible, but I'd try to look for alternative solutions, starting with a multi transaction solution and compensating transactions if you need them.
    Can you get data back from that 3rd party server? Perhaps you can move an object back and forth to that server and let it carry the state. Eventually one of the server can perform a tx with all the information.

    Good luck,
    Federico.

  5. #5
    Join Date
    Aug 2004
    Posts
    229

    Default

    I've come up with a solution. It is not the most efficient solution, but it works for our purposes. I explored two possibilities. One is what I mention here, being able to move a transaction around. This is actually possible but requires code either very specific to Spring and the TransactionManager you are using or very specific to the JVM you are using. It basically requires one to move all thread locals from one thread to another. In essence, Spring is hardwired for one-transaction-per-physical-thread, and you are really swimming upstream if you want to go beyond that model.
    My second approach was to work with the system and not against it. Basically, I create a service that can spawn its own managed threads. When a transaction is requested by a client, a new thread is spawned, a transaction is started in that thread, and then the thread waits for commands from a queue. Any command received from the queue is executed within the context of the transaction that was started in thread. When the client indicates the transaction should be rolled back or committed, then the appropriate command is sent down the queue, the thread executes the commit/rollback, and the thread ends. This does require additional threads on the server side, but for our usage, it isn't a problem. The final step was applying an aspect for the service we export via http invoker so that method invocations are sent down the command queue representing the client's transaction.

  6. #6
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    That's an interesting solution. One question, how long do you keep those transactions in flight before committing them?

  7. #7
    Join Date
    Aug 2004
    Posts
    229

    Default

    I basically created a quick and dirty little "ServiceSession" interface. It's similar to this example:
    Code:
    public interface ServiceSession {
      String startSession(TransactionDefinition ...);
      void commitSession(String sessionId);
      void rollbackSession(String sessionId);
    }
    I export this from the server using Spring's http invoker stuff. Technically, it's up to the client to invoke 'startSession' when it needs to, record the session id, and pass that session id to any remote method invocation on the server side. Once startSession is invoked, a new thread is created on the server and stays alive until either commitSession or rollbackSession is invoked. However, I'm going to make this transparent to both client code and server side code (see below). I've got another interface available on the server side only that allows server side code to execute a Callable within a session's dedicated thread. It looks something like this:
    Code:
    public interface ServiceSessionExecutor {
      <T> T invokeInSession(String sessionId, Callable<T> invocation);
    }
    Right now, I'm in the process of implementing these things:
    • Session timeout - if no activity occurs against a session within a specified amount of time, the session's transaction will be rolled back and the session will be closed.
    • Client side aspect to automatically start and stop the service session when invoking a target remote service. This aspect is applied to the (client side) remote invocation proxy of the destination service (the one providing the actual service, not the ServiceSession service). When a method is invoked against the proxy, the aspect checks to see (using Spring API) if a transaction is active on the client side. If so, it checks to see if the client already has a session open on the server side for the current thread. If not, it calls 'startSession' and binds the session id to the current thread. It then registers a transaction synchronization on the client side that automatically invokes 'commitSession' when the client side transaction commits or 'rollbackSession' if the client side transaction rolls back. This is all done via Spring's TransactionSynchronizationManager.
    • A RemoteInvocationFactory (client side) that decorates a Spring http invoker invocation with the service session id bound to the thread.
    • A server side RemoteInvocationExecutor that checks to see if a service session id is present in the remote invocation and, if so, does something like this:
    Code:
    Object invoke(RemoteInvocation invocation, Object targetObject) throws ... {
      if(invocation has session id) {
        return sessionExecutor.invokeInSession(sessionId, new Callable<Object>() {
          public Object call() throws Exception {
            decoratedInvoker.invoke(invocation, targetObject);
          }
        });
      } else {
        decoratedInvoker.invoke(invocation, targetObject);
      }
    }
    With all that in place, all one has to do to make use of "service sessions" is configure things properly via Spring. The key here is that neither the client code or your server code need know anything about this whole service session thing, as it is now all handled via aspects and other Spring abstractions. On the client side, you use your remote service like your normally would, only now it participates (though not in a fully ACID compliant way) with the client's transaction. In other words, a poor man's distributed transaction. Your client code need know nothing of the service sessions things. If it uses Spring's transaction abstraction, then the remote service will participate. The client is also free to invoke the remote service outside of a transaction, in which case it will get default Spring behavior.

  8. #8
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    This is a lot of interesting work!
    One thing I don't get, the server actually has in independent transaction doesn't it? You send commands to it and they execute inside that -per client- tx, right?

  9. #9
    Join Date
    Aug 2004
    Posts
    229

    Default

    Quote Originally Posted by fschroder View Post
    This is a lot of interesting work!
    One thing I don't get, the server actually has in independent transaction doesn't it? You send commands to it and they execute inside that -per client- tx, right?
    Yes, basically what this does is allow a server side transaction to run parallel to the client side transaction, committing it when the client commits, or rolling it back when the client rolls back. It also allows multiple remote invocations from the client to all execute within the same server side transaction (which is normally not possible).

  10. #10
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    I guess you're not modifying the same data in the same database, otherwise it could cause some locking problems.

    Let me know how that goes once you fully implement it.

    Federico.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •