jpos / jPOS-EE

jPOS Extended Edition
http://jpos.org
GNU Affero General Public License v3.0
107 stars 152 forks source link

QRest SendResponse participant does not send response in the prepare method #300

Open mchhil-incomm opened 5 months ago

mchhil-incomm commented 5 months ago

https://github.com/jpos/jPOS-EE/blob/master/modules/qrest/src/main/java/org/jpos/qrest/SendResponse.java#L49-L50

I have a participant that takes a lot of time. LongRunningParticpant

This LongRunningParticpant is placed after the SendResponse participant in the txn mgr.

But since the response is only sent in the commit method

https://github.com/jpos/jPOS-EE/blob/master/modules/qrest/src/main/java/org/jpos/qrest/SendResponse.java#L54-L60

It gets sent after the  'LongRunningParticpant' completes.

Snippet of profiler

    <profiler>
     prepare: o.j.q.p.Router [0.6/0.6]
     prepare: c.o.a.PrepareContext [1.7/2.4]
     prepare: c.o.a.InsertRulesInDB [21.8/24.2]
     prepare: o.j.q.SendResponse [0.1/24.4]
     prepare: c.o.a.TriggerMerchantBlockCacheReload [60241.7/60266.1]
      commit: c.o.a.PrepareContext [0.1/60266.2]
      commit: c.o.a.InsertRulesInDB [0.0/60266.3]
      commit: o.j.q.SendResponse [0.8/60267.1]
      commit: c.o.a.TriggerMerchantBlockCacheReload [0.0/60267.1]
     end [1.2/60268.3]
   </profiler>

So the question is why does the prepare not do anything in the SendResponse participant?

barspi commented 5 months ago

SendResponse does its main job in the "closing phase" of the two-phase commit protocol. That could be either a commit or abort. It's good to do it there (and to allow it to happen even if a previous participant aborted it) because that's when you have enough information to create your response.

I don't know what you're doing in your TriggerMerchantBlockCacheReload. If it's not related to the transaction, maybe you can choose to 1) do the work also during commit/abort (instead of prepare) 2) do the work asynchronously, for example by preparing some work payload and sending it to a queue from where a "TriggerMerchantBlockWhatever" QBean will be reading in its own thread and doing the work in a concurrent thread, after the transaction has happened.

mchhil-incomm commented 5 months ago

Thanks for responding @barspi
Could the identical code in commit and abort be moved to prepare and call prepare from abort and remove the commit call?

Option 2 was my backup though the long running participant is ideally part of the transaction :)

Suggestion

 public int prepare(long id, Serializable context) {
        sendHTTPResponse(context);
        return PREPARED | READONLY;
    }

    protected void sendHTTPResponse(Serializable context) {
        Context               ctx      = (Context) context;
        ChannelHandlerContext ch       = ctx.get(SESSION);
        FullHttpRequest       request  = ctx.get(REQUEST);
        FullHttpResponse      response = getResponse(ctx);
        sendResponse(ctx, ch, request, response);
    }

    @Override
    public void abort(long id, Serializable context) {
        sendHTTPResponse(context);
    }
    // don't call it in commit