qena-shared-lib 0.1.17__py3-none-any.whl → 0.1.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qena_shared_lib/__init__.py +3 -2
- qena_shared_lib/application.py +4 -4
- qena_shared_lib/background.py +9 -7
- qena_shared_lib/exception_handling.py +409 -0
- qena_shared_lib/exceptions.py +170 -57
- qena_shared_lib/http/__init__.py +90 -0
- qena_shared_lib/{http.py → http/_base.py} +36 -36
- qena_shared_lib/http/_exception_handlers.py +202 -0
- qena_shared_lib/kafka/__init__.py +21 -0
- qena_shared_lib/kafka/_base.py +233 -0
- qena_shared_lib/kafka/_consumer.py +597 -0
- qena_shared_lib/kafka/_exception_handlers.py +124 -0
- qena_shared_lib/kafka/_producer.py +133 -0
- qena_shared_lib/logging.py +17 -13
- qena_shared_lib/rabbitmq/__init__.py +4 -6
- qena_shared_lib/rabbitmq/_base.py +68 -132
- qena_shared_lib/rabbitmq/_channel.py +2 -4
- qena_shared_lib/rabbitmq/_exception_handlers.py +69 -142
- qena_shared_lib/rabbitmq/_listener.py +246 -157
- qena_shared_lib/rabbitmq/_publisher.py +5 -5
- qena_shared_lib/rabbitmq/_rpc_client.py +21 -22
- qena_shared_lib/remotelogging/_base.py +20 -20
- qena_shared_lib/remotelogging/logstash/_base.py +2 -2
- qena_shared_lib/remotelogging/logstash/_http_sender.py +2 -4
- qena_shared_lib/remotelogging/logstash/_tcp_sender.py +2 -2
- qena_shared_lib/scheduler.py +24 -15
- qena_shared_lib/security.py +39 -32
- qena_shared_lib/utils.py +13 -11
- {qena_shared_lib-0.1.17.dist-info → qena_shared_lib-0.1.18.dist-info}/METADATA +4 -1
- qena_shared_lib-0.1.18.dist-info/RECORD +38 -0
- qena_shared_lib/exception_handlers.py +0 -235
- qena_shared_lib-0.1.17.dist-info/RECORD +0 -31
- {qena_shared_lib-0.1.17.dist-info → qena_shared_lib-0.1.18.dist-info}/WHEEL +0 -0
@@ -30,8 +30,9 @@ from pydantic import ValidationError
|
|
30
30
|
from pydantic_core import from_json, to_json
|
31
31
|
|
32
32
|
from ..dependencies.miscellaneous import validate_annotation
|
33
|
-
from ..
|
34
|
-
from ..
|
33
|
+
from ..exception_handling import ServiceContext
|
34
|
+
from ..exceptions import RabbitMQBlockedError, RabbitMQServiceException
|
35
|
+
from ..logging import LoggerFactory
|
35
36
|
from ..remotelogging import BaseRemoteLogSender
|
36
37
|
from ..utils import AsyncEventLoopMixin, TypeAdapterCache
|
37
38
|
from ._channel import BaseChannel
|
@@ -97,21 +98,29 @@ class RpcReply:
|
|
97
98
|
self,
|
98
99
|
channel_pool: ChannelPool,
|
99
100
|
reply_to: str,
|
101
|
+
blocked_connection_check_callback: Callable[[], bool],
|
100
102
|
correlation_id: str | None = None,
|
101
103
|
) -> None:
|
102
104
|
self._channel_pool = channel_pool
|
103
105
|
self._reply_to = reply_to
|
106
|
+
self._blocked_connection_check_callback = (
|
107
|
+
blocked_connection_check_callback
|
108
|
+
)
|
104
109
|
self._correlation_id = correlation_id
|
105
110
|
self._replied = False
|
106
111
|
|
107
112
|
async def reply(self, message: Any) -> None:
|
108
113
|
base_channel = await self._channel_pool.get()
|
109
|
-
|
110
114
|
reply_properties = BasicProperties(content_type="application/json")
|
111
115
|
|
112
116
|
if self._correlation_id is not None:
|
113
117
|
reply_properties.correlation_id = self._correlation_id
|
114
118
|
|
119
|
+
if self._blocked_connection_check_callback():
|
120
|
+
raise RabbitMQBlockedError(
|
121
|
+
"rabbitmq broker is not able to accept message right now for manual reply"
|
122
|
+
)
|
123
|
+
|
115
124
|
try:
|
116
125
|
with base_channel as channel:
|
117
126
|
channel.basic_publish(
|
@@ -131,7 +140,7 @@ class RpcReply:
|
|
131
140
|
|
132
141
|
|
133
142
|
@dataclass
|
134
|
-
class ListenerContext:
|
143
|
+
class ListenerContext(ServiceContext):
|
135
144
|
queue: str
|
136
145
|
listener_name: str
|
137
146
|
body: bytes
|
@@ -218,6 +227,13 @@ class ListenerMethodContainer:
|
|
218
227
|
dependencies: dict[str, type]
|
219
228
|
retry_policy: RetryPolicy | None = None
|
220
229
|
|
230
|
+
def __post_init__(self) -> None:
|
231
|
+
self._is_async_listener = iscoroutinefunction(self.listener_method)
|
232
|
+
|
233
|
+
@property
|
234
|
+
def is_async_listener(self) -> bool:
|
235
|
+
return self._is_async_listener
|
236
|
+
|
221
237
|
|
222
238
|
class ListenerChannelAdapter(BaseChannel):
|
223
239
|
def __init__(
|
@@ -242,7 +258,7 @@ class ListenerChannelAdapter(BaseChannel):
|
|
242
258
|
|
243
259
|
|
244
260
|
@dataclass
|
245
|
-
class
|
261
|
+
class ListenerMessageMetadata:
|
246
262
|
body: bytes
|
247
263
|
method: Basic.Deliver
|
248
264
|
properties: BasicProperties
|
@@ -252,17 +268,17 @@ class ListenerMessageMeta:
|
|
252
268
|
|
253
269
|
|
254
270
|
class Listener(AsyncEventLoopMixin):
|
255
|
-
|
271
|
+
_LISTENER_SUCCEEDED_COMSUMPTION = Counter(
|
256
272
|
name="listener_succeeded_comsumption",
|
257
273
|
documentation="Listener succeeded comsumption",
|
258
274
|
labelnames=["queue", "listener_name"],
|
259
275
|
)
|
260
|
-
|
276
|
+
_LISTENER_FAILED_COMSUMPTION = Counter(
|
261
277
|
name="listener_failed_comsumption",
|
262
278
|
documentation="Listener failed comsumption",
|
263
279
|
labelnames=["queue", "listener_name", "exception"],
|
264
280
|
)
|
265
|
-
|
281
|
+
_LISTENER_PROCESSING_LATENCY = Summary(
|
266
282
|
name="listener_processing_latency",
|
267
283
|
documentation="Listener processing latency",
|
268
284
|
labelnames=["queue", "listener_name"],
|
@@ -287,7 +303,7 @@ class Listener(AsyncEventLoopMixin):
|
|
287
303
|
self._listeners_tasks_and_futures: list[Task[Any] | Future[Any]] = []
|
288
304
|
self._consumer_tag: str | None = None
|
289
305
|
self._cancelled = False
|
290
|
-
self._logger =
|
306
|
+
self._logger = LoggerFactory.get_logger("rabbitmq.listener")
|
291
307
|
|
292
308
|
@property
|
293
309
|
def queue(self) -> str:
|
@@ -356,6 +372,7 @@ class Listener(AsyncEventLoopMixin):
|
|
356
372
|
connection: AsyncioConnection,
|
357
373
|
channel_pool: ChannelPool,
|
358
374
|
on_exception_callback: Callable[[ListenerContext, BaseException], bool],
|
375
|
+
blocked_connection_check_callback: Callable[[], bool],
|
359
376
|
container: Container,
|
360
377
|
remote_logger: BaseRemoteLogSender,
|
361
378
|
global_retry_policy: RetryPolicy | None = None,
|
@@ -364,6 +381,9 @@ class Listener(AsyncEventLoopMixin):
|
|
364
381
|
self._channel_pool = channel_pool
|
365
382
|
self._listener_future = self.loop.create_future()
|
366
383
|
self._on_exception_callback = on_exception_callback
|
384
|
+
self._blocked_connection_check_callback = (
|
385
|
+
blocked_connection_check_callback
|
386
|
+
)
|
367
387
|
self._container = container
|
368
388
|
self._remote_logger = remote_logger
|
369
389
|
self._global_retry_policy = global_retry_policy
|
@@ -485,12 +505,13 @@ class Listener(AsyncEventLoopMixin):
|
|
485
505
|
self._remote_logger.error(
|
486
506
|
message=f"no listener registered with the name `{listener_name}` on queue `{self._queue}`",
|
487
507
|
tags=[
|
488
|
-
"
|
508
|
+
"rabbitmq",
|
509
|
+
"listener_doesnt_exist",
|
489
510
|
self._queue,
|
490
511
|
listener_name,
|
491
512
|
],
|
492
513
|
extra={
|
493
|
-
"serviceType": "
|
514
|
+
"serviceType": "rabbitmq",
|
494
515
|
"queue": self._queue,
|
495
516
|
"listenerName": listener_name,
|
496
517
|
},
|
@@ -498,7 +519,7 @@ class Listener(AsyncEventLoopMixin):
|
|
498
519
|
|
499
520
|
return
|
500
521
|
|
501
|
-
|
522
|
+
listener_message_metadata = ListenerMessageMetadata(
|
502
523
|
body=body,
|
503
524
|
method=method,
|
504
525
|
properties=properties,
|
@@ -509,13 +530,17 @@ class Listener(AsyncEventLoopMixin):
|
|
509
530
|
|
510
531
|
self.loop.run_in_executor(
|
511
532
|
executor=None,
|
512
|
-
func=partial(self._parse_and_execute,
|
533
|
+
func=partial(self._parse_and_execute, listener_message_metadata),
|
513
534
|
).add_done_callback(
|
514
|
-
partial(
|
535
|
+
partial(
|
536
|
+
self._on_submitted_listener_error, listener_message_metadata
|
537
|
+
)
|
515
538
|
)
|
516
539
|
|
517
540
|
def _on_submitted_listener_error(
|
518
|
-
self,
|
541
|
+
self,
|
542
|
+
listener_message_metadata: ListenerMessageMetadata,
|
543
|
+
future: Future[None],
|
519
544
|
) -> None:
|
520
545
|
if future.cancelled():
|
521
546
|
return
|
@@ -525,33 +550,31 @@ class Listener(AsyncEventLoopMixin):
|
|
525
550
|
if exception is not None:
|
526
551
|
self._call_exception_callback(
|
527
552
|
exception=exception,
|
528
|
-
|
529
|
-
message=f"error occured while submitting listener callback on listener `{
|
553
|
+
listener_message_metadata=listener_message_metadata,
|
554
|
+
message=f"error occured while submitting listener callback on listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`",
|
530
555
|
)
|
531
556
|
|
532
557
|
def _parse_and_execute(
|
533
|
-
self,
|
558
|
+
self, listener_message_metadata: ListenerMessageMetadata
|
534
559
|
) -> None:
|
535
560
|
try:
|
536
561
|
listener_method_args, listener_method_kwargs = self._parse_args(
|
537
|
-
|
562
|
+
listener_message_metadata
|
538
563
|
)
|
539
564
|
except Exception as e:
|
540
565
|
self._call_exception_callback(
|
541
566
|
exception=e,
|
542
|
-
|
543
|
-
message=f"arguments for listener `{
|
567
|
+
listener_message_metadata=listener_message_metadata,
|
568
|
+
message=f"arguments for listener `{listener_message_metadata.listener_name}` in queue `{self._queue}` are not valid",
|
544
569
|
)
|
545
570
|
|
546
571
|
return
|
547
572
|
|
548
573
|
listener_task_or_future: Task[Any] | Future[Any] | None = None
|
549
574
|
|
550
|
-
if
|
551
|
-
listener_message_meta.listener_method_container.listener_method
|
552
|
-
):
|
575
|
+
if listener_message_metadata.listener_method_container.is_async_listener:
|
553
576
|
listener_task_or_future = self.loop.create_task(
|
554
|
-
|
577
|
+
listener_message_metadata.listener_method_container.listener_method(
|
555
578
|
*listener_method_args, **listener_method_kwargs
|
556
579
|
)
|
557
580
|
)
|
@@ -559,7 +582,7 @@ class Listener(AsyncEventLoopMixin):
|
|
559
582
|
listener_task_or_future = self.loop.run_in_executor(
|
560
583
|
executor=None,
|
561
584
|
func=partial(
|
562
|
-
|
585
|
+
listener_message_metadata.listener_method_container.listener_method,
|
563
586
|
*listener_method_args,
|
564
587
|
**listener_method_kwargs,
|
565
588
|
),
|
@@ -569,16 +592,16 @@ class Listener(AsyncEventLoopMixin):
|
|
569
592
|
|
570
593
|
self._listeners_tasks_and_futures.append(listener_task_or_future)
|
571
594
|
listener_task_or_future.add_done_callback(
|
572
|
-
partial(self._on_listener_done_executing,
|
595
|
+
partial(self._on_listener_done_executing, listener_message_metadata)
|
573
596
|
)
|
574
597
|
|
575
598
|
def _parse_args(
|
576
|
-
self,
|
599
|
+
self, listener_message_metadata: ListenerMessageMetadata
|
577
600
|
) -> tuple[list[Any], dict[str, Any]]:
|
578
601
|
try:
|
579
|
-
message = from_json(
|
602
|
+
message = from_json(listener_message_metadata.body)
|
580
603
|
except:
|
581
|
-
message =
|
604
|
+
message = listener_message_metadata.body.decode()
|
582
605
|
|
583
606
|
assigned_args: list[Any] = []
|
584
607
|
listener_method_args = []
|
@@ -593,8 +616,8 @@ class Listener(AsyncEventLoopMixin):
|
|
593
616
|
for (
|
594
617
|
parameter_name,
|
595
618
|
parameter,
|
596
|
-
) in
|
597
|
-
dependency_key =
|
619
|
+
) in listener_message_metadata.listener_method_container.parameters.items():
|
620
|
+
dependency_key = listener_message_metadata.listener_method_container.dependencies.get(
|
598
621
|
parameter_name
|
599
622
|
)
|
600
623
|
dependency = None
|
@@ -609,8 +632,8 @@ class Listener(AsyncEventLoopMixin):
|
|
609
632
|
):
|
610
633
|
listener_context = ListenerContext(
|
611
634
|
queue=self._queue,
|
612
|
-
listener_name=
|
613
|
-
body=
|
635
|
+
listener_name=listener_message_metadata.listener_name,
|
636
|
+
body=listener_message_metadata.body,
|
614
637
|
flow_control=self._flow_control,
|
615
638
|
)
|
616
639
|
|
@@ -709,8 +732,8 @@ class Listener(AsyncEventLoopMixin):
|
|
709
732
|
for (
|
710
733
|
parameter_name,
|
711
734
|
parameter,
|
712
|
-
) in
|
713
|
-
dependency =
|
735
|
+
) in listener_message_metadata.listener_method_container.parameters.items():
|
736
|
+
dependency = listener_message_metadata.listener_method_container.dependencies.get(
|
714
737
|
parameter_name
|
715
738
|
)
|
716
739
|
|
@@ -721,8 +744,8 @@ class Listener(AsyncEventLoopMixin):
|
|
721
744
|
listener_method_args.append(
|
722
745
|
ListenerContext(
|
723
746
|
queue=self._queue,
|
724
|
-
listener_name=
|
725
|
-
body=
|
747
|
+
listener_name=listener_message_metadata.listener_name,
|
748
|
+
body=listener_message_metadata.body,
|
726
749
|
flow_control=self._flow_control,
|
727
750
|
)
|
728
751
|
)
|
@@ -773,7 +796,7 @@ class Listener(AsyncEventLoopMixin):
|
|
773
796
|
|
774
797
|
def _on_listener_done_executing(
|
775
798
|
self,
|
776
|
-
|
799
|
+
listener_message_metadata: ListenerMessageMetadata,
|
777
800
|
task_or_future: Task[Any] | Future[Any],
|
778
801
|
) -> None:
|
779
802
|
if (
|
@@ -785,14 +808,14 @@ class Listener(AsyncEventLoopMixin):
|
|
785
808
|
if task_or_future.cancelled():
|
786
809
|
return
|
787
810
|
|
788
|
-
self._observe_listener_time(
|
811
|
+
self._observe_listener_time(listener_message_metadata)
|
789
812
|
|
790
813
|
exception = task_or_future.exception()
|
791
814
|
|
792
815
|
if exception is not None:
|
793
|
-
if
|
816
|
+
if listener_message_metadata.properties.reply_to is None:
|
794
817
|
retry_policy = (
|
795
|
-
|
818
|
+
listener_message_metadata.listener_method_container.retry_policy
|
796
819
|
or self._retry_policy
|
797
820
|
or self._global_retry_policy
|
798
821
|
)
|
@@ -812,12 +835,10 @@ class Listener(AsyncEventLoopMixin):
|
|
812
835
|
):
|
813
836
|
times_rejected = None
|
814
837
|
|
815
|
-
if
|
838
|
+
if listener_message_metadata.properties.headers is not None:
|
816
839
|
try:
|
817
|
-
_times_rejected = (
|
818
|
-
|
819
|
-
"times_rejected"
|
820
|
-
)
|
840
|
+
_times_rejected = listener_message_metadata.properties.headers.get(
|
841
|
+
"times_rejected"
|
821
842
|
)
|
822
843
|
|
823
844
|
if isinstance(_times_rejected, int):
|
@@ -835,7 +856,7 @@ class Listener(AsyncEventLoopMixin):
|
|
835
856
|
|
836
857
|
if retry_policy.can_retry(times_rejected):
|
837
858
|
self._reject_message(
|
838
|
-
|
859
|
+
listener_message_metadata=listener_message_metadata,
|
839
860
|
retry_policy=retry_policy,
|
840
861
|
times_rejected=times_rejected,
|
841
862
|
)
|
@@ -844,43 +865,44 @@ class Listener(AsyncEventLoopMixin):
|
|
844
865
|
|
845
866
|
self._call_exception_callback(
|
846
867
|
exception=exception,
|
847
|
-
|
848
|
-
message=f"error occured while executing listener `{
|
868
|
+
listener_message_metadata=listener_message_metadata,
|
869
|
+
message=f"error occured while executing listener `{listener_message_metadata.listener_name}` in queue `{self._queue}`",
|
849
870
|
)
|
850
871
|
|
851
872
|
if (
|
852
|
-
|
873
|
+
listener_message_metadata.properties.reply_to is not None
|
853
874
|
and exception is None
|
854
875
|
):
|
855
876
|
self._reply_response(
|
856
|
-
|
877
|
+
listener_message_metadata=listener_message_metadata,
|
857
878
|
response=task_or_future.result(),
|
858
879
|
)
|
859
880
|
|
860
881
|
self._logger.debug(
|
861
882
|
"message from queue `%s` consumed by listener `%s`",
|
862
883
|
self.queue,
|
863
|
-
|
884
|
+
listener_message_metadata.listener_name,
|
864
885
|
)
|
865
886
|
|
866
887
|
if exception is not None:
|
867
|
-
self.
|
888
|
+
self._LISTENER_FAILED_COMSUMPTION.labels(
|
868
889
|
queue=self._queue,
|
869
|
-
listener_name=
|
890
|
+
listener_name=listener_message_metadata.listener_name,
|
870
891
|
exception=exception.__class__.__name__,
|
871
892
|
).inc()
|
872
893
|
else:
|
873
|
-
self.
|
894
|
+
self._LISTENER_SUCCEEDED_COMSUMPTION.labels(
|
874
895
|
queue=self._queue,
|
875
|
-
listener_name=
|
896
|
+
listener_name=listener_message_metadata.listener_name,
|
876
897
|
).inc()
|
877
898
|
|
878
899
|
def _observe_listener_time(
|
879
|
-
self,
|
900
|
+
self, listener_message_metadata: ListenerMessageMetadata
|
880
901
|
) -> None:
|
881
|
-
self.
|
882
|
-
queue=self._queue,
|
883
|
-
|
902
|
+
self._LISTENER_PROCESSING_LATENCY.labels(
|
903
|
+
queue=self._queue,
|
904
|
+
listener_name=listener_message_metadata.listener_name,
|
905
|
+
).observe(listener_message_metadata.listener_start_time - time())
|
884
906
|
|
885
907
|
def _is_recoverable_exception(
|
886
908
|
self,
|
@@ -905,7 +927,7 @@ class Listener(AsyncEventLoopMixin):
|
|
905
927
|
):
|
906
928
|
return True
|
907
929
|
|
908
|
-
cause = cause.__cause__ or
|
930
|
+
cause = cause.__cause__ or cause.__context__
|
909
931
|
|
910
932
|
return False
|
911
933
|
|
@@ -922,84 +944,86 @@ class Listener(AsyncEventLoopMixin):
|
|
922
944
|
def _call_exception_callback(
|
923
945
|
self,
|
924
946
|
exception: BaseException,
|
925
|
-
|
947
|
+
listener_message_metadata: ListenerMessageMetadata,
|
926
948
|
message: str | None = None,
|
927
949
|
) -> None:
|
928
950
|
context_dispose_callback = None
|
929
951
|
rpc_reply = None
|
952
|
+
tags = [
|
953
|
+
"rabbitmq",
|
954
|
+
self._queue,
|
955
|
+
listener_message_metadata.listener_name,
|
956
|
+
]
|
957
|
+
extra = {
|
958
|
+
"serviceType": "rabbitmq",
|
959
|
+
"queue": self._queue,
|
960
|
+
"listenerName": listener_message_metadata.listener_name,
|
961
|
+
"section": "exceptionHandlerCallback",
|
962
|
+
"raisedException": exception.__class__.__name__,
|
963
|
+
}
|
930
964
|
|
931
|
-
if
|
965
|
+
if listener_message_metadata.properties.reply_to is not None:
|
932
966
|
|
933
967
|
def on_context_disposed(context: ListenerContext) -> None:
|
934
|
-
assert
|
968
|
+
assert listener_message_metadata.properties.reply_to is not None
|
935
969
|
assert context.rpc_reply is not None
|
936
970
|
|
937
971
|
if not context.rpc_reply.replied:
|
938
972
|
self._reply_response(
|
939
|
-
|
973
|
+
listener_message_metadata=listener_message_metadata,
|
940
974
|
response=self._reponse_from_exception(exception),
|
941
975
|
)
|
942
976
|
|
943
977
|
context_dispose_callback = on_context_disposed
|
944
978
|
rpc_reply = RpcReply(
|
945
979
|
channel_pool=self._channel_pool,
|
946
|
-
reply_to=
|
947
|
-
|
980
|
+
reply_to=listener_message_metadata.properties.reply_to,
|
981
|
+
blocked_connection_check_callback=self._blocked_connection_check_callback,
|
982
|
+
correlation_id=listener_message_metadata.properties.correlation_id,
|
948
983
|
)
|
949
984
|
|
950
985
|
try:
|
951
986
|
exception_callback_succeeded = self._on_exception_callback(
|
952
987
|
ListenerContext(
|
953
988
|
queue=self._queue,
|
954
|
-
listener_name=
|
955
|
-
body=
|
989
|
+
listener_name=listener_message_metadata.listener_name,
|
990
|
+
body=listener_message_metadata.body,
|
956
991
|
flow_control=self._flow_control,
|
957
992
|
rpc_reply=rpc_reply,
|
958
993
|
context_dispose_callback=context_dispose_callback,
|
994
|
+
).set_labels(
|
995
|
+
{
|
996
|
+
"queue": self._queue,
|
997
|
+
"listener_name": listener_message_metadata.listener_name,
|
998
|
+
"exception": exception.__class__.__name__,
|
999
|
+
}
|
959
1000
|
),
|
960
1001
|
exception,
|
961
1002
|
)
|
962
1003
|
except:
|
1004
|
+
tags.append("exception_callback_error")
|
963
1005
|
self._remote_logger.exception(
|
964
|
-
message=f"error occured while invoking rabbitmq exception handler callback in listener `{
|
965
|
-
tags=
|
966
|
-
|
967
|
-
self._queue,
|
968
|
-
listener_message_meta.listener_name,
|
969
|
-
],
|
970
|
-
extra={
|
971
|
-
"serviceType": "RabbitMQ",
|
972
|
-
"queue": self._queue,
|
973
|
-
"listenerName": listener_message_meta.listener_name,
|
974
|
-
"section": "exceptionHandlerCallback",
|
975
|
-
"raisedException": exception.__class__.__name__,
|
976
|
-
},
|
1006
|
+
message=f"error occured while invoking rabbitmq exception handler callback in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`",
|
1007
|
+
tags=tags,
|
1008
|
+
extra=extra,
|
977
1009
|
)
|
978
1010
|
|
979
1011
|
return
|
980
1012
|
|
981
1013
|
if not exception_callback_succeeded:
|
1014
|
+
tags.append("exception_callback_unsuccessful")
|
982
1015
|
self._remote_logger.exception(
|
983
1016
|
message=(
|
984
1017
|
message
|
985
|
-
or f"error occured while handling event in listener `{
|
1018
|
+
or f"error occured while handling event in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`"
|
986
1019
|
),
|
987
|
-
tags=
|
988
|
-
|
989
|
-
self._queue,
|
990
|
-
listener_message_meta.listener_name,
|
991
|
-
],
|
992
|
-
extra={
|
993
|
-
"serviceType": "RabbitMQ",
|
994
|
-
"queue": self._queue,
|
995
|
-
"listenerName": listener_message_meta.listener_name,
|
996
|
-
"raisedException": exception.__class__.__name__,
|
997
|
-
},
|
1020
|
+
tags=tags,
|
1021
|
+
extra=extra,
|
998
1022
|
)
|
999
1023
|
|
1000
1024
|
def _reject_message(
|
1001
1025
|
self,
|
1002
|
-
|
1026
|
+
listener_message_metadata: ListenerMessageMetadata,
|
1003
1027
|
retry_policy: RetryPolicy,
|
1004
1028
|
times_rejected: int,
|
1005
1029
|
) -> None:
|
@@ -1007,7 +1031,7 @@ class Listener(AsyncEventLoopMixin):
|
|
1007
1031
|
|
1008
1032
|
self._logger.debug(
|
1009
1033
|
"message will be redelivered to listenr `%s` on queue `%s` after `%f` seconds, times redelivered `%d`",
|
1010
|
-
|
1034
|
+
listener_message_metadata.listener_name,
|
1011
1035
|
self._queue,
|
1012
1036
|
message_redelivery_delay,
|
1013
1037
|
times_rejected,
|
@@ -1016,27 +1040,27 @@ class Listener(AsyncEventLoopMixin):
|
|
1016
1040
|
delay=message_redelivery_delay,
|
1017
1041
|
callback=partial(
|
1018
1042
|
self._on_time_to_redeliver_message,
|
1019
|
-
|
1043
|
+
listener_message_metadata,
|
1020
1044
|
times_rejected,
|
1021
1045
|
),
|
1022
1046
|
)
|
1023
1047
|
|
1024
1048
|
def _on_time_to_redeliver_message(
|
1025
1049
|
self,
|
1026
|
-
|
1050
|
+
listener_message_metadata: ListenerMessageMetadata,
|
1027
1051
|
times_rejected: int,
|
1028
1052
|
) -> None:
|
1029
1053
|
self.loop.create_task(self._channel_pool.get()).add_done_callback(
|
1030
1054
|
partial(
|
1031
1055
|
self._on_redelivery_channel_found,
|
1032
|
-
|
1056
|
+
listener_message_metadata,
|
1033
1057
|
times_rejected,
|
1034
1058
|
)
|
1035
1059
|
)
|
1036
1060
|
|
1037
1061
|
def _on_redelivery_channel_found(
|
1038
1062
|
self,
|
1039
|
-
|
1063
|
+
listener_message_metadata: ListenerMessageMetadata,
|
1040
1064
|
times_rejected: int,
|
1041
1065
|
task: Task[BaseChannel],
|
1042
1066
|
) -> None:
|
@@ -1048,66 +1072,84 @@ class Listener(AsyncEventLoopMixin):
|
|
1048
1072
|
if exception is not None:
|
1049
1073
|
self._call_exception_callback(
|
1050
1074
|
exception=exception,
|
1051
|
-
|
1052
|
-
message=f"error occured while getting channel from pool in listener `{
|
1075
|
+
listener_message_metadata=listener_message_metadata,
|
1076
|
+
message=f"error occured while getting channel from pool in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}` after rejecteed {times_rejected} times",
|
1053
1077
|
)
|
1054
1078
|
|
1055
1079
|
return
|
1056
1080
|
|
1057
1081
|
headers = {
|
1058
|
-
self._listener_name_header_key:
|
1082
|
+
self._listener_name_header_key: listener_message_metadata.listener_name,
|
1059
1083
|
"times_rejected": times_rejected + 1,
|
1060
1084
|
}
|
1061
1085
|
|
1062
|
-
if
|
1063
|
-
|
1086
|
+
if listener_message_metadata.properties.headers is None:
|
1087
|
+
listener_message_metadata.properties.headers = headers
|
1064
1088
|
else:
|
1065
1089
|
cast(
|
1066
|
-
dict[str, Any],
|
1090
|
+
dict[str, Any], listener_message_metadata.properties.headers
|
1067
1091
|
).update(headers)
|
1068
1092
|
|
1093
|
+
if self._blocked_connection_check_callback():
|
1094
|
+
self._remote_logger.error(
|
1095
|
+
message=f"couldn't redeliver message to queue `{self._queue}` and listener `{listener_message_metadata.listener_name}` due to blocked connection",
|
1096
|
+
tags=[
|
1097
|
+
"rabbitmq",
|
1098
|
+
"redelivery_connection_blocked",
|
1099
|
+
self._queue,
|
1100
|
+
listener_message_metadata.listener_name,
|
1101
|
+
],
|
1102
|
+
extra={
|
1103
|
+
"serviceType": "rabbitmq",
|
1104
|
+
"queue": self._queue,
|
1105
|
+
"listenerName": listener_message_metadata.listener_name,
|
1106
|
+
},
|
1107
|
+
)
|
1108
|
+
|
1109
|
+
return
|
1110
|
+
|
1069
1111
|
try:
|
1070
1112
|
with task.result() as channel:
|
1071
1113
|
channel.basic_publish(
|
1072
1114
|
exchange=DEFAULT_EXCHANGE,
|
1073
1115
|
routing_key=self._queue,
|
1074
|
-
body=
|
1075
|
-
properties=
|
1116
|
+
body=listener_message_metadata.body,
|
1117
|
+
properties=listener_message_metadata.properties,
|
1076
1118
|
)
|
1077
1119
|
except Exception as e:
|
1078
1120
|
self._call_exception_callback(
|
1079
1121
|
exception=e,
|
1080
|
-
|
1081
|
-
message=f"error occured while sending event for redelivery in listener `{
|
1122
|
+
listener_message_metadata=listener_message_metadata,
|
1123
|
+
message=f"error occured while sending event for redelivery in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}` after rejecteed {times_rejected} times",
|
1082
1124
|
)
|
1083
1125
|
|
1084
1126
|
return
|
1085
1127
|
|
1086
1128
|
self._logger.debug(
|
1087
1129
|
"message queued for redelivery to `%s` on queue `%s`, times redelivered `%d`",
|
1088
|
-
|
1130
|
+
listener_message_metadata.listener_name,
|
1089
1131
|
self._queue,
|
1090
1132
|
times_rejected + 1,
|
1091
1133
|
)
|
1092
1134
|
|
1093
1135
|
def _reply_response(
|
1094
1136
|
self,
|
1095
|
-
|
1137
|
+
listener_message_metadata: ListenerMessageMetadata,
|
1096
1138
|
response: Any,
|
1097
1139
|
) -> None:
|
1098
|
-
assert
|
1140
|
+
assert listener_message_metadata.properties.reply_to is not None
|
1099
1141
|
|
1100
1142
|
reponse_properties = BasicProperties(content_type="application/json")
|
1101
1143
|
|
1102
|
-
if
|
1144
|
+
if listener_message_metadata.properties.correlation_id is None:
|
1103
1145
|
self._logger.warning(
|
1104
1146
|
"`correlation_id` property not supplied for listener `%s` and queue `%s`",
|
1105
|
-
|
1147
|
+
listener_message_metadata.listener_name,
|
1106
1148
|
self._queue,
|
1107
1149
|
)
|
1108
1150
|
else:
|
1109
1151
|
reponse_properties.correlation_id = (
|
1110
|
-
|
1152
|
+
listener_message_metadata.properties.correlation_id
|
1111
1153
|
)
|
1112
1154
|
|
1113
1155
|
try:
|
@@ -1115,8 +1157,8 @@ class Listener(AsyncEventLoopMixin):
|
|
1115
1157
|
except Exception as e:
|
1116
1158
|
self._call_exception_callback(
|
1117
1159
|
exception=e,
|
1118
|
-
|
1119
|
-
message=f"listener response is not json serializable in listener `{
|
1160
|
+
listener_message_metadata=listener_message_metadata,
|
1161
|
+
message=f"listener response is not json serializable in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`",
|
1120
1162
|
)
|
1121
1163
|
|
1122
1164
|
return
|
@@ -1124,7 +1166,7 @@ class Listener(AsyncEventLoopMixin):
|
|
1124
1166
|
self.loop.create_task(self._channel_pool.get()).add_done_callback(
|
1125
1167
|
partial(
|
1126
1168
|
self._on_reply_channel_found,
|
1127
|
-
|
1169
|
+
listener_message_metadata,
|
1128
1170
|
response_body,
|
1129
1171
|
reponse_properties,
|
1130
1172
|
)
|
@@ -1156,7 +1198,7 @@ class Listener(AsyncEventLoopMixin):
|
|
1156
1198
|
|
1157
1199
|
def _on_reply_channel_found(
|
1158
1200
|
self,
|
1159
|
-
|
1201
|
+
listener_message_metadata: ListenerMessageMetadata,
|
1160
1202
|
response_body: bytes,
|
1161
1203
|
response_properties: BasicProperties,
|
1162
1204
|
task: Task[BaseChannel],
|
@@ -1169,36 +1211,54 @@ class Listener(AsyncEventLoopMixin):
|
|
1169
1211
|
if exception is not None:
|
1170
1212
|
self._call_exception_callback(
|
1171
1213
|
exception=exception,
|
1172
|
-
|
1173
|
-
message=f"error occured while getting channel for publishing response in listener `{
|
1214
|
+
listener_message_metadata=listener_message_metadata,
|
1215
|
+
message=f"error occured while getting channel for publishing response in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`",
|
1174
1216
|
)
|
1175
1217
|
|
1176
1218
|
return
|
1177
1219
|
|
1178
|
-
|
1179
|
-
|
1220
|
+
if self._blocked_connection_check_callback():
|
1221
|
+
self._remote_logger.error(
|
1222
|
+
message=f"couldn't repond to rpc call on queue `{self._queue}` and listener `{listener_message_metadata.listener_name}` due to blocked connection",
|
1223
|
+
tags=[
|
1224
|
+
"rabbitmq",
|
1225
|
+
"reply_connection_blocked",
|
1226
|
+
self._queue,
|
1227
|
+
listener_message_metadata.listener_name,
|
1228
|
+
],
|
1229
|
+
extra={
|
1230
|
+
"serviceType": "rabbitmq",
|
1231
|
+
"queue": self._queue,
|
1232
|
+
"listenerName": listener_message_metadata.listener_name,
|
1233
|
+
},
|
1234
|
+
)
|
1235
|
+
|
1236
|
+
return
|
1180
1237
|
|
1238
|
+
assert listener_message_metadata.properties.reply_to is not None
|
1239
|
+
|
1240
|
+
try:
|
1181
1241
|
with task.result() as channel:
|
1182
1242
|
channel.basic_publish(
|
1183
1243
|
exchange=DEFAULT_EXCHANGE,
|
1184
|
-
routing_key=
|
1244
|
+
routing_key=listener_message_metadata.properties.reply_to,
|
1185
1245
|
properties=response_properties,
|
1186
1246
|
body=response_body,
|
1187
1247
|
)
|
1188
1248
|
except Exception as e:
|
1189
1249
|
self._call_exception_callback(
|
1190
1250
|
exception=e,
|
1191
|
-
|
1192
|
-
message=f"error occured while publishing response to rpc call in listener `{
|
1251
|
+
listener_message_metadata=listener_message_metadata,
|
1252
|
+
message=f"error occured while publishing response to rpc call in listener `{listener_message_metadata.listener_name}` and queue `{self._queue}`",
|
1193
1253
|
)
|
1194
1254
|
|
1195
1255
|
return
|
1196
1256
|
|
1197
1257
|
self._logger.debug(
|
1198
1258
|
"sent a reply to `%s` for request from queue `%s` and listener `%s`",
|
1199
|
-
|
1259
|
+
listener_message_metadata.properties.reply_to,
|
1200
1260
|
self._queue,
|
1201
|
-
|
1261
|
+
listener_message_metadata.listener_name,
|
1202
1262
|
)
|
1203
1263
|
|
1204
1264
|
def __call__(self, listener: type[L]) -> type[L]:
|
@@ -1207,6 +1267,13 @@ class Listener(AsyncEventLoopMixin):
|
|
1207
1267
|
return listener
|
1208
1268
|
|
1209
1269
|
|
1270
|
+
@dataclass
|
1271
|
+
class ListenerMethodMetadata:
|
1272
|
+
listener_type: type[Listener]
|
1273
|
+
listener_name: str | None = None
|
1274
|
+
retry_policy: RetryPolicy | None = None
|
1275
|
+
|
1276
|
+
|
1210
1277
|
class Consumer(Listener):
|
1211
1278
|
def __init__(
|
1212
1279
|
self,
|
@@ -1266,7 +1333,11 @@ def consume(
|
|
1266
1333
|
setattr(
|
1267
1334
|
consumer_method,
|
1268
1335
|
CONSUMER_ATTRIBUTE,
|
1269
|
-
|
1336
|
+
ListenerMethodMetadata(
|
1337
|
+
listener_type=Consumer,
|
1338
|
+
listener_name=target,
|
1339
|
+
retry_policy=retry_policy,
|
1340
|
+
),
|
1270
1341
|
)
|
1271
1342
|
|
1272
1343
|
return consumer_method
|
@@ -1317,7 +1388,11 @@ def execute(
|
|
1317
1388
|
)
|
1318
1389
|
|
1319
1390
|
setattr(
|
1320
|
-
worker_method,
|
1391
|
+
worker_method,
|
1392
|
+
RPC_WORKER_ATTRIBUTE,
|
1393
|
+
ListenerMethodMetadata(
|
1394
|
+
listener_type=RpcWorker, listener_name=procedure
|
1395
|
+
),
|
1321
1396
|
)
|
1322
1397
|
|
1323
1398
|
return worker_method
|
@@ -1325,13 +1400,9 @@ def execute(
|
|
1325
1400
|
return wrapper
|
1326
1401
|
|
1327
1402
|
|
1328
|
-
@dataclass
|
1329
|
-
class ListenerMethodMeta:
|
1330
|
-
listener_name: str | None = None
|
1331
|
-
retry_policy: RetryPolicy | None = None
|
1332
|
-
|
1333
|
-
|
1334
1403
|
class ListenerBase:
|
1404
|
+
_LISTENER_ATTRIBUTES = {CONSUMER_ATTRIBUTE, RPC_WORKER_ATTRIBUTE}
|
1405
|
+
|
1335
1406
|
def get_inner_listener(self) -> Listener:
|
1336
1407
|
listener = getattr(self, LISTENER_ATTRIBUTE, None)
|
1337
1408
|
|
@@ -1352,32 +1423,35 @@ class ListenerBase:
|
|
1352
1423
|
if attribute is None:
|
1353
1424
|
continue
|
1354
1425
|
|
1355
|
-
listener_method_attribute
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1426
|
+
listener_method_attribute = None
|
1427
|
+
listener_method = None
|
1428
|
+
listener_method_metadata = None
|
1429
|
+
|
1430
|
+
for listener_attribute in self._LISTENER_ATTRIBUTES:
|
1431
|
+
if (
|
1432
|
+
listener_method is not None
|
1433
|
+
and listener_method_metadata is not None
|
1434
|
+
):
|
1435
|
+
break
|
1362
1436
|
|
1363
|
-
if listener_method is None or listener_method_meta is None:
|
1364
1437
|
(
|
1365
1438
|
listener_method_attribute,
|
1366
1439
|
listener_method,
|
1367
|
-
|
1440
|
+
listener_method_metadata,
|
1368
1441
|
) = self._validate_listener_method_attribute(
|
1369
1442
|
attribute=attribute,
|
1370
|
-
listener_method_attribute=
|
1443
|
+
listener_method_attribute=listener_attribute,
|
1371
1444
|
previous_listener_method_attribute=listener_method_attribute,
|
1445
|
+
listener=listener,
|
1372
1446
|
)
|
1373
1447
|
|
1374
|
-
if listener_method is None or
|
1448
|
+
if listener_method is None or listener_method_metadata is None:
|
1375
1449
|
continue
|
1376
1450
|
|
1377
1451
|
listener.add_listener_method(
|
1378
|
-
listener_name=
|
1452
|
+
listener_name=listener_method_metadata.listener_name,
|
1379
1453
|
listener_method=listener_method,
|
1380
|
-
retry_policy=
|
1454
|
+
retry_policy=listener_method_metadata.retry_policy,
|
1381
1455
|
)
|
1382
1456
|
|
1383
1457
|
return listener
|
@@ -1387,19 +1461,21 @@ class ListenerBase:
|
|
1387
1461
|
attribute: Any,
|
1388
1462
|
listener_method_attribute: str,
|
1389
1463
|
previous_listener_method_attribute: str | None,
|
1464
|
+
listener: Listener,
|
1390
1465
|
) -> tuple[
|
1391
|
-
str | None, Callable[..., Any] | None,
|
1466
|
+
str | None, Callable[..., Any] | None, ListenerMethodMetadata | None
|
1392
1467
|
]:
|
1393
|
-
|
1468
|
+
listener_method_metadata = getattr(
|
1394
1469
|
attribute, listener_method_attribute, None
|
1395
1470
|
)
|
1396
1471
|
|
1397
|
-
if
|
1472
|
+
if listener_method_metadata is None:
|
1398
1473
|
return previous_listener_method_attribute or None, None, None
|
1399
1474
|
|
1400
|
-
if not isinstance(
|
1475
|
+
if not isinstance(listener_method_metadata, ListenerMethodMetadata):
|
1401
1476
|
raise TypeError(
|
1402
|
-
f"expected `{listener_method_attribute}` to by of type `
|
1477
|
+
f"expected `{listener_method_attribute}` to by of type `ListenerMethodMetadata`, "
|
1478
|
+
f"got {type(listener_method_metadata)}"
|
1403
1479
|
)
|
1404
1480
|
|
1405
1481
|
if (
|
@@ -1413,4 +1489,17 @@ class ListenerBase:
|
|
1413
1489
|
f"object annotated with `{listener_method_attribute}` is not callable"
|
1414
1490
|
)
|
1415
1491
|
|
1416
|
-
|
1492
|
+
if (
|
1493
|
+
listener_method_metadata.listener_type
|
1494
|
+
not in listener.__class__.mro()
|
1495
|
+
):
|
1496
|
+
listener_mro = ", ".join(
|
1497
|
+
map(lambda c: c.__name__, listener.__class__.mro())
|
1498
|
+
)
|
1499
|
+
|
1500
|
+
raise TypeError(
|
1501
|
+
f"listener method is not correct member of `{listener_method_metadata.listener_type.__name__}`, "
|
1502
|
+
f"got `[{listener_mro}]` super classes"
|
1503
|
+
)
|
1504
|
+
|
1505
|
+
return listener_method_attribute, attribute, listener_method_metadata
|