← Prev in month ← Prev in thread
Next in thread → Next in month →

AW: [sca-j] AW: ISSUE 8: Concurrency model for Service Reference instances

From
Barack, Ron <>
Date
2008-02-22T18:26:05+00:00
ID
Thread
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:] 

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:] 
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:] 
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
← Prev in month ← Prev in thread
Next in thread → Next in month →