18
18
*/
19
19
package org .apache .pulsar .client .impl ;
20
20
21
+ import static java .nio .charset .StandardCharsets .UTF_8 ;
21
22
import static java .util .UUID .randomUUID ;
22
23
import static org .apache .pulsar .broker .service .BrokerService .BROKER_SERVICE_CONFIGURATION_PATH ;
23
24
import static org .mockito .Mockito .any ;
30
31
import static org .mockito .Mockito .verify ;
31
32
import static org .testng .Assert .assertEquals ;
32
33
import static org .testng .Assert .assertFalse ;
34
+ import static org .testng .Assert .assertNotEquals ;
33
35
import static org .testng .Assert .assertNotNull ;
34
36
import static org .testng .Assert .assertNull ;
35
37
import static org .testng .Assert .assertTrue ;
36
38
import static org .testng .Assert .fail ;
37
-
39
+ import io . netty . buffer . ByteBuf ;
38
40
import java .lang .reflect .Field ;
41
+ import java .nio .ByteBuffer ;
39
42
import java .nio .charset .StandardCharsets ;
40
43
import java .security .GeneralSecurityException ;
41
44
import java .util .ArrayList ;
55
58
56
59
import com .fasterxml .jackson .databind .ObjectMapper ;
57
60
import lombok .Cleanup ;
58
-
59
61
import lombok .EqualsAndHashCode ;
60
62
import lombok .Getter ;
61
63
import lombok .Setter ;
@@ -132,6 +134,11 @@ public Object[][] subType() {
132
134
return new Object [][] { { SubscriptionType .Shared }, { SubscriptionType .Failover } };
133
135
}
134
136
137
+ @ DataProvider (name = "booleanFlagProvider" )
138
+ public Object [][] booleanFlagProvider () {
139
+ return new Object [][] { { true }, { false } };
140
+ }
141
+
135
142
/**
136
143
* Verifies unload namespace-bundle doesn't close shared connection used by other namespace-bundle.
137
144
*
@@ -918,4 +925,98 @@ public void testJsonSchemaProducerConsumerWithSpecifiedReaderAndWriter() throws
918
925
private static final class TestMessageObject {
919
926
private String value ;
920
927
}
921
- }
928
+
929
+ /**
930
+ * It validates pooled message consumption for batch and non-batch messages.
931
+ *
932
+ * @throws Exception
933
+ */
934
+ @ Test (dataProvider = "booleanFlagProvider" )
935
+ public void testConsumerWithPooledMessages (boolean isBatchingEnabled ) throws Exception {
936
+ log .info ("-- Starting {} test --" , methodName );
937
+
938
+ @ Cleanup
939
+ PulsarClient newPulsarClient = PulsarClient .builder ().serviceUrl (lookupUrl .toString ()).build ();
940
+
941
+ final String topic = "persistent://my-property/my-ns/testConsumerWithPooledMessages" + isBatchingEnabled ;
942
+
943
+ @ Cleanup
944
+ Consumer <ByteBuffer > consumer = newPulsarClient .newConsumer (Schema .BYTEBUFFER ).topic (topic )
945
+ .subscriptionName ("my-sub" ).poolMessages (true ).subscribe ();
946
+
947
+ @ Cleanup
948
+ Producer <byte []> producer = newPulsarClient .newProducer ().topic (topic ).enableBatching (isBatchingEnabled ).create ();
949
+
950
+ final int numMessages = 100 ;
951
+ for (int i = 0 ; i < numMessages ; i ++) {
952
+ producer .newMessage ().value (("value-" + i ).getBytes (UTF_8 ))
953
+ .eventTime ((i + 1 ) * 100L ).sendAsync ();
954
+ }
955
+ producer .flush ();
956
+
957
+ // Reuse pre-allocated pooled buffer to process every message
958
+ byte [] val = null ;
959
+ int size = 0 ;
960
+ for (int i = 0 ; i < numMessages ; i ++) {
961
+ Message <ByteBuffer > msg = consumer .receive ();
962
+ ByteBuffer value ;
963
+ try {
964
+ value = msg .getValue ();
965
+ int capacity = value .remaining ();
966
+ // expand the size of buffer if needed
967
+ if (capacity > size ) {
968
+ val = new byte [capacity ];
969
+ size = capacity ;
970
+ }
971
+ // read message into pooled buffer
972
+ value .get (val , 0 , capacity );
973
+ // process the message
974
+ assertEquals (("value-" + i ), new String (val , 0 , capacity ));
975
+ } finally {
976
+ msg .release ();
977
+ }
978
+ }
979
+ consumer .close ();
980
+ producer .close ();
981
+ }
982
+
983
+ /**
984
+ * It verifies that expiry/redelivery of messages relesaes the messages without leak.
985
+ *
986
+ * @param isBatchingEnabled
987
+ * @throws Exception
988
+ */
989
+ @ Test (dataProvider = "booleanFlagProvider" )
990
+ public void testPooledMessageWithAckTimeout (boolean isBatchingEnabled ) throws Exception {
991
+ log .info ("-- Starting {} test --" , methodName );
992
+
993
+ @ Cleanup
994
+ PulsarClient newPulsarClient = PulsarClient .builder ().serviceUrl (lookupUrl .toString ()).build ();
995
+
996
+ final String topic = "persistent://my-property/my-ns/testPooledMessageWithAckTimeout" + isBatchingEnabled ;
997
+
998
+ @ Cleanup
999
+ ConsumerImpl <ByteBuffer > consumer = (ConsumerImpl <ByteBuffer >) newPulsarClient .newConsumer (Schema .BYTEBUFFER )
1000
+ .topic (topic ).subscriptionName ("my-sub" ).poolMessages (true ).subscribe ();
1001
+
1002
+ @ Cleanup
1003
+ Producer <byte []> producer = newPulsarClient .newProducer ().topic (topic ).enableBatching (isBatchingEnabled )
1004
+ .create ();
1005
+
1006
+ final int numMessages = 100 ;
1007
+ for (int i = 0 ; i < numMessages ; i ++) {
1008
+ producer .newMessage ().value (("value-" + i ).getBytes (UTF_8 )).eventTime ((i + 1 ) * 100L ).sendAsync ();
1009
+ }
1010
+ producer .flush ();
1011
+
1012
+ retryStrategically ((test ) -> consumer .incomingMessages .peek () != null , 5 , 500 );
1013
+ MessageImpl <ByteBuffer > msg = (MessageImpl ) consumer .incomingMessages .peek ();
1014
+ assertNotNull (msg );
1015
+ ByteBuf payload = ((MessageImpl ) msg ).getPayload ();
1016
+ assertNotEquals (payload .refCnt (), 0 );
1017
+ consumer .redeliverUnacknowledgedMessages ();
1018
+ assertEquals (payload .refCnt (), 0 );
1019
+ consumer .close ();
1020
+ producer .close ();
1021
+ }
1022
+ }
0 commit comments