For the first scenario, we can address it with countdown (and timeout) check , as each async invocation only have one local-transaction (or sub-transaction).
For the second scenario, as the topic publisher and topic subscribers are decoupled, it's impossible for Alpha to find out if all the local-transactions are finished or not. This issue could be addressed by published the domain object event across all the services in a decentralized way. Please check out Chris Richardson's microservice Saga pattern[1] for more information. [1]https://microservices.io/patterns/data/saga.html Willem Jiang Twitter: willemjiang Weibo: 姜宁willem On Tue, Jul 30, 2019 at 10:56 PM Zhang Lei <zhang_...@boco.com.cn> wrote: > > Hi, Team > > For asynchronous, I think we need to distinguish between two scenario > > 1. After the asynchronous method is executed, the sub-transaction execution > ends, for example: using internal threads to execute. > For this scenario, we allow sub-transactions to be in parallel, but we must > wait for all threads to finish before we can end the main method. same thread > join or use CountDownLatch > > 2. After the asynchronous method is executed, the sub-transaction is still in > execution, for example, PUB / SUB > For this scenario, Can I solve it using TransactionContext? > > Is there something I have missed? > > Lei Zhang > > > 在 2019年7月30日,下午3:28,Willem Jiang <willem.ji...@gmail.com> 写道: > > > > TxAsyncStarted is just let Alpha know there is an async transaction > > invocation, as the local transaction is not started yet, we can only > > connect the TxAsyncStarted event LocalTransactionID with TxStarted > > event ParentTransactionID. > > Once Alpha receives the TxAsyncStarted event, it should wait for > > TxStarted event, In this way Alpha can tell if all the Saga local > > transaction are finished. > > it could cause some trouble if the async invocation is based in > > pub/sub module, it looks like we cannot know if there are multiple > > local transactions will be triggered by a simple message. Unless > > customer tells us about it through annotation. > > > > Willem Jiang > > > > Twitter: willemjiang > > Weibo: 姜宁willem > > > > On Tue, Jul 30, 2019 at 2:49 PM Zheng Feng <zh.f...@gmail.com> wrote: > >> > >> Hi Willem, > >> > >> I am not very clear about the TxAsyncStart event ? what's the difference > >> between the TxAyncStart and TxStarted Event. How does the alpha process the > >> async events ? > >> > >> Thanks, > >> Zheng Feng > >> > >> Willem Jiang <willem.ji...@gmail.com> 于2019年7月30日周二 上午8:41写道: > >> > >>> According to the feedback for PR, I think we need to rethink about the > >>> SagaEnd implementation in the Async invocation scenario. Normally it's > >>> harmless we close the Saga transaction later, but we need to make sure > >>> all the ongoing local transaction are finished. > >>> > >>> In the old way, Alpha can know nothing about the start of Async > >>> Componseable transaction if the TxStartedEvent is not sent, so my > >>> propose that we introduce a new event TxAsyncStart to tell Alpha there > >>> is new Async invocation, so Alpha can keep tracking this Async > >>> invocation event the Componseable transaction is not started yet. In > >>> this way we could block the Async invocation if the Saga transaction > >>> is timeout or close the Saga transaction if all the TxAsyncStart > >>> transaction is finished. > >>> > >>> Any thoughts? > >>> > >>> Willem Jiang > >>> > >>> Twitter: willemjiang > >>> Weibo: 姜宁willem > >>> > >>> On Mon, Jul 29, 2019 at 5:32 PM Willem Jiang <willem.ji...@gmail.com> > >>> wrote: > >>>> > >>>> Export the low level API could introduce some error if the user > >>>> doesn't use the API rightly. > >>>> My suggestion is we just > >>>> BTW, I submit a PR[1] to address this issue in a simple way (But we > >>>> still need to tell user what's the right way to configure the > >>>> annotation attribute). > >>>> > >>>> [1]https://github.com/apache/servicecomb-pack/pull/517 > >>>> > >>>> Willem Jiang > >>>> > >>>> Twitter: willemjiang > >>>> Weibo: 姜宁willem > >>>> > >>>> On Thu, Jul 25, 2019 at 8:44 PM Daniel Qian <chanjars...@gmail.com> > >>> wrote: > >>>>> > >>>>> Hi Zhang Lei, > >>>>> > >>>>> What I'm trying to say is to provide a way for user to send > >>>>> TxEndedEvent, TxAbortedEvent, TxCompensatedEvent, SagaEndedEvent ... > >>>>> explicitly on Omega side. > >>>>> Because current implementation doesn't support following > >>> situation(async): > >>>>> > >>>>> @Compensable(compensationMethod="rollbackFoo") > >>>>> public void foo() { > >>>>> new Thread(() -> /* local tx goes here */).start(); > >>>>> // TxEndedEvent sent when returns, it's too early > >>>>> } > >>>>> > >>>>> public void rollbackFoo() { > >>>>> new Thread(() -> /* compensation goes here*/).start(); > >>>>> // TxCompensatedEvent sent when returns, it's too early > >>>>> } > >>>>> > >>>>> @SagaStart > >>>>> public void bar() { > >>>>> new Thread(() -> /* call other service goes here */).start(); > >>>>> // SagaEndedEvent sent when returns, it's too early > >>>>> } > >>>>> > >>>>> I suggest providing a helper class, called omega or something else, > >>>>> user can use it to send TxEndedEvent, TxAbortedEvent, > >>>>> TxCompensatedEvent, SagaEndedEvent, etc. So the code goes like this: > >>>>> > >>>>> @Compensable(async=true, compensationMethod="rollbackFoo", > >>>>> compensationAsync=true) > >>>>> public void foo() { > >>>>> TransactionContext txContext = omegaContext.getTransactionContext(); > >>>>> new Thread(() -> { > >>>>> try { > >>>>> /* local tx goes here */ > >>>>> omega.txEnded(txContext); > >>>>> } catch(Exception e) { > >>>>> omega.txAborted(txContext); > >>>>> } > >>>>> }).start(); > >>>>> } > >>>>> > >>>>> public void rollbackFoo() { > >>>>> TransactionContext txContext = omegaContext.getTransactionContext(); > >>>>> new Thread(() -> { > >>>>> /*compensation goes here*/ > >>>>> omega.txCompensated() > >>>>> }).start(); > >>>>> } > >>>>> > >>>>> @SagaStart(async=true) > >>>>> public void bar() { > >>>>> TransactionContext txContext = omegaContext.getTransactionContext(); > >>>>> new Thread(() -> { > >>>>> /* call other service goes here */ > >>>>> try { > >>>>> omega.sagaEnded(txContext); > >>>>> } catch (Exception e) { > >>>>> omega.sagaAborted(txContext); > >>>>> } > >>>>> }).start(); > >>>>> } > >>>>> > >>>>> > >>>>> Zhang Lei <zhang_...@boco.com.cn> 于2019年7月25日周四 下午4:46写道: > >>>>> > >>>>> > >>>>> Zhang Lei <zhang_...@boco.com.cn> 于2019年7月25日周四 下午4:46写道: > >>>>>> > >>>>>> Hi, Daniel Qian > >>>>>> > >>>>>> Are you talking about the asynchronous problem with the @SagaStart > >>> and @Compensable methods on the Omega side? I think this is a typical long > >>> transaction scene. > >>>>>> > >>>>>> Alpha based on Actor model has implemented asynchronous processing > >>> of Omega and Alpha, The event sent to Alpha needs to ensure that all child > >>> transactions have been executed before sending SagaEndedEvent or > >>> SagaAbortedEvent. > >>>>>> > >>>>>> Lei Zhang > >>>>>> > >>>>>>> 在 2019年7月20日,下午9:49,Daniel Qian <chanjars...@gmail.com> 写道: > >>>>>>> > >>>>>>> After look into SCB-163, SCB-1385 and SCB-1386 I have some > >>> thoughts on Saga > >>>>>>> involved in async invocation. > >>>>>>> Current implementation is basically based on sync invocation, > >>> there are > >>>>>>> some assumption: > >>>>>>> > >>>>>>> 1. When @SagaStart method returns, the Saga finished. > >>>>>>> 2. When @Compensable method returns/throws exception, the Local > >>> Tx > >>>>>>> succeeds/failed. > >>>>>>> 3. When compensationMethod returns, the Local Tx is compensated. > >>>>>>> > >>>>>>> Even if considering what SCB-100 provided: > >>>>>>> > >>>>>>> 1. Add @OmegaContextAware annotation enabling > >>>>>>> java.util.concurrent.Executor inject OmegaConext into threads it > >>>>>>> manages/spawns > >>>>>>> 2. Make OmegaContext use InheritableThreadLocal field let child > >>> thread > >>>>>>> inherit parent thread's Local Tx info > >>>>>>> > >>>>>>> There are still some limitations: > >>>>>>> > >>>>>>> 1. @OmegaContextAware is only viable if you use spring framework > >>>>>>> 2. @OmegaContextAware and OmegaContext's InheritableThreadLocal > >>> field > >>>>>>> assuming that the calling thread or initator thread has Local > >>> Tx info. > >>>>>>> > >>>>>>> > >>>>>>> What if user code use producer-consumer pattern in which > >>>>>>> InheritableThreadLocal can't work? > >>>>>>> What if user code use a thread scheduling library which we cannot > >>> use > >>>>>>> @OmegaContextAware,RxJava and Reactor, for example? > >>>>>>> I think we could provide some low-level APIs that user code can > >>> manualy > >>>>>>> starts/ends Saga and Local Tx, something like below: > >>>>>>> > >>>>>>> TxContext context = omega.startSaga(); > >>>>>>> TxContext subTxContext = omega.startTx(TxContext parentTxContext); > >>>>>>> omega.endTx(TxContext); > >>>>>>> omega.abortTx(TxContext); > >>>>>>> omega.abortSaga(TxContext); > >>>>>>> omega.endSaga(TxContext); > >>>>>>> > >>>>>>> TxContext is just a immutable dto like this: > >>>>>>> > >>>>>>> public class TxContext { > >>>>>>> private final String globalTxId; > >>>>>>> private final String localTxId; > >>>>>>> } > >>>>>>> > >>>>>>> Above is a just a rough idea. So any thoughts? > >>>>>>> -- > >>>>>>> Daniel Qian > >>>>>>> > >>>>>>> 博客:https://segmentfault.com/u/chanjarster > >>>>>>> github:https://github.com/chanjarster > >>>>>> > >>>>> > >>>>> > >>>>> -- > >>>>> Daniel Qian > >>>>> > >>>>> 博客:https://segmentfault.com/u/chanjarster > >>>>> github:https://github.com/chanjarster > >>> >