Mike,
I think I laid out a fairly believable
example where a composite scoped client (BookBatch) would deal with a
conversational service (Amazon) and then pointed out the place where threading
problems might occur. I don’t see, based on your email, how you
would expect this example to be solved.
If you don’t like the example of setting the conversation ID,
then setting a callback ID might be more to your liking. It would run
into the same race condition.
Michael
From: Mike
Edwards [mailto:mike_edwards@uk.ibm.com]
Sent: Friday, February 22, 2008
6:14 AM
To: OASIS Java
Subject: Re: [sca-j] AW: ISSUE 8:
Concurrency model for Service Reference instances
Folks,
I
wonder whether we're really getting to the heart of the problem here.
Let's
step back and examine the whole picture for a moment.
At
one level, we have a concern over the use of references by multiple threads.
At
another level we have an interest in understanding how multiple conversations
can be conducted by a client.
These
interact, but not in the way that we have been discussing.
There
is a third question lurking about how Callbacks work in a multi-threaded
environment. I'll get to that one later.
First,
let's take a look at a simple non-conversational service and a client with a
reference to that service.
I
think that the scope of the client matters little, in fact. It's the
usage of the reference that matters.
I
think that a reference to a stateless service can happily be invoked at any
time by any thread belonging to
the
client component. If the client has multiple threads, there may be some
concerns about the order of
requests
that get sent, but that is a problem for the client to sort out. It won't
matter to the service. There
are
certainly no concerns that there will be any "technical" problems
invoking the service operations.
Now,
switch attention to a conversational service and the reference to that service
held by a client.
It
seems to me very simple to express a principle here: a single reference
instance object represents
at
most a single conversation. ( I say "at most" since I think
that we have said that is possible for such
a
reference to be in "conversation not started" state and for non
conversational invocations to take
place
in that state.)
Now
the usage of that single reference object can be handled in one of two ways:
1)
all uses of the reference, by any thread, are for one conversation and one
conversation only
2)
over time, the reference may be used for multiple conversations, but serially -
again, only one
conversation
at a time
1)
requires relatively little management other than ensuring only one set-up of a
conversation ID.
2)
requires more control to ensure that each conversation is started and ended in
a controlled
fashion
(ie setting of a conversationID, calling of an endsConversation method etc,
must be
serialized)
Let's
express another principle: if a client of a conversational service wants to
conduct multiple
conversations
at the same time, it must use multiple reference objects, one for each
conversation.
(This
implies necessary use of the context API to fetch new copies of reference
objects)
I
think that the main concern over multi-thread use of a conversational reference
is thus solved
in
a relatively simple way. If you stick with the idea that one reference is
only involved in one
conversation
at a time, then things are relatively simple and only a small amount of
mechanics
is
required.
One
comment I make is: "why do we allow the client to set the
conversationID?" What's the
purpose
of this? The conversation is primarily about enabling the same service
provider
data
to be reached - the conversation ID matters far more to the provider than to
the client.
I
argue for removing the capability for a client to set the conversation ID.
Now,
that has dealt with conversational references. That leaves callbacks. I'll
leave those
for
another email....
Yours, Mike.
Strategist - Emerging Technologies, SCA & SDO.
Co Chair OASIS SCA Assembly TC.
IBM Hursley
Park, Mail Point 146, Winchester, SO21
2JN, Great Britain.
Phone & FAX: +44-1962-818014 Mobile: +44-7802-467431
Email: mike_edwards@uk.ibm.com
"Barack, Ron"
<ron.barack@sap.com>
21/02/2008 22:15
|
To
|
"OASIS Java"
<sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
|
[sca-j] AW: ISSUE 8: Concurrency model for
Service Reference instances
|
|
Hi Michael,
Let me first make sure I understand your scenario. The
composite-scoped component is essentially a middle man, involved in 2
conversations, with amazon on one side, and the customer on the other.
And the problem is to make sure that requests coming in from the client
conversation get passed to the correct amazon conversation. Both
conversations can be long running. Does that fit?
I think the scenario we had in mind was much more oriented to
short lived conversations. That is, the case where the whole conversation
with StoreRef takes place within a single call to buy books. In this case, you
simply have to replace the injected field "storeRef" with an injected
ComponentContext, and the implementation of buyBook would call
context.getService("storeRef"). Whether the component calls
storeRef.setConversationID or not, you never have any race conditions.
The situation is more complex for these long-running
conversations, and I think it's unsurprising that the code would be, too.
In this case, the code would need to map from the client conversation ID,
to the ID of the amazon conversation. That is, instead of checking if
storeRef.getConversation() is null, the code would call something like
lookupStoreRefId(context.getRequestContext().getServiceReference().getConversationID()).
If the value returned was non-null, the component it would set the storeRef.conversationID
accordingly. Otherwise, it sets the conversionID to chooseID(). The
method lookupStoreRefId probably would use a DB, but could use an in memory
map, or anything else.
What I don't understand is how the alternative proposal,
"P1", would work. Are you expecting the runtime in inject
storeRef's conversationID into some thread local storage before invoking
buyBooks? In this case, isn't the implication that the runtime would be
maintaining the map, just like proposal P2 demands that the client do? Or
are you assuming that the conversationID is already on the thread from previous
calls to setConversationID? In this case, it's true that the client
remains very simple, but the solution requires
a) that the calls in the conversation always
occur in the same thread, and
b) that the server will not be restarted
during the lifetime of the conversation.
Ron
Von: Michael Rowley
[mailto:mrowley@bea.com]
Gesendet: Donnerstag, 21. Februar 2008 20:45
An: OASIS Java; Barack, Ron
Betreff: ISSUE 8: Concurrency model for Service Reference instances
Here
is the description of the issue 8 problem (from the PPT on today’s call):
While the current text says that a
service reference represents a single conversation, it is not clear how a
multi-threaded client should protect a non-conversational service reference's
configuration (conversation id, callback, etc) so that it stays unmodified by
other threads until an actual invocation is executed.
Consider the following code snippet for example:
class AComponent {
@Reference ItemCheckerService srv;
void goCheckItem(long ticket, String itemId) {
ServiceReference sr = (ServiceReference) srv;
sr.setConversationID(ticket);
srv.check(itemId);
}
}
A simple synchronization may lead to strict serialization of remote calls which
is generally undesirable.
I
think we should have a good idea of the likely scenarios in which this
multi-threading will happen. On today’s call, Simon suggested that
code could start its own threads. I agree this is true, but I don’t
want to concentrate on that case, since I think people who go there are willing
to be pretty sophisticated about the threading logic.
I
believe other cases are that the client could be conversation or composite
scoped. Stateless and request scoped components are only active for one
thread at a time. This is implied by the semantics of the @Init and
@Destroy methods, which are called at the beginning and end of the scope
lifetime. For a stateless scope, that lifetime is one call. For
request scope, it is one remotable call (to be clarified based on one of our
open issues).
The
scenario where a conversation-scoped client could be active in two threads at
once is possible, but unlikely, so I’ll concentrate on the case where the
client is composite scoped.
Consider
this scenario: a composite scoped component exists for the purpose of batching
up book orders to Amazon. When orders come in to the BookBatch component,
it forwards them on to Amazon, using the shopping cart that is associated with
the current conversation. After a certain amount of time, or a certain
number of books, the current batch is purchased, and the conversation is ended.
When the next book order comes in, a new batch (conversation) will be
started. How might this look:
@Scope(“COMPOSITE”)
class BookBatch {
@Reference BookStore store;
void buyBook(String ISBN) {
store.addToCart(ISBN);
if
(isTimeToCheckOut())
checkOut();
}
boolean isTimeToCheckOut() {}
void checkOut() {}
}
This seems like a potentially
common scenario where the client would be multi-threaded. Now, to run
into the problem, we have to imagine that the client wanted to choose its own
conversation ID. So, perhaps it would look like this:
@Scope(“COMPOSITE”)
class BookBatch {
@Reference ServiceReference<BookStore> storeRef;
void buyBook(String ISBN) {
if
(storeRef.getConversation() == null)
storeRef.setConversationID(chooseID());
storeRef.getService().addToCart(ISBN);
if
(isTimeToCheckOut())
checkOut();
}
boolean isTimeToCheckOut() {}
void checkOut() {}
String chooseID() {} //
Choose a conversation ID for the next bookstore conversation.
}
In this version, we pick a new
conversation ID if a conversation isn’t already going and set it on the
service reference.
This version has a race condition!
Multiple threads could have null returned from getConversation() and so
multiple threads will attempt to choose the next conversation ID. In this
particular case, it probably doesn’t matter which one wins that race, but
I suppose that in some cases it would matter.
Is this the problem we are trying
to solve? If so, I’m not sure how the proposal in the PPT
presentation given today would help much.
Ron or Simon, would you be willing
to modify this class so that it works correctly given the proposed resolution
to issue 8?
Michael
Unless stated
otherwise above:
IBM United Kingdom Limited - Registered in England
and Wales
with number 741598.
Registered office: PO Box 41,
North Harbour, Portsmouth, Hampshire PO6 3AU