sca-j message
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
| [List Home]
Subject: Re: AW: [sca-j] AW: ISSUE 8: Concurrency model for Service Referenceinstances
- From: Mike Edwards <mike_edwards@uk.ibm.com>
- To: "OASIS Java" <sca-j@lists.oasis-open.org>
- Date: Sun, 24 Feb 2008 14:39:48 +0000
Ron,
Good discussion.....comment inline as
<mje>...</mje>
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>
22/02/2008 18:25
|
To
| "Michael Rowley" <mrowley@bea.com>,
"OASIS Java" <sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
| AW: [sca-j] AW: ISSUE 8: Concurrency
model for Service Reference instances |
|
Hi Michael,
So my understanding of the scenario
was wrong... the component isn't a middleman, he's a funnel. All
the requests that come in, regardless of the client conversation go into
the same conversation with amazon.
In this case, the deeper problem
is not the race condition of setConversationID, but whether or not ServiceReference
is thread safe, and in particular if the service invocation must be implemented
in a thread safe manner. AFAIK, the spec currently makes no such
statement. OTOH, do we say anywhere that service reference are NOT
thread safe. Should we?
<mje>I had always
taken the view that a ServiceReference or indeed a reference object, should
be thread safe and as a result callable from multiple threads at the "same
time".
I think that we should
make statements about thread safety in the spec.</mje>
I believe your code sample to
be correct, and believe that it's the client's responsibility to handle
synchronization in this case. I'm not sure how to even express the
behavior that the runtime would need, in order to relieve the client of
this responsiblity.
<mje>I'm not convinced
that we should expect the runtime to police this stuff - the timing of
when to end a conversation is up to the client and the client needs to
control it.
Mechanics to allow the
system to provide the control could be unwieldy.</mje>
But that wasn't my understanding
of Issue-8. Issue-8 is, I thought, 2 threads, both of which want
to participate in seperate conversations, using the same service reference.
Does everyone now agree that they cannot?
<mje>I never thought
that was possible. The solution to that requirement is the simple
one of having each thread have its own reference object, obtained via the
context API, not a single shared injected reference.</mje>
In a way, your solution is consistent
with P2... which also does not proposal any new functionality, but says
to use the tools that already there to solve the problem. In your
scenario, where the reference is shared, the client must perform synchronization.
In my scenario, where different threads participate in different
conversations, then ComponentContext must be used as a ServiceReference
factory.
<mje>I agree with
that approach, for multiple simultaneous conversations. For me the problem
is multiple sequential conversations using a single reference object, where
the transition from one conversation to the next requires careful control.
I've posted my solution to that problem in a separate email (I'm
hoping for a gold star ;-) ).</mje>
Ron
Von: Michael Rowley [mailto:mrowley@bea.com]
Gesendet: Freitag, 22. Februar 2008 18:49
An: Barack, Ron; OASIS Java
Betreff: RE: [sca-j] AW: ISSUE 8: Concurrency model for Service Reference
instances
Ron,
Thanks for taking on my challenge.
Responses inline...
From: Barack, Ron [mailto:ron.barack@sap.com]
Sent: Thursday, February 21, 2008 5:15 PM
To: OASIS Java
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.
<MR>I’ve always described
conversational services as being designed to enable conversations between
components that can possibly take days (as would this book buying example).</MR>
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.
<MR>It doesn’t sound
like that would solve the problem that my hypothetical developer is trying
to solve. He is trying to maintain a single outstanding conversation
with Amazon, in order to batch up orders of books (possibly to qualify
for free shipping?). Your solution seems to introduce multiple simultaneous
conversations, which would defeat the purpose of this batching.
Also, RequestContext...getConversationID() called from within the BookBatch
component would not return anything, since the communication to BookBatch
would probably be non-conversational – after all, it is composite scoped.</MR>
What I don't understand is howthe
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.
<MR>I’m not arguing
for P1. I’m arguing that whatever solution we come up with should
solve the scenario that I laid forth, since it is, I believe, the most
common scenario where people will run into this problem. And, naturally,
I’d like for it to be fairly easy to use. I suspect that the solution
will include some kind of lock, from the time that the client decides to
set the ConversationID, until the business method is called. Perhaps
like this:
void buyBook(String ISBN)
{
if
(storeRef.getConversation() != null) {
storeRef.getService().addToCart(ISBN);
} else
{
synchronized(storeRef) {
if (storeRef.getConversation() == null) {
storeRef.setConversationID(chooseID());
}
storeRef.getService().addToCart(ISBN);
} // synchronized
}
if
(isTimeToCheckOut())
checkOut();
}
In this solution, the call
first call in the conversation has to be in a mutex section with the code
that sets the conversation ID, so it will not be concurrent. However,
all subsequent calls on the conversation can be concurrent. Note
that this solution is neither P1 nor P2. It basically just says that
Java synchronization needs to be used.
</MR>
Michael
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
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
| [List Home]