-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathasyncio.py
More file actions
825 lines (740 loc) · 32.7 KB
/
asyncio.py
File metadata and controls
825 lines (740 loc) · 32.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# pylint: disable=no-name-in-module,c-extension-no-member,protected-access
"""
The Pulsar Python client APIs that work with the asyncio module.
"""
import asyncio
import functools
from datetime import timedelta
from typing import Any, Callable, List, Union
import _pulsar
from _pulsar import (
InitialPosition,
CompressionType,
PartitionsRoutingMode,
BatchingType,
ProducerAccessMode,
RegexSubscriptionMode,
ConsumerCryptoFailureAction,
)
import pulsar
from pulsar import _check_type
class PulsarException(BaseException):
"""
The exception that wraps the Pulsar error code
"""
def __init__(self, result: pulsar.Result) -> None:
"""
Create the Pulsar exception.
Parameters
----------
result: pulsar.Result
The error code of the underlying Pulsar APIs.
"""
self._result = result
def error(self) -> pulsar.Result:
"""
Returns the Pulsar error code.
"""
return self._result
def __str__(self):
"""
Convert the exception to string.
"""
return f'{self._result.value} {self._result.name}'
class Producer:
"""
The Pulsar message producer, used to publish messages on a topic.
"""
def __init__(self, producer: _pulsar.Producer, schema: pulsar.schema.Schema) -> None:
"""
Create the producer.
Users should not call this constructor directly. Instead, create the
producer via `Client.create_producer`.
Parameters
----------
producer: _pulsar.Producer
The underlying Producer object from the C extension.
schema: pulsar.schema.Schema
The schema of the data that will be sent by this producer.
"""
self._producer = producer
self._schema = schema
# pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
async def send(self, content: Any,
properties: dict | None = None,
partition_key: str | None = None,
ordering_key: str | None = None,
sequence_id: int | None = None,
replication_clusters: List[str] | None = None,
disable_replication: bool | None = None,
event_timestamp: int | None = None,
deliver_at: int | None = None,
deliver_after: timedelta | None = None) -> pulsar.MessageId:
"""
Send a message asynchronously.
parameters
----------
content: Any
The message payload, whose type should respect the schema defined in
`Client.create_producer`.
properties: dict | None
A dict of application-defined string properties.
partition_key: str | None
Sets the partition key for the message routing. A hash of this key is
used to determine the message's topic partition.
ordering_key: str | None
Sets the ordering key for the message routing.
sequence_id: int | None
Specify a custom sequence id for the message being published.
replication_clusters: List[str] | None
Override namespace replication clusters. Note that it is the caller's responsibility
to provide valid cluster names and that all clusters have been previously configured
as topics. Given an empty list, the message will replicate per the namespace
configuration.
disable_replication: bool | None
Do not replicate this message.
event_timestamp: int | None
Timestamp in millis of the timestamp of event creation
deliver_at: int | None
Specify the message should not be delivered earlier than the specified timestamp.
deliver_after: timedelta | None
Specify a delay in timedelta for the delivery of the messages.
Returns
-------
pulsar.MessageId
The message id that represents the persisted position of the message.
Raises
------
PulsarException
"""
builder = _pulsar.MessageBuilder()
builder.content(self._schema.encode(content))
if properties is not None:
for k, v in properties.items():
builder.property(k, v)
if partition_key is not None:
builder.partition_key(partition_key)
if ordering_key is not None:
builder.ordering_key(ordering_key)
if sequence_id is not None:
builder.sequence_id(sequence_id)
if replication_clusters is not None:
builder.replication_clusters(replication_clusters)
if disable_replication is not None:
builder.disable_replication(disable_replication)
if event_timestamp is not None:
builder.event_timestamp(event_timestamp)
if deliver_at is not None:
builder.deliver_at(deliver_at)
if deliver_after is not None:
builder.deliver_after(deliver_after)
future = asyncio.get_running_loop().create_future()
self._producer.send_async(builder.build(), functools.partial(_set_future, future))
msg_id = await future
return pulsar.MessageId(
msg_id.partition(),
msg_id.ledger_id(),
msg_id.entry_id(),
msg_id.batch_index(),
)
async def flush(self) -> None:
"""
Flush all the messages buffered in the producer asynchronously.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._producer.flush_async(functools.partial(_set_future, future, value=None))
await future
async def close(self) -> None:
"""
Close the producer.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._producer.close_async(functools.partial(_set_future, future, value=None))
await future
def topic(self):
"""
Return the topic which producer is publishing to
"""
return self._producer.topic()
def producer_name(self):
"""
Return the producer name which could have been assigned by the
system or specified by the client
"""
return self._producer.producer_name()
def last_sequence_id(self):
"""
Return the last sequence id that was published and acknowledged by this producer.
The sequence id can be either automatically assigned or custom set on the message.
After recreating a producer with the same name, this will return the sequence id
of the last message that was published in the previous session, or -1 if no
message was ever published.
"""
return self._producer.last_sequence_id()
def is_connected(self) -> bool:
"""
Check if the producer is connected or not.
"""
return self._producer.is_connected()
class Consumer:
"""
The Pulsar message consumer, used to subscribe to messages from a topic.
"""
def __init__(self, consumer: _pulsar.Consumer, schema: pulsar.schema.Schema) -> None:
"""
Create the consumer.
Users should not call this constructor directly. Instead, create the
consumer via `Client.subscribe`.
Parameters
----------
consumer: _pulsar.Consumer
The underlying Consumer object from the C extension.
schema: pulsar.schema.Schema
The schema of the data that will be received by this consumer.
"""
self._consumer = consumer
self._schema = schema
async def receive(self) -> pulsar.Message:
"""
Receive a single message asynchronously.
Returns
-------
pulsar.Message
The message received.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._consumer.receive_async(functools.partial(_set_future, future))
msg = await future
m = pulsar.Message()
m._message = msg
m._schema = self._schema
return m
async def acknowledge(
self,
message: Union[pulsar.Message, pulsar.MessageId,
_pulsar.Message, _pulsar.MessageId]
) -> None:
"""
Acknowledge the reception of a single message asynchronously.
Parameters
----------
message : Message, MessageId, _pulsar.Message, _pulsar.MessageId
The received message or message id.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
if isinstance(message, pulsar.Message):
msg = message._message
elif isinstance(message, pulsar.MessageId):
msg = message._msg_id
else:
msg = message
self._consumer.acknowledge_async(msg, functools.partial(_set_future, future, value=None))
await future
async def acknowledge_cumulative(
self,
message: Union[pulsar.Message, pulsar.MessageId,
_pulsar.Message, _pulsar.MessageId]
) -> None:
"""
Acknowledge the reception of all the messages in the stream up to (and
including) the provided message asynchronously.
Parameters
----------
message : Message, MessageId, _pulsar.Message, _pulsar.MessageId
The received message or message id.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
if isinstance(message, pulsar.Message):
msg = message._message
elif isinstance(message, pulsar.MessageId):
msg = message._msg_id
else:
msg = message
self._consumer.acknowledge_cumulative_async(
msg, functools.partial(_set_future, future, value=None)
)
await future
async def negative_acknowledge(
self,
message: Union[pulsar.Message, pulsar.MessageId, _pulsar.Message, _pulsar.MessageId]
) -> None:
"""
Acknowledge the failure to process a single message asynchronously.
When a message is "negatively acked" it will be marked for redelivery after
some fixed delay. The delay is configurable when constructing the consumer
with {@link ConsumerConfiguration#setNegativeAckRedeliveryDelayMs}.
This call is not blocking.
Parameters
----------
message:
The received message or message id.
"""
if isinstance(message, pulsar.Message):
msg = message._message
elif isinstance(message, pulsar.MessageId):
msg = message._msg_id
else:
msg = message
await asyncio.to_thread(self._consumer.negative_acknowledge, msg)
async def unsubscribe(self) -> None:
"""
Unsubscribe the current consumer from the topic asynchronously.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._consumer.unsubscribe_async(functools.partial(_set_future, future, value=None))
await future
async def seek(self, messageid: Union[pulsar.MessageId, int]) -> None:
"""
Reset the subscription associated with this consumer to a specific
message id or publish timestamp asynchronously.
The message id can either be a specific message or represent the first
or last messages in the topic.
Parameters
----------
messageid : MessageId or int
The message id for seek, OR an integer event time (timestamp) to
seek to
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
if isinstance(messageid, pulsar.MessageId):
msg_id = messageid._msg_id
elif isinstance(messageid, int):
msg_id = messageid
else:
raise ValueError(f"invalid messageid type {type(messageid)}")
self._consumer.seek_async(
msg_id, functools.partial(_set_future, future, value=None)
)
await future
async def close(self) -> None:
"""
Close the consumer asynchronously.
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._consumer.close_async(functools.partial(_set_future, future, value=None))
await future
async def get_last_message_id(self) -> _pulsar.MessageId:
"""
Asynchronously get the last message id.
"""
future = asyncio.get_running_loop().create_future()
self._consumer.get_last_message_id_async(functools.partial(_set_future, future))
id = await future
return id
def redeliver_unacknowledged_messages(self):
"""
Redelivers all the unacknowledged messages. In failover mode, the
request is ignored if the consumer is not active for the given topic. In
shared mode, the consumer's messages to be redelivered are distributed
across all the connected consumers. This is a non-blocking call and
doesn't throw an exception. In case the connection breaks, the messages
are redelivered after reconnect.
"""
self._consumer.redeliver_unacknowledged_messages()
def topic(self) -> str:
"""
Return the topic this consumer is subscribed to.
"""
return self._consumer.topic()
def subscription_name(self) -> str:
"""
Return the subscription name.
"""
return self._consumer.subscription_name()
def consumer_name(self) -> str:
"""
Return the consumer name.
"""
return self._consumer.consumer_name()
class Client:
"""
The asynchronous version of `pulsar.Client`.
"""
def __init__(self, service_url, **kwargs) -> None:
"""
See `pulsar.Client.__init__`
"""
self._client: _pulsar.Client = pulsar.Client(service_url, **kwargs)._client
# pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
async def create_producer(self, topic: str,
producer_name: str | None = None,
schema: pulsar.schema.Schema | None = None,
initial_sequence_id: int | None = None,
send_timeout_millis: int = 30000,
compression_type: CompressionType = CompressionType.NONE,
max_pending_messages: int = 1000,
max_pending_messages_across_partitions: int = 50000,
block_if_queue_full: bool = False,
batching_enabled: bool = True,
batching_max_messages: int = 1000,
batching_max_allowed_size_in_bytes: int = 128*1024,
batching_max_publish_delay_ms: int = 10,
chunking_enabled: bool = False,
message_routing_mode: PartitionsRoutingMode =
PartitionsRoutingMode.RoundRobinDistribution,
lazy_start_partitioned_producers: bool = False,
properties: dict | None = None,
batching_type: BatchingType = BatchingType.Default,
encryption_key: str | None = None,
crypto_key_reader: pulsar.CryptoKeyReader | None = None,
access_mode: ProducerAccessMode = ProducerAccessMode.Shared,
message_router: Callable[[pulsar.Message, int], int] | None = None,
) -> Producer:
"""
Create a new producer on a given topic
Parameters
----------
topic: str
The topic name
producer_name: str | None
Specify a name for the producer. If not assigned, the system will generate a globally
unique name which can be accessed with `Producer.producer_name()`. When specifying a
name, it is up to the user to ensure that, for a given topic, the producer name is
unique across all Pulsar's clusters.
schema: pulsar.schema.Schema | None, default=None
Define the schema of the data that will be published by this producer.
initial_sequence_id: int | None, default=None
Set the baseline for the sequence ids for messages published by
the producer.
send_timeout_millis: int, default=30000
If a message is not acknowledged by the server before the
send_timeout expires, an error will be reported.
compression_type: CompressionType, default=CompressionType.NONE
Set the compression type for the producer.
max_pending_messages: int, default=1000
Set the max size of the queue holding the messages pending to
receive an acknowledgment from the broker.
max_pending_messages_across_partitions: int, default=50000
Set the max size of the queue holding the messages pending to
receive an acknowledgment across partitions.
block_if_queue_full: bool, default=False
Set whether send operations should block when the outgoing
message queue is full.
batching_enabled: bool, default=True
Enable automatic message batching. Note that, unlike the synchronous producer API in
``pulsar.Client.create_producer``, batching is enabled by default for the asyncio
producer.
batching_max_messages: int, default=1000
Maximum number of messages in a batch.
batching_max_allowed_size_in_bytes: int, default=128*1024
Maximum size in bytes of a batch.
batching_max_publish_delay_ms: int, default=10
The batch interval in milliseconds.
chunking_enabled: bool, default=False
Enable chunking of large messages.
message_routing_mode: PartitionsRoutingMode,
default=PartitionsRoutingMode.RoundRobinDistribution
Set the message routing mode for the partitioned producer.
lazy_start_partitioned_producers: bool, default=False
Start partitioned producers lazily on demand.
properties: dict | None, default=None
Sets the properties for the producer.
batching_type: BatchingType, default=BatchingType.Default
Sets the batching type for the producer.
encryption_key: str | None, default=None
The key used for symmetric encryption.
crypto_key_reader: pulsar.CryptoKeyReader | None, default=None
Symmetric encryption class implementation.
access_mode: ProducerAccessMode, default=ProducerAccessMode.Shared
Set the type of access mode that the producer requires on the topic.
message_router: Callable[[pulsar.Message, int], int] | None, default=None
A custom message router function that takes a Message and the
number of partitions and returns the partition index.
Returns
-------
Producer
The producer created
Raises
------
PulsarException
"""
if batching_enabled and chunking_enabled:
raise ValueError("Batching and chunking of messages can't be enabled together.")
if schema is None:
schema = pulsar.schema.BytesSchema()
schema.attach_client(self._client)
future = asyncio.get_running_loop().create_future()
conf = _pulsar.ProducerConfiguration()
if producer_name is not None:
conf.producer_name(producer_name)
conf.schema(schema.schema_info())
if initial_sequence_id is not None:
conf.initial_sequence_id(initial_sequence_id)
conf.send_timeout_millis(send_timeout_millis)
conf.compression_type(compression_type)
conf.max_pending_messages(max_pending_messages)
conf.max_pending_messages_across_partitions(max_pending_messages_across_partitions)
conf.block_if_queue_full(block_if_queue_full)
conf.batching_enabled(batching_enabled)
conf.batching_max_messages(batching_max_messages)
conf.batching_max_allowed_size_in_bytes(batching_max_allowed_size_in_bytes)
conf.batching_max_publish_delay_ms(batching_max_publish_delay_ms)
conf.chunking_enabled(chunking_enabled)
conf.partitions_routing_mode(message_routing_mode)
conf.lazy_start_partitioned_producers(lazy_start_partitioned_producers)
if properties is not None:
for k, v in properties.items():
conf.property(k, v)
conf.batching_type(batching_type)
if encryption_key is not None:
conf.encryption_key(encryption_key)
if crypto_key_reader is not None:
conf.crypto_key_reader(crypto_key_reader.cryptoKeyReader)
conf.access_mode(access_mode)
if message_router is not None:
def underlying_router(msg: _pulsar.Message, num_partitions: int) -> int:
return message_router(pulsar.Message._wrap(msg), num_partitions)
conf.message_router(underlying_router)
self._client.create_producer_async(
topic, conf, functools.partial(_set_future, future)
)
return Producer(await future, schema)
# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-positional-arguments
async def subscribe(self, topic: Union[str, List[str]],
subscription_name: str,
consumer_type: pulsar.ConsumerType =
pulsar.ConsumerType.Exclusive,
schema: pulsar.schema.Schema | None = None,
receiver_queue_size: int = 1000,
max_total_receiver_queue_size_across_partitions: int =
50000,
consumer_name: str | None = None,
unacked_messages_timeout_ms: int | None = None,
broker_consumer_stats_cache_time_ms: int = 30000,
negative_ack_redelivery_delay_ms: int = 60000,
is_read_compacted: bool = False,
properties: dict | None = None,
initial_position: InitialPosition = InitialPosition.Latest,
crypto_key_reader: pulsar.CryptoKeyReader | None = None,
replicate_subscription_state_enabled: bool = False,
max_pending_chunked_message: int = 10,
auto_ack_oldest_chunked_message_on_queue_full: bool = False,
start_message_id_inclusive: bool = False,
batch_receive_policy: pulsar.ConsumerBatchReceivePolicy | None =
None,
key_shared_policy: pulsar.ConsumerKeySharedPolicy | None =
None,
batch_index_ack_enabled: bool = False,
regex_subscription_mode: RegexSubscriptionMode =
RegexSubscriptionMode.PersistentOnly,
dead_letter_policy: pulsar.ConsumerDeadLetterPolicy | None =
None,
crypto_failure_action: ConsumerCryptoFailureAction =
ConsumerCryptoFailureAction.FAIL,
is_pattern_topic: bool = False) -> Consumer:
"""
Subscribe to the given topic and subscription combination.
Parameters
----------
topic: str, List[str], or regex pattern
The name of the topic, list of topics or regex pattern.
When `is_pattern_topic` is True, `topic` is treated as a regex.
subscription_name: str
The name of the subscription.
consumer_type: pulsar.ConsumerType, default=pulsar.ConsumerType.Exclusive
Select the subscription type to be used when subscribing to the topic.
schema: pulsar.schema.Schema | None, default=None
Define the schema of the data that will be received by this consumer.
receiver_queue_size: int, default=1000
Sets the size of the consumer receive queue.
max_total_receiver_queue_size_across_partitions: int, default=50000
Set the max total receiver queue size across partitions.
consumer_name: str | None, default=None
Sets the consumer name.
unacked_messages_timeout_ms: int | None, default=None
Sets the timeout in milliseconds for unacknowledged messages.
broker_consumer_stats_cache_time_ms: int, default=30000
Sets the time duration for which the broker-side consumer stats
will be cached in the client.
negative_ack_redelivery_delay_ms: int, default=60000
The delay after which to redeliver the messages that failed to be
processed.
is_read_compacted: bool, default=False
Selects whether to read the compacted version of the topic.
properties: dict | None, default=None
Sets the properties for the consumer.
initial_position: InitialPosition, default=InitialPosition.Latest
Set the initial position of a consumer when subscribing to the topic.
crypto_key_reader: pulsar.CryptoKeyReader | None, default=None
Symmetric encryption class implementation.
replicate_subscription_state_enabled: bool, default=False
Set whether the subscription status should be replicated.
max_pending_chunked_message: int, default=10
Consumer buffers chunk messages into memory until it receives all the chunks.
auto_ack_oldest_chunked_message_on_queue_full: bool, default=False
Automatically acknowledge oldest chunked messages on queue
full.
start_message_id_inclusive: bool, default=False
Set the consumer to include the given position of any reset
operation.
batch_receive_policy: pulsar.ConsumerBatchReceivePolicy | None, default=None
Set the batch collection policy for batch receiving.
key_shared_policy: pulsar.ConsumerKeySharedPolicy | None, default=None
Set the key shared policy for use when the ConsumerType is
KeyShared.
batch_index_ack_enabled: bool, default=False
Enable the batch index acknowledgement.
regex_subscription_mode: RegexSubscriptionMode,
default=RegexSubscriptionMode.PersistentOnly
Set the regex subscription mode for use when the topic is a regex
pattern.
dead_letter_policy: pulsar.ConsumerDeadLetterPolicy | None, default=None
Set dead letter policy for consumer.
crypto_failure_action: ConsumerCryptoFailureAction,
default=ConsumerCryptoFailureAction.FAIL
Set the behavior when the decryption fails.
is_pattern_topic: bool, default=False
Whether `topic` is a regex pattern. If it's True when `topic` is a list, a ValueError
will be raised.
Returns
-------
Consumer
The consumer created
Raises
------
PulsarException
"""
if schema is None:
schema = pulsar.schema.BytesSchema()
future = asyncio.get_running_loop().create_future()
conf = _pulsar.ConsumerConfiguration()
conf.consumer_type(consumer_type)
conf.regex_subscription_mode(regex_subscription_mode)
conf.read_compacted(is_read_compacted)
conf.receiver_queue_size(receiver_queue_size)
conf.max_total_receiver_queue_size_across_partitions(
max_total_receiver_queue_size_across_partitions
)
if consumer_name:
conf.consumer_name(consumer_name)
if unacked_messages_timeout_ms:
conf.unacked_messages_timeout_ms(unacked_messages_timeout_ms)
conf.negative_ack_redelivery_delay_ms(negative_ack_redelivery_delay_ms)
conf.broker_consumer_stats_cache_time_ms(broker_consumer_stats_cache_time_ms)
if properties:
for k, v in properties.items():
conf.property(k, v)
conf.subscription_initial_position(initial_position)
conf.schema(schema.schema_info())
if crypto_key_reader:
conf.crypto_key_reader(crypto_key_reader.cryptoKeyReader)
conf.replicate_subscription_state_enabled(replicate_subscription_state_enabled)
conf.max_pending_chunked_message(max_pending_chunked_message)
conf.auto_ack_oldest_chunked_message_on_queue_full(
auto_ack_oldest_chunked_message_on_queue_full
)
conf.start_message_id_inclusive(start_message_id_inclusive)
if batch_receive_policy:
conf.batch_receive_policy(batch_receive_policy.policy())
if key_shared_policy:
conf.key_shared_policy(key_shared_policy.policy())
conf.batch_index_ack_enabled(batch_index_ack_enabled)
if dead_letter_policy:
conf.dead_letter_policy(dead_letter_policy.policy())
conf.crypto_failure_action(crypto_failure_action)
if isinstance(topic, str):
if is_pattern_topic:
self._client.subscribe_async_pattern(
topic, subscription_name, conf,
functools.partial(_set_future, future)
)
else:
self._client.subscribe_async(
topic, subscription_name, conf,
functools.partial(_set_future, future)
)
elif isinstance(topic, list):
if is_pattern_topic:
raise ValueError(
"Argument 'topic' must be a string when "
"'is_pattern_topic' is True; lists of topics do not "
"support pattern subscriptions"
)
self._client.subscribe_async_topics(
topic, subscription_name, conf,
functools.partial(_set_future, future)
)
else:
raise ValueError( "Argument 'topic' is expected to be of type 'str' or 'list'")
schema.attach_client(self._client)
return Consumer(await future, schema)
async def get_topic_partitions(self, topic: str) -> List[str]:
"""
Get the list of partitions for a given topic in asynchronous mode.
If the topic is partitioned, this will return a list of partition names. If the topic is not partitioned, the returned list will contain the topic name itself.
This can be used to discover the partitions and create Reader, Consumer or Producer instances directly on a particular partition.
Parameters
----------
topic: str
the topic name to lookup
Returns
-------
list
a list of partition names
"""
_check_type(str, topic, 'topic')
future = asyncio.get_running_loop().create_future()
self._client.get_topic_partitions_async(topic, functools.partial(_set_future, future))
id = await future
return id
async def close(self) -> None:
"""
Close the client and all the associated producers and consumers
Raises
------
PulsarException
"""
future = asyncio.get_running_loop().create_future()
self._client.close_async(
functools.partial(_set_future, future, value=None)
)
await future
def _set_future(future: asyncio.Future, result: _pulsar.Result, value: Any):
def complete():
if result == _pulsar.Result.Ok:
future.set_result(value)
else:
future.set_exception(PulsarException(result))
future.get_loop().call_soon_threadsafe(complete)