[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]
Subject: 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] 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] 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. 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 {
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 {
if (storeRef.getConversation() == null)
storeRef.setConversationID(chooseID());
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 |
[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]