Re: Unexpected idle timeout behaviours
On Fri, Mar 15, 2024 at 8:03 PM Timothy Bish wrote: > > I'm glad you got to the bottom of it, thank you for bearing with me. I > can > > run a parallel compile for > > my purposes, but I was wondering if there is a planned release date for > M20 > > ? > > > > Thank you. > > > No planned date as someone keeps reporting issues. Was planning to > release something in a few weeks for protonj2 and proton-dotnet > > 😂 Sorry about that. You'll be glad to know I'm almost at the end of writing my test suite that covers the non application layer logic! Thank you for solving the underlying issue, it was a touch problematic!
Re: ProtonJ2 client handling of null message bodies
On 3/15/24 15:42, Ciaran wrote: On Fri, Mar 15, 2024 at 6:14 PM Timothy Bish wrote: On 3/15/24 11:50, Ciaran wrote: Hi, I'm attempting to build a resilient client using the protonj2 client and as part of my testing I was checking that it would handle messages with null bodies (specifically my client is only willing to work with strings or byte arrays so it rejects any other AMQP message body type). However, when I put a null body on the message (i.e. body length of 0), when I de-reference the delivery's message's body property (e.g. delivery.message().body) I receive an error from the proton engine: Can you capture a frame trace, I need enough information to try and create an exact reproducer which can be aided with traces of the incoming frames to reverse engineer a test. I wouldn't expect and exception here and its likely something that was missed in the current test suite. Code snippets of the sender and receiver bits can help as well. Hi, Please find below everything I think you've asked for, the real code is somewhat distributed across several different classes, but this should be pretty equivalent: RECEIVER CODE - ConnectionOptions options = constructBasicConnectionOptions(); options.reconnectOptions() .reconnectEnabled(true) .useReconnectBackOff(true) .reconnectDelay(5000) .maxReconnectDelay(10240001) .maxReconnectAttempts(12) .warnAfterReconnectAttempts(1); Client client= Client.create(); Connection connection = client.connect("host", 1234, options); ReceiverOptions ro = new ReceiverOptions(); ro.autoAccept(false); Receiver receiver = connection.openReceiver( "queue", ro )) { Delivery delivery = receiver.tryReceive(); if( delivery != null ) { Message message= delivery.message(); // Exception occurs here. Object body= message.body(); } SENDER CODE --- final Client client = Client.create(); final ConnectionOptions options = new ConnectionOptions(); options.sslOptions().sslEnabled(true); options.user("MY_USER"); options.password("MY_PASSWORD"); Connection connection = client.connect(serverHost, serverPort, options); Sender sender = connection.openSender(address); sender.send(Message.create((byte[])null)); FRAME TRACE -> SASL:[269961536:0] AMQP,3,1,0,0 <- SASL:[269961536:0] AMQP,3,1,0,0 <- SASL:[269961536:0] SaslMechanisms{saslServerMechanisms=[MSSBCBS, PLAIN, ANONYMOUS, EXTERNAL]} 19:36:21,633 WARN SaslMechanismSelector:130 - Caught exception while trying to create SASL mechanism MSSBCBS: No Matching SASL Mechanism with name: MSSBCBS -> SASL:[269961536:0] SaslInit{mechanism=PLAIN, initialResponse="\x00MY_USER\x00MY_PASSWORD"...(truncated), hostname=' MY_HOST.servicebus.windows.net'} <- SASL:[269961536:0] SaslOutcome{code=OK, additionalData="Welcome!"} -> AMQP:[269961536:0] AMQP,0,1,0,0 <- AMQP:[269961536:0] AMQP,0,1,0,0 -> AMQP:[269961536:0] Open{ containerId='ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2', hostname=' MY_HOST.servicebus.windows.net', maxFrameSize=65536, channelMax=65535, idleTimeOut=6, outgoingLocales=null, incomingLocales=null, offeredCapabilities=null, desiredCapabilities=[ANONYMOUS-RELAY], properties=null} -> AMQP:[269961536:0] Begin{remoteChannel=null, nextOutgoingId=0, incomingWindow=1600, outgoingWindow=2147483647, handleMax=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} -> AMQP:[269961536:0] Attach{name='receiver-ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2:1:1', handle=0, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST, source=Source{address='testq', durable=NONE, expiryPolicy=LINK_DETACH, timeout=0, dynamic=false, dynamicNodeProperties=null, distributionMode=null, filter=null, defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false, messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list, amqp:released:list, amqp:modified:list], capabilities=null}, target=Target{address='testq', durable=NONE, expiryPolicy=SESSION_END, timeout=0, dynamic=false, dynamicNodeProperties=null, capabilities=null}, unsettled=null, incompleteUnsettled=null, initialDeliveryCount=null, maxMessageSize=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} -> AMQP:[269961536:0] Flow{nextIncomingId=null, incomingWindow=1600, nextOutgoingId=0, outgoingWindow=2147483647, handle=0, deliveryCount=null, linkCredit=10, available=null, drain=false, echo=null, properties=null} <- AMQP:[269961536:0] Open{ containerId='af1bcb999146429ea0a245db6affd5e4_G60', hostname='null', maxFrameSize=65536, channelMax=4999, idleTimeOut=12, outgoingLocales=null, incomingLocales=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} <- AMQP:[269961536:0] Begin{remoteChannel=0, nextOutgoingId=1, incomingWindow=5000, outgoingWindow=1600, handleMax=255, offeredCapabilities=null, desiredCapabilities=null, properties=null} <- AMQP:[269961536:0] Attach{name='receiver-ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2:1:1', handle=0, role
Re: Unexpected idle timeout behaviours
On 3/15/24 15:44, Ciaran wrote: This should be fixed no in: https://issues.apache.org/jira/browse/PROTON-2807 This essentially occurred because the Proton buffers were rewritten to allow zero copy semantics after the ingest code was written and the read offset is no longer reflective of the read outcome as the buffer may have handed off its underlying payload and have a size of zero which means the read offset will still be zero. This code was written in early days and had some safety checks in it that are no longer needed given other mechanics in place in the engine so has been updated to treat an non-empty buffer as cause to update the input sequence value. It's necessary to reproduce and explain the issues before making these changes so I needed to find a test case for this which I now have. I'm glad you got to the bottom of it, thank you for bearing with me. I can run a parallel compile for my purposes, but I was wondering if there is a planned release date for M20 ? Thank you. No planned date as someone keeps reporting issues. Was planning to release something in a few weeks for protonj2 and proton-dotnet -- Tim Bish - To unsubscribe, e-mail: users-unsubscr...@qpid.apache.org For additional commands, e-mail: users-h...@qpid.apache.org
Re: Unexpected idle timeout behaviours
> > > > > > This should be fixed no in: > > > > https://issues.apache.org/jira/browse/PROTON-2807 > > > > This essentially occurred because the Proton buffers were rewritten to > > allow zero copy semantics after the ingest code was written and the read > > offset is no longer reflective of the read outcome as the buffer may > > have handed off its underlying payload and have a size of zero which > > means the read offset will still be zero. This code was written in > > early days and had some safety checks in it that are no longer needed > > given other mechanics in place in the engine so has been updated to > > treat an non-empty buffer as cause to update the input sequence value. > > It's necessary to reproduce and explain the issues before making these > > changes so I needed to find a test case for this which I now have. > > I'm glad you got to the bottom of it, thank you for bearing with me. I can run a parallel compile for my purposes, but I was wondering if there is a planned release date for M20 ? Thank you.
Re: ProtonJ2 client handling of null message bodies
On Fri, Mar 15, 2024 at 6:14 PM Timothy Bish wrote: > On 3/15/24 11:50, Ciaran wrote: > > Hi, > > > > I'm attempting to build a resilient client using the protonj2 client and > as > > part of my testing I was checking that it would handle messages with null > > bodies (specifically my client is only willing to work with strings or > byte > > arrays so it rejects any other AMQP message body type). However, when I > put > > a null body on the message (i.e. body length of 0), when I de-reference > the > > delivery's message's body property (e.g. delivery.message().body) I > receive > > an error from the proton engine: > > Can you capture a frame trace, I need enough information to try and > create an exact reproducer which can be aided with traces of the > incoming frames to reverse engineer a test. I wouldn't expect and > exception here and its likely something that was missed in the current > test suite. > > Code snippets of the sender and receiver bits can help as well. > > > Hi, Please find below everything I think you've asked for, the real code is somewhat distributed across several different classes, but this should be pretty equivalent: RECEIVER CODE - ConnectionOptions options = constructBasicConnectionOptions(); options.reconnectOptions() .reconnectEnabled(true) .useReconnectBackOff(true) .reconnectDelay(5000) .maxReconnectDelay(10240001) .maxReconnectAttempts(12) .warnAfterReconnectAttempts(1); Client client= Client.create(); Connection connection = client.connect("host", 1234, options); ReceiverOptions ro = new ReceiverOptions(); ro.autoAccept(false); Receiver receiver = connection.openReceiver( "queue", ro )) { Delivery delivery = receiver.tryReceive(); if( delivery != null ) { Message message= delivery.message(); // Exception occurs here. Object body= message.body(); } SENDER CODE --- final Client client = Client.create(); final ConnectionOptions options = new ConnectionOptions(); options.sslOptions().sslEnabled(true); options.user("MY_USER"); options.password("MY_PASSWORD"); Connection connection = client.connect(serverHost, serverPort, options); Sender sender = connection.openSender(address); sender.send(Message.create((byte[])null)); FRAME TRACE -> SASL:[269961536:0] AMQP,3,1,0,0 <- SASL:[269961536:0] AMQP,3,1,0,0 <- SASL:[269961536:0] SaslMechanisms{saslServerMechanisms=[MSSBCBS, PLAIN, ANONYMOUS, EXTERNAL]} 19:36:21,633 WARN SaslMechanismSelector:130 - Caught exception while trying to create SASL mechanism MSSBCBS: No Matching SASL Mechanism with name: MSSBCBS -> SASL:[269961536:0] SaslInit{mechanism=PLAIN, initialResponse="\x00MY_USER\x00MY_PASSWORD"...(truncated), hostname=' MY_HOST.servicebus.windows.net'} <- SASL:[269961536:0] SaslOutcome{code=OK, additionalData="Welcome!"} -> AMQP:[269961536:0] AMQP,0,1,0,0 <- AMQP:[269961536:0] AMQP,0,1,0,0 -> AMQP:[269961536:0] Open{ containerId='ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2', hostname=' MY_HOST.servicebus.windows.net', maxFrameSize=65536, channelMax=65535, idleTimeOut=6, outgoingLocales=null, incomingLocales=null, offeredCapabilities=null, desiredCapabilities=[ANONYMOUS-RELAY], properties=null} -> AMQP:[269961536:0] Begin{remoteChannel=null, nextOutgoingId=0, incomingWindow=1600, outgoingWindow=2147483647, handleMax=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} -> AMQP:[269961536:0] Attach{name='receiver-ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2:1:1', handle=0, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST, source=Source{address='testq', durable=NONE, expiryPolicy=LINK_DETACH, timeout=0, dynamic=false, dynamicNodeProperties=null, distributionMode=null, filter=null, defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false, messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list, amqp:released:list, amqp:modified:list], capabilities=null}, target=Target{address='testq', durable=NONE, expiryPolicy=SESSION_END, timeout=0, dynamic=false, dynamicNodeProperties=null, capabilities=null}, unsettled=null, incompleteUnsettled=null, initialDeliveryCount=null, maxMessageSize=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} -> AMQP:[269961536:0] Flow{nextIncomingId=null, incomingWindow=1600, nextOutgoingId=0, outgoingWindow=2147483647, handle=0, deliveryCount=null, linkCredit=10, available=null, drain=false, echo=null, properties=null} <- AMQP:[269961536:0] Open{ containerId='af1bcb999146429ea0a245db6affd5e4_G60', hostname='null', maxFrameSize=65536, channelMax=4999, idleTimeOut=12, outgoingLocales=null, incomingLocales=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} <- AMQP:[269961536:0] Begin{remoteChannel=0, nextOutgoingId=1, incomingWindow=5000, outgoingWindow=1600, handleMax=255, offeredCapabilities=null, desiredCapabilities=null, properties=null} <- AMQP:[269961536:0] Attach{name='receiver-ID:081fcabd-ac34-41b5-9bff-fe35d0cfbff1:1:2:1:1',
Re: ProtonJ2 client handling of null message bodies
On 3/15/24 11:50, Ciaran wrote: Hi, I'm attempting to build a resilient client using the protonj2 client and as part of my testing I was checking that it would handle messages with null bodies (specifically my client is only willing to work with strings or byte arrays so it rejects any other AMQP message body type). However, when I put a null body on the message (i.e. body length of 0), when I de-reference the delivery's message's body property (e.g. delivery.message().body) I receive an error from the proton engine: Can you capture a frame trace, I need enough information to try and create an exact reproducer which can be aided with traces of the incoming frames to reverse engineer a test. I wouldn't expect and exception here and its likely something that was missed in the current test suite. Code snippets of the sender and receiver bits can help as well. Caused by: org.apache.qpid.protonj2.client.exceptions.ClientException: Index 1 out of bounds for length 1 at org.apache.qpid.protonj2.client.impl.ClientExceptionSupport.createNonFatalOrPassthrough( ClientExceptionSupport.java:103) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:170) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:152) at org.apache.qpid.protonj2.client.impl.ClientDelivery.message( ClientDelivery.java:79) at com.thisisnumero.smartagent.gateway.inbound.sources.background.amqp.AMQPMessageConsumerImpl.internalConsumeMessages( AMQPMessageConsumerImpl.java:53) ... 4 more Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1 at org.apache.qpid.protonj2.buffer.impl.ProtonCompositeBufferImpl.findChunkWithIndex( ProtonCompositeBufferImpl.java:1530) at org.apache.qpid.protonj2.buffer.impl.ProtonCompositeBufferImpl.copyInto( ProtonCompositeBufferImpl.java:387) at org.apache.qpid.protonj2.codec.decoders.messaging.DataTypeDecoder.readValue( DataTypeDecoder.java:85) at org.apache.qpid.protonj2.codec.decoders.messaging.DataTypeDecoder.readValue( DataTypeDecoder.java:40) at org.apache.qpid.protonj2.codec.decoders.ProtonDecoder.readObject( ProtonDecoder.java:192) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:168) ... 7 more My expectation from the comments on the 'body()' method is that I should expect it to return null in this scenario? Should I be trapping this exception and rejecting the messages to avoid poisoning my queue? (I was planning on rejecting them anyway, but it seems weird to have to catch the exception first.) For reference, I'm putting messages onto the queue with the following protonj2 calls: sender.send(Message.create((byte[])null)); sender.send(Message.create((*String*)null)); Thank you (and sorry for the bombardment of things) -- Tim Bish - To unsubscribe, e-mail: users-unsubscr...@qpid.apache.org For additional commands, e-mail: users-h...@qpid.apache.org
Re: Unexpected idle timeout behaviours
On 3/14/24 19:28, Ciaran wrote: I've provided the full set of frames below, but if I'm interpreting correctly the inputSequence property is not being incremented on receipt of data which means that the deadline for hearing from the peer again is not being suitably increased. (I can see the sequence increments and the localdeadline is recalculated for the empty frames the peer sends, but not for when there's actual data.) I've gone back through the client and engine code and did some testing and cannot reproduce any issue where the sequence tracking is not updated when incoming frames are read regardless of which frame they are. I fixed a minor issue where the Client is not configuring the idle timeout to half the set value as it says it will. Hi Tim, Thank you for taking a look, but I'm still seeing the issue :( I've cherry-picked the idle timeout commit on top of M19 and it /may/ be a little better, but that still feels like it's covering up an issue. Forgive me, but I'm not deeply familiar with the protonj2 codebase, but it seems logical to me that the inputSequence is incremented whenever some data is received from the peer. This should be fixed no in: https://issues.apache.org/jira/browse/PROTON-2807 This essentially occurred because the Proton buffers were rewritten to allow zero copy semantics after the ingest code was written and the read offset is no longer reflective of the read outcome as the buffer may have handed off its underlying payload and have a size of zero which means the read offset will still be zero. This code was written in early days and had some safety checks in it that are no longer needed given other mechanics in place in the engine so has been updated to treat an non-empty buffer as cause to update the input sequence value. It's necessary to reproduce and explain the issues before making these changes so I needed to find a test case for this which I now have. I've attached another trace with additional logging to show the state of ProtonEngine#ingest() function. As I mentioned earlier, for Transfer frames the readOffset of the input buffer after fireRead has completed is 0, which the logic treats as a 'no sequence' operation. The interesting part, you may see below is that for each transfer there are multiple (two) calls to ingest, both returning 0 as the post fireRead value of the readOffset. I presume this is due to buffering and needing to fetch more data for this particular frame type, so I can get my head around the first of these reads being ignored as a sequence increment, but it still feels to me that the second (or presumably the very last one in the case of larger payloads) should return a non-zero value to trigger the sequence increment? I'm not quite sure what the value of enforcing this logical sequence is as it only appears to be used as a 'liveness' indicator for the remote connection, does it matter if it was a valid frame returned? Trace with additional logging of the variable state below: <- AMQP:[1619358550:0] Open{ containerId='91791f32464449109b4136667ccaa3f7_G52', hostname='null', maxFrameSize=65536, channelMax=4999, idleTimeOut=12, outgoingLocales=null, incomingLocales=null, offeredCapabilities=null, desiredCapabilities=null, properties=null} startIndex:0, input.getReadOffset():71 inputSequence++ <- AMQP:[1619358550:0] Begin{remoteChannel=0, nextOutgoingId=1, incomingWindow=5000, outgoingWindow=1600, handleMax=255, offeredCapabilities=null, desiredCapabilities=null, properties=null} startIndex:0, input.getReadOffset():34 inputSequence++ <- AMQP:[1619358550:0] Attach{name='receiver-ID:ecec0184-d4d3-4fe8-b8ec-0b132b249368:1:2:1:1', handle=0, role=SENDER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST, source=Source{address='testq', durable=NONE, expiryPolicy=LINK_DETACH, timeout=0, dynamic=false, dynamicNodeProperties=null, distributionMode=null, filter=null, defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false, messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list, amqp:released:list, amqp:modified:list], capabilities=null}, target=Target{address='testq', durable=NONE, expiryPolicy=SESSION_END, timeout=0, dynamic=false, dynamicNodeProperties=null, capabilities=null}, unsettled=null, incompleteUnsettled=null, initialDeliveryCount=0, maxMessageSize=18446744073709551615, offeredCapabilities=[SHARED-SUBS], desiredCapabilities=null, properties=null} startIndex:0, input.getReadOffset():258 inputSequence++ startIndex:0, input.getReadOffset():0 <- AMQP:[1619358550:0] Transfer{handle=0, deliveryId=0, deliveryTag=DeliveryTag: {[56, 109, 70, 77, -127, -59, 8, 72, -120, 61, 85, -116, 30, -125, -121, -112]}, messageFormat=0, settled=null, more=false, rcvSettleMode=null, state=null, resume=null, aborted=null, batchable=true} - "\x00Sp\xc0\x0a\x05@@pH\x19\x08\x00@C \x00Sq\xc1$\x02\xa3\x10x-opt"...(truncated) startIndex:0, input.getReadOffset():0 -> AMQP:[1619358550:0] Disposition{role=RECEIVER, fi
ProtonJ2 client handling of null message bodies
Hi, I'm attempting to build a resilient client using the protonj2 client and as part of my testing I was checking that it would handle messages with null bodies (specifically my client is only willing to work with strings or byte arrays so it rejects any other AMQP message body type). However, when I put a null body on the message (i.e. body length of 0), when I de-reference the delivery's message's body property (e.g. delivery.message().body) I receive an error from the proton engine: Caused by: org.apache.qpid.protonj2.client.exceptions.ClientException: Index 1 out of bounds for length 1 at org.apache.qpid.protonj2.client.impl.ClientExceptionSupport.createNonFatalOrPassthrough( ClientExceptionSupport.java:103) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:170) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:152) at org.apache.qpid.protonj2.client.impl.ClientDelivery.message( ClientDelivery.java:79) at com.thisisnumero.smartagent.gateway.inbound.sources.background.amqp.AMQPMessageConsumerImpl.internalConsumeMessages( AMQPMessageConsumerImpl.java:53) ... 4 more Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1 at org.apache.qpid.protonj2.buffer.impl.ProtonCompositeBufferImpl.findChunkWithIndex( ProtonCompositeBufferImpl.java:1530) at org.apache.qpid.protonj2.buffer.impl.ProtonCompositeBufferImpl.copyInto( ProtonCompositeBufferImpl.java:387) at org.apache.qpid.protonj2.codec.decoders.messaging.DataTypeDecoder.readValue( DataTypeDecoder.java:85) at org.apache.qpid.protonj2.codec.decoders.messaging.DataTypeDecoder.readValue( DataTypeDecoder.java:40) at org.apache.qpid.protonj2.codec.decoders.ProtonDecoder.readObject( ProtonDecoder.java:192) at org.apache.qpid.protonj2.client.impl.ClientMessageSupport.decodeMessage( ClientMessageSupport.java:168) ... 7 more My expectation from the comments on the 'body()' method is that I should expect it to return null in this scenario? Should I be trapping this exception and rejecting the messages to avoid poisoning my queue? (I was planning on rejecting them anyway, but it seems weird to have to catch the exception first.) For reference, I'm putting messages onto the queue with the following protonj2 calls: sender.send(Message.create((byte[])null)); sender.send(Message.create((*String*)null)); Thank you (and sorry for the bombardment of things)
RE: [QPID PROTON PYTHON] How to send messages in loop within the same link or connection.
Hi Ted. Thank you so much! It's working fine with this timer. Sylvain -Original Message- From: Ted Ross Sent: Thursday, March 14, 2024 4:41 PM To: users@qpid.apache.org Subject: Re: [QPID PROTON PYTHON] How to send messages in loop within the same link or connection. Richard, If you look in the Python examples directory, you'll find an example called recurring_timer.py. This shows how you can set up a recurring timer that fires every 4 seconds. When the timer fires, you can send your 30 messages in a loop. I would recommend checking the available credit on the sender (sender.credit I think) and to not send any more messages than you have credit for. You should never write an infinite loop, nor should you ever call a blocking "sleep" function. HTH -Ted On Thu, Mar 14, 2024 at 10:48 AM Richard Sylvain < sylvain.rich...@skyguide.ch> wrote: > Hi all. > I would like to know how to send messages in an infinite loop within > the same producer (link). Or at least the same connection. > > I would like to send 30 messages then sleep 4 seconds in loop. > > I am not specialist of event programming. Could you share a sample please. > > As in the QPID proton python example. > I start my proton container with a sender MessagingHandler. > I create my AMQP1.0 connection and my sender (link) on the on_start method. > Then I send all the messages in the on_sendable method. > > I observe that the events on_accepted or on_rejected are not called > until the on_sendable method is completed. > > So I don't know how to do an infinite loop! > > Thank you for helping me. >