Hi,
marc fleury wrote:
>
> Man,
>
> heavy Tx stuff for a Sunday, I do love "Web-Txs" though and I believe few
> people *get* it.
>
> Ok I also have a 20 month toddler :) I am playing with her... any typo on
> the keyboard is my daughter :)
[discussing tx import]
> |I have a reason for doing this in the MI
> |instead of directly in the RMI: If the
> |transaction is never used, there is no need
> |to import it. With a real DTM, the import
> |includes expensive (in terms of latency)
> |network calls to register with a transaction
> |service. If we can avoid it, we may get
> |a significant performance gain, but we do
> |not know if we can avoid it until we are
> |in the interceptor chain.
>
> wait, I am confused. I wouldn't expect you to do it in the RMI NOR in the
> MI, but in the interceptor chain. I haven't actually looked at your code,
> but from what you explain I envision a "TxImported" flag in the MI. The MI
> is really a "message" an XML message of sort if you wish! it says "method,
> params, return object, tx, principal" I don't feel it should be aware of the
> surrounding "assembly line" logic. I guess it is just "packaging" but if
> you can factor that out then so much the better. MI remains a
> "serializable" object at heart, or I should say and externalizable object at
> heart. I was never a big fan of the MI/RMI distinction introduced for
> "portability", I mean the default layers can use MI...
The MI change is quite simple: A new property
for the TPC is added. A new constructor taking
a TPC instead of a Transaction is added. Method
MI.getTransaction() is changed so that if TX
is null and TPC is not null, the TPC Importer
is called to get a Transaction that the TX
property is set to and that is returned.
If the MI should be serializable, it could not
hold the Transaction TX attribute, as we do not
know if the particular TM delivers Transaction
instances that are serializable. Also MI holds
a reference to an EntityContext instance that
would also not be serializable.
I didn't really like to add this simple logic
to check for a null TX in MI.getTransaction(),
as the MI looks like a simple value holder.
The "excuse" that I found in my mind is that
the TCP is really just an alternative
representation of the TX. So in getTransaction(),
I look for the alternative representation if the
standard representation does not yet exist.
> |To put things into the JTA terminology:
> |The TPC Factory and Importer interfaces
> |define the communication resource manager
> |(CRM) that the JTA does not specify
> |except for mentioning JTS for
> |interoperability (standard wire protocol).
> |In JTS, package org.omg.CosTSPortability
> |does the job of the CRM. The two
> |interfaces I made for this are a little
> |simpler, as I prefer to keep things simple,
> |and we generally do not need the complexity
> |introduced by CORBAs Dynamic Invocation
> |Interface. In the few special cases where
> |it might be needed (like CORBA deferred
> |invocation with transaction contexts under
> |a non-JTS transaction service), it should
> |be possible to add a little extra code to
> |the non-JTS TM support it. And we won't
> |even have this problem until we have a
> |non-JTS distributed TM capable of exporting
> |transactions to the CORBA OTS domain.
>
> Ok so you create a Tx representation in the local TM through portable
> interfaces...
No, I do not use portable interfaces here, only
JBoss specific.
IMHO implementing CosTSPortability import/export
(or something similar) would be more complex
than needed.
> Corba introduces so much complexity to insure "portability" :)
In this case CORBA is not complex to ensure
portability, but to support seldom used and
advanced features.
For example: CORBA makes it possible to send
out an invocation request that does not block,
but lets the local thread continue and later
poll for a reply (or exception).
For a TM to implement checked transactions, it
must be able to ensure that all work in the
transaction has finished before the transaction
is committed. An outstanding transactional
invocation where the reply has not yet been
received is outstanding work-in-progress,
meaning that an attempt to commit the
transaction should fail with checked behaviour.
So to support checked behaviour, two callbacks
from the object broker transport to the TM are
needed on outgoing calls: One when sending the
invocation request, and another when the reply
is received. And these callbacks need some kind
of argument that the TM can use to correlate
requests with replies.
A bit heavy-weight when we just need one
callback to convert a Transaction into a TPC,
so I decided against it.
In most systems the transaction import, export
and thread association is done completely
transparent to all code outside the TM and
object invocation transport layer.
This is how JTS operates when it is cooperating
with a CORBA object request broker.
If we should go this route, the import and
thread association would happen in the
RemoteMethodInvocation, and containers
would use the thread associated TX for the
incoming TX context.
This is possible, and would make the code for
optimized local calls a bit simpler.
But it would also mean that an incoming TX
context would always be imported, even if
never used.
> |> |Another thing that binds JBoss to the current
> |> |native TM is the use of the non-JTA methods
> |> |associateThread() and disassociateThread. As a
> |> |proparation for avoiding these calls, I've
> |> |implemented the JTA methods suspend() and
> |> |resume(). When JBoss has been changed to use
> |>
> |> We have been there before and it is a mistake.
> |
> |Well, judging from code comments, we do not
> |quite agree here. I am just not sure why.
> |Guess this happened before I got involved with
> |JBoss.
>
> Most of the comments in the TM are mine :)
>
> |> suspend() and resume() are
> |> methods ON THE TRANSACTION (suspend the transaction and resume the
> |> transaction), suspend association and resume association are ENTIRELY
> |> DIFFERENT semantics, they suspend and resume teh association but NOT THE
> |> TRANSACTION. It is a spec missread.
> |
> |I think that the method names suspend() and
> |resume() are misleading:
> |Reading JTA 1.0.1, section 3.2.3, it is clear
> |that thread association *is* done in these
> |methods.
>
> Yes, suspend and resume "suspend" the Transaction AND disassociate (of
> course). But a "disassociation" does not suspend the transaction. Do we
> agree on that?
I think we can agree that conceptually the
transaction suspend means that it is disassociated
from the thread and its enlisted resources are
suspended.
But when reading JTA section 3.2.3 (less than
one page), it is pretty clear that the resource
suspension is the responsibility of the
application server, *not* the JTA implementation.
This is why I think that the method names
suspend() and resume() are misleading.
But if the suspension of resources should not
be done in these methods, only the thread
association remains.
Please note that the notion of transaction
suspension doesn't really exist outside JTA.
XA doesn't know about it, and for JTS it is
only defined as two methods suspend() and
resume() that does thread association only.
Also, resource suspension is specific to JTA.
> So do not use "suspend" to JUST disassociate since it propagates to the RM.
> (that is how I spotted the problem with JOnAS)
If you used them JonAS-specific methods for
registering XAConnections: Yes, it propagates
to these resources.
If you didn't use these methods to register
XAConnections: The version of JonAS I am
looking at would do thread association *only*
in the TM suspend()/resume() methods.
> |But given these method names one would think
> |that transaction suspension and resumption
> |is also done here. This is not really the
> |case, as JTA 1.0.1, section 3.2.3 specifically
> |says that the application server is responsible
> |for calling XAResource.end(TMSUSPEND) and
> |XAResource.start(TMRESUME) on the right
> |resources at the right times.
> |
> |JTA 1.0.1, section 3.2 says: "The
> |javax.transaction.TransactionManager interface
> |allows the application server to control
> |transaction boundaries on behalf of the
> |application being managed. For example, the EJB
> |container manages the transaction states for
> |transactional EJB components; the container
> |uses the TransactionManager interface mainly to
> |demarcate transaction boundaries where
> |operations affect the calling thread's
> |transaction context. The Transaction Manager
> |maintains the transaction context association
> |with threads as part of its internal data
> |structure. A thread's transaction context is
> |either null or it refers to a specific global
> |transaction. Multiple threads may concurrently
> |be associated with the same global transaction."
> |
>
> Well at least we are further than we were with Rickard :)
> Ok this sentence also clearly shows the separation between "suspend" and
> suspend Association. Many thread can be associated to a transaction and you
> can "suspend" the association on each one individually. Suspend on the
> "transaction" would suspend teh "Global" transaction and of course propagate
> to the other "local" associations.
>
> "suspend" is a global notion on the "global transaction" whereas association
> doesn't affect teh liveliness of the TM just the local VM association.
I guess we should be careful about the definition
of "global" here. For a distributed TM this could
have two meanings:
- Everything on all nodes that know about the TX.
- Everything on a local node that know about the TX.
In the following I assume you mean the last definition,
since we are talking JTA and JTA defines VM-local
interfaces only. (And X/Open DTM and CORBA OTS has
no remote interface for tx suspension.)
You say that suspend on the "transaction" (I read:
transaction associated with local thread) would
suspend the "global transaction" (I read: Any
thread local transaction representing the same global
transaction in the local VM) and propagate to the
other "local" associations.
But what exactly do you mean by this?
If you mean the thread association (I don't
really think so), it would mean that the tx
is disassociated from all threads when one of
them calls TM.suspend().
If you mean the resource suspension: Yes this
touches the entire tx, as seen from any
thread. But JTA says it this is the
responsibility of the application server (not
the TM) to do this.
> Thread association has no "web meaning" it just is an engineering trick to
> propagate context by the thread.
Yes. A simple trick to make programming
easier. Nothing more.
In JTS the tx thread association is only
done in interface Current, an special
local interface designed only to make tx
programming easier.
> What has meaning is the global transaction. Suspend and resume apply to
> these, not the local ones.
>
> |I cannot really read this as anything but:
> |"Thread association done here".
>
> I read as "Thread association ALSO done here"
So we can agree that thread association *is*
done here, and disagreement is about if
something else is also done here.
> |And since section 3.2.3 (about these two
> |methods) also says "Thread association done
> |here" and "Resource tx suspension is caller's
> |responsibility", I find it hard to read the
> |JTA spec any other way than "the method names
> |are misleading", and "these methods do (almost)
> |what the JBoss-specific thread association
> |does".
>
> hmmmm I think it is due to the fact that most people missread this (few
> people understand TX). To me it is pretty clear. I did factor out the
> "Thread association" as a portability layer in propagation context... you
> put them back as one, in my opinion it is misleading.
I think that I am about to understand your point
here. Some kind of layer of code is needed for
keeping track of:
- Which components are executing in which TXs.
- Which resource connections are open, and
which components have them open.
I just cannot read JTA as saying that this
functionality should belong to the JTA
implementation.
An example: Component Ca is holding a reference
to an open resource connection Ra, and component
Cb is holding a reference to an open resource
connection Rb. Component Ca is executing in a
transaction Ta, and is about to call a method on
Cb with tx attribute REQUIRES_NEW.
Question is: When interposing on this call, what
should be done by the application server, and
what should be done by the JTA implementation.
IMO, the answer is:
1) The application server should suspend Ra from
its association to Ta by calling Ra.end(TMSUSPEND).
This tells Ra that any work done on it is not done
in the context of Ta.
2) The application server calls TM.suspend() to
suspend the thread association with Ta.
3) The application server calls TM.begin() to
start the new transaction that Cb needs. Lets
call this transaction Tb.
4) The application server enlists Rb with Tb
by calling Tb.enlistResource(Rb). This ensures
that work done on Rb is done in the context of
Tb.
5) The application server calls the method on
Cb.
6) Cb method returns sucessfully.
7) The application server calls TM.commit() to
commit Tb.
8) The JTA implementation checks to see if any
enlisted resources have not yet been delisted,
and delists these resources. Rb was not delisted
from Tb, so the JTA impl calls Rb.end(TMSUCCESS).
(I am not sure if JTA mandates this. It seems to
say that the AS should do Tb.delistResource(Rb),
but doing this check doesn't hurt.)
9) The application server calls Ra.start(RESUME).
This tells Ra that any work done on it is now
again done in the context of Ta.
10) The application server calls TM.resume(Ta) to
re-associate the thread with Ta.
11) The application server returns to Ca.
JTA spec, section 3.2.3 says that steps 1 and 9
must be performed by the application server, but
does not explain why. My guess it that these two
steps do not always have to be done, and the AS
is the only one who really knows. In the example
above steps 1 and 9 could be avoided if (and only
if) the AS knows for sure that Ra will not be
used at all between these two steps.
> |I think I am right here, but I may still
> |misread JTA.
> |If you think so, please let me know.
>
> Well ok I wont' say you misread the global vs local scope of the operations.
>
> btw that is why I love "Mark Hapner" as a spec writer, (EJB/JMS) local is
> his building block... global notions are constructs... very powerful very
> clean very clear... JTA and XA aren't as clear.
IMHO, XA is hard to understand simply because it
is complicated.
But JTA is not the best written specification I've
seen. I saw a comment that describes this nicely
on a public mailing list archive: "This looks like
it was written overnight, in a hurry".
For example, JTA section 4.2 illustrates how the
application, AS and JTA implementation cooperate,
but completely forgets to show anything about
tx suspension.
If we should be independent of the actual JTA
implementation used we will have to do with the
API that JTA provides. We could have as many
intermediary layers on top of the JTA implementations
as we like, but even these would have to do with
the API provided by JTA.
Otherwise we are not independent from the JTA
implementation, and would have to do special
programming to use non-JTA calls that are specific
to the JTA implementation we use, and that would
mean a JTA implementation specific intermediary
layer for each different JTA implementation we
support.
> Sure, well lets' get it working and then we will worry about "research"...
> first things first
Yes, lets get this working.
I thought this was working, but during a long
load test I got the message:
[daniel_22] error in setOtherField () on bean #3
in loop 342: java.lang.reflect.UndeclaredThrowableException
No further message. Server console logging may
have scrolled off the screen.
I have so far been unable to reproduce this,
and it only occurred once. I guess I have to
test a bit further before committing.
If anybody is interested in seeing the changes
I did, I've uploaded a tarball to
http://www.sparre.dk/unpublished/jboss-tmhack.tar.gz
Best Regards,
Ole Husgaard.