users@ejb-spec.java.net

[ejb-spec users] [jsr345-experts] Re: Transaction timeout

From: David Blevins <david.blevins_at_gmail.com>
Date: Mon, 12 Dec 2011 20:51:36 -0800

On Dec 12, 2011, at 2:58 AM, Carlo de Wolf wrote:

> I've raised http://java.net/jira/browse/EJB_SPEC-46 using the annotation you provided.
>
> Let's dig into the edge case:
>
> @Stateless class FooBean {
> @EJB OtherBean other;
>
> @TransactionAttribute(NEVER)
> @TransactionTimeout(30)
> public void doSomething() {
> other.doSomethingElse();
> }
> }
>
> @Stateless class OtherBean {
> @TransactionAttribute(REQUIRED) // REQUIRES_NEW
> public void doSomethingElse() {
> assert currentTransactionTimeout() == 30;
> }
> }
>
> Does it blend?
>
> I would say yes. The transaction timeout is set on the current thread before FooBean.doSomething.

I was imaging something plain and declared and deterministic. If the timeout is not specified explicitly in annotation or xml on the method that starts the transaction (or its class for class-level defaults) then the timeout defaults to whatever behavior that vendor had prior to EJB 3.2. Consistent to how @StatefulTimeout and @AccessTimeout behave and fit gently ontop of what vendors were doing already in a way that doesn't prevent them from continuing to do it.


Reading between the lines I think I see what you're getting at though; that the callers of beans (not the beans themselves) should be able to make these kinds of decisions. There might be room for that in general if we can find good semantics for it, but I wouldn't want to see us quietly change the semantics of @TransactionTimeout to be wildly different than @StatefulTimeout or @AccessTimeout where the caller does not get to decide the timeouts for their usage of the bean (the bean decides). I think quietly diverging here would actually damage our ability to cleanly do caller-driven metadata in the future (as opposed to callee-driven as we have now).

It's a good idea you're onto. There's some real vision there I've come to appreciate. Not sure what JSR-308 will offer in this regard, but if we had the ability to annotate method calls (as opposed to the method itself) and some clean way to get that data at runtime, we could do some really simple and powerful caller-driven declarative programming.

Using your example, imagine how cool this would be:

    @Stateless class FooBean {
      @EJB OtherBean other;
    
      @TransactionAttribute(NEVER)
      public void doSomething() {

         @TransactionTimeout(30)
         @TransactionAttribute(REQUIRED)
         other.doSomethingElse();
      }
    }
    
    @Stateless class OtherBean {
      public void doSomethingElse() {
         assert currentTransactionTimeout() == 30;
      }
    }

If that day comes, we'll be really glad we don't have a legacy mess to clean up.

Not sure how you'd do that at the VM level. Maybe similar to a closure, fabricate a method call around the other.doSomethingElse() call and move the annotations to that method. Then you just need some way expose that method object at runtime to interested parties (in this situation, the container). Maybe you don't even need special semantics, the container could just see this fabricated method as a plain method call made on the proxy and do what it does anyway.

Then there are effectively no "EJBs" anymore. There are effectively only services you use on plain objects when and were you want as determined by the code that uses those objects, not the objects themselves. People write plain code, others can use it in an "enterprisey" way if they like when they like. No more component fixation. Would change the way we do pretty much everything. Imagine this:

    public void doSomething() {
        
        if (thisMightBeLong) {
            @TransactionTimeout(10, TimeUnit.MINUTES)
            @TransactionAttribute(REQUIRED)
            other.doSomethingElse();
        } else {
            @TransactionTimeout(30)
            @TransactionAttribute(REQUIRED)
            other.doSomethingElse();
        }
    }

Got off on a tangent :) Anyway, I see the vision and like it. Not sure we have the tools to do it now, but boy will it be cool when we do.


-David


> On 12/09/2011 04:29 PM, David Blevins wrote:
>> It's a good concept.
>>
>> The fact that many @TransactionAttributes can effectively affect a single transaction seems to lead me to the conclusion that the timeout should be a different annotation. Otherwise I suspect there will be several frustrated people who can't figure out why the timeout doesn't work when they put it on SUPPORTS or worse REQUIRED in situations where that particular method didn't start the transaction.
>>
>> Perhaps @TransactionTimeout to be consistent with @StatefulTimeout and @AccessTimeout. We could have put the stateful timeout data on @Stateful. As well we could have put the access timeout info on @Lock. We didn't, so my vote would be for consistency.
>>
>> @Target({METHOD, TYPE})
>> @Retention(RUNTIME)
>> public @interface TransactionTimeout {
>> long timeout() default -1L;
>> TimeUnit unit() default TimeUnit.SECONDS;
>> }
>>
>>
>> What I wonder is that it clearly only would work 100% of the time on REQUIRES_NEW and sometimes on REQUIRED. We'll definitely have to document that, but I wonder if we might want to be a bit strict about it.
>>
>> Something like this is a little loose, but workable:
>>
>> @Stateless
>> @TransactionTimeout(30)
>> public class FooBean {
>>
>> @TransactionAttribute(REQUIRES_NEW)
>> public void doSomething() {}
>>
>> @TransactionAttribute(NEVER)
>> public void doSomething() {}
>> }
>>
>> This is flat out wrong:
>>
>> @Stateless
>> public class FooBean {
>>
>> @TransactionAttribute(REQUIRES_NEW)
>> @TransactionTimeout(30)
>> public void doSomething() {}
>>
>> @TransactionAttribute(NEVER)
>> @TransactionTimeout(30)
>> public void doSomething() {}
>> }
>>
>> Not sure what tact is the best to discourage that blatant misuse.
>>
>>
>> -David
>>
>>
>> On Dec 9, 2011, at 3:35 AM, Carlo de Wolf wrote:
>>
>>> Given the functionality of a transaction manager to set the transaction timeout [1]. I would like to expose this feature in a spec defined manner.
>>>
>>> How about the following annotation:
>>>
>>> @Target({METHOD, TYPE})
>>> @Retention(RUNTIME)
>>> public @interface TransactionAttribute {
>>> TransactionAttributeType value() default TransactionAttributeType.REQUIRED;
>>> long timeout() default -1L;
>>> TimeUnit unit() default TimeUnit.SECONDS;
>>> }
>>>
>>> Where -1 means unspecified.
>>>
>>> And the following descriptor fragment:
>>>
>>> <ejb-jar version="3.2">
>>> <assembly-descriptor>
>>> <container-transaction>
>>> <method>
>>> <ejb-name>A</ejb-name>
>>> <method-name>*</method-name>
>>> </method>
>>> <trans-attribute>RequiresNew</trans-attribute>
>>> <trans-timeout>
>>> <timeout>10</timeout>
>>> <unit>Seconds</unit>
>>> </trans-timeout>
>>> </container-transaction>
>>> </assembly-descriptor>
>>> </ejb-jar>
>>>
>>> What do you guys think?
>>>
>>> Carlo
>>>
>>> [1] http://docs.oracle.com/javaee/6/api/javax/transaction/TransactionManager.html#setTransactionTimeout%28int%29
>>>
>>> PS. Since people are going to dig, we discussed changing transaction attributes on Oct 17th 2007.
>