Wiring being changed requires a call to set(),
so naturally, after a set() the value of get() will be different. If someone
accesses a reference twice, without the reference having been modified between
those two accesses, then one would expect to get the same thing.
Michael
From: Simon
Nash [mailto:NASH@uk.ibm.com]
Sent: Thursday, February 28, 2008
7:35 AM
To: OASIS Java
Subject: RE: [sca-j] AW: ISSUE 8:
Concurrency model for Service Reference instances
The semantics of getServiceReference() require it to
return a newly created object in some cases. Specifically, this will
happen when wiring has been changed, as we agreed in the resolution to issue 4.
For consistency and simplicity, I think this API should always return a
newly created object. The business logic can cache the result of a
previous call if it doesn't want a new object to be created.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM
Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
"Michael Rowley"
<mrowley@bea.com>
23/02/2008 16:05
|
To
|
"Barack, Ron"
<ron.barack@sap.com>, "OASIS Java"
<sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
|
RE: [sca-j] AW: ISSUE 8: Concurrency model
for Service Reference instances
|
|
I agree.
I guess my concern is that the scenario in which the P2
solution is helpful is, IMO, unlikely. One of the problems is that I view
threads as being used for only a few seconds at a time, while I expect that
conversations will typically last much longer than that.
So, what is the harm in P2? I suppose the main harm is
in the name of the routine: getServiceReference(). In my opinion, a
“get” doesn’t sound like a create, and so I would be
surprised if I got a newly created object with each call to get(). If the
API to do create were called createServiceReference() instead, I suppose I
would be less uncomfortable about it. I suppose the question would then
be, how the client get the “current” conversational service
reference for the reference, as I believe that the most common case is that
there will only be one conversation going at a time, especially when the client
is conversation scoped.
Michael
From: Barack, Ron
[mailto:ron.barack@sap.com]
Sent: Friday, February 22, 2008 1:26 PM
To: Michael Rowley; OASIS Java
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?
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.
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?
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.
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