jararaca 0.3.12a4__tar.gz → 0.3.12a5__tar.gz
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.
Potentially problematic release.
This version of jararaca might be problematic. Click here for more details.
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/PKG-INFO +1 -1
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/pyproject.toml +1 -1
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/__init__.py +10 -4
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/worker.py +102 -84
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/microservice.py +42 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/server.py +57 -11
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/LICENSE +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/README.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/CNAME +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/architecture.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.jpeg +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.webp +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/assets/tracing_example.png +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/http-rpc.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/index.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/interceptors.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/messagebus.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/retry.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/scheduler.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/stylesheets/custom.css +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/websocket.md +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/__main__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/broker_backend/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/broker_backend/mapper.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/broker_backend/redis_broker_backend.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/cli.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/common/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/core/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/core/providers.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/core/uow.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/di.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/files/entity.py.mako +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/lifecycle.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/bus_message_controller.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/consumers/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/interceptors/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/interceptors/publisher_interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/message.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/messagebus/publisher.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/observability/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/observability/interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/observability/providers/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/observability/providers/otel.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/base.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/exports.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/interceptors/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/session.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/sort_filter.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/utilities.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/hooks.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/http_microservice.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/base_types.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/context.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/redis.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/types.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/websocket_interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/py.typed +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/reflect/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/reflect/controller_inspect.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/reflect/metadata.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/backends/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/backends/httpx.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/backends/otel.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/rpc/http/httpx.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/scheduler/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/scheduler/beat_worker.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/scheduler/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/scheduler/types.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/tools/app_config/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/tools/app_config/decorators.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/tools/app_config/interceptor.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/tools/typescript/interface_parser.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/utils/__init__.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/utils/rabbitmq_utils.py +0 -0
- {jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/utils/retry.py +0 -0
|
@@ -102,6 +102,8 @@ if TYPE_CHECKING:
|
|
|
102
102
|
from .messagebus.publisher import use_publisher
|
|
103
103
|
from .microservice import (
|
|
104
104
|
Microservice,
|
|
105
|
+
is_shutting_down,
|
|
106
|
+
request_shutdown,
|
|
105
107
|
use_app_context,
|
|
106
108
|
use_app_transaction_context,
|
|
107
109
|
use_app_tx_ctx_data,
|
|
@@ -207,6 +209,12 @@ if TYPE_CHECKING:
|
|
|
207
209
|
"QueryInjector",
|
|
208
210
|
"HttpMicroservice",
|
|
209
211
|
"use_current_container",
|
|
212
|
+
"use_app_context",
|
|
213
|
+
"use_app_transaction_context",
|
|
214
|
+
"use_app_tx_ctx_data",
|
|
215
|
+
"is_shutting_down",
|
|
216
|
+
"request_shutdown",
|
|
217
|
+
"Microservice",
|
|
210
218
|
"T_BASEMODEL",
|
|
211
219
|
"DatedEntity",
|
|
212
220
|
"BaseEntity",
|
|
@@ -229,7 +237,6 @@ if TYPE_CHECKING:
|
|
|
229
237
|
"MessageBusController",
|
|
230
238
|
"MessageHandler",
|
|
231
239
|
"ScheduledAction",
|
|
232
|
-
"Microservice",
|
|
233
240
|
"ProviderSpec",
|
|
234
241
|
"Token",
|
|
235
242
|
"AIOSqlAlchemySessionInterceptor",
|
|
@@ -284,9 +291,6 @@ if TYPE_CHECKING:
|
|
|
284
291
|
"RetryConfig",
|
|
285
292
|
# Exception classes
|
|
286
293
|
"TimeoutException",
|
|
287
|
-
"use_app_context",
|
|
288
|
-
"use_app_transaction_context",
|
|
289
|
-
"use_app_tx_ctx_data",
|
|
290
294
|
"AppTransactionContext",
|
|
291
295
|
"AppContext",
|
|
292
296
|
"ControllerMemberReflect",
|
|
@@ -501,6 +505,8 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
|
|
|
501
505
|
"use_app_context": (__SPEC_PARENT__, "microservice", None),
|
|
502
506
|
"use_app_transaction_context": (__SPEC_PARENT__, "microservice", None),
|
|
503
507
|
"use_app_tx_ctx_data": (__SPEC_PARENT__, "microservice", None),
|
|
508
|
+
"is_shutting_down": (__SPEC_PARENT__, "microservice", None),
|
|
509
|
+
"request_shutdown": (__SPEC_PARENT__, "microservice", None),
|
|
504
510
|
"AppContext": (__SPEC_PARENT__, "microservice", None),
|
|
505
511
|
"AppInterceptor": (__SPEC_PARENT__, "microservice", None),
|
|
506
512
|
"AppTransactionContext": (__SPEC_PARENT__, "microservice", None),
|
|
@@ -49,6 +49,8 @@ from jararaca.microservice import (
|
|
|
49
49
|
MessageBusTransactionData,
|
|
50
50
|
Microservice,
|
|
51
51
|
SchedulerTransactionData,
|
|
52
|
+
ShutdownState,
|
|
53
|
+
provide_shutdown_state,
|
|
52
54
|
)
|
|
53
55
|
from jararaca.scheduler.decorators import ScheduledActionData
|
|
54
56
|
from jararaca.utils.rabbitmq_utils import RabbitmqUtils
|
|
@@ -129,6 +131,17 @@ class MessageBusConsumer(ABC):
|
|
|
129
131
|
"""Close all resources related to the consumer"""
|
|
130
132
|
|
|
131
133
|
|
|
134
|
+
class _WorkerShutdownState(ShutdownState):
|
|
135
|
+
def __init__(self, shutdown_event: asyncio.Event):
|
|
136
|
+
self.shutdown_event = shutdown_event
|
|
137
|
+
|
|
138
|
+
def request_shutdown(self) -> None:
|
|
139
|
+
self.shutdown_event.set()
|
|
140
|
+
|
|
141
|
+
def is_shutdown_requested(self) -> bool:
|
|
142
|
+
return self.shutdown_event.is_set()
|
|
143
|
+
|
|
144
|
+
|
|
132
145
|
class AioPikaMicroserviceConsumer(MessageBusConsumer):
|
|
133
146
|
def __init__(
|
|
134
147
|
self,
|
|
@@ -146,6 +159,7 @@ class AioPikaMicroserviceConsumer(MessageBusConsumer):
|
|
|
146
159
|
self.incoming_map: dict[str, MessageHandlerData] = {}
|
|
147
160
|
self.uow_context_provider = uow_context_provider
|
|
148
161
|
self.shutdown_event = asyncio.Event()
|
|
162
|
+
self.shutdown_state = _WorkerShutdownState(self.shutdown_event)
|
|
149
163
|
self.lock = asyncio.Lock()
|
|
150
164
|
self.tasks: set[asyncio.Task[Any]] = set()
|
|
151
165
|
self.connection: aio_pika.abc.AbstractConnection | None = None
|
|
@@ -832,18 +846,19 @@ class ScheduledMessageHandlerCallback:
|
|
|
832
846
|
args: tuple[Any, ...],
|
|
833
847
|
kwargs: dict[str, Any],
|
|
834
848
|
) -> None:
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
849
|
+
with provide_shutdown_state(self.consumer.shutdown_state):
|
|
850
|
+
async with self.consumer.uow_context_provider(
|
|
851
|
+
AppTransactionContext(
|
|
852
|
+
controller_member_reflect=scheduled_action.controller_member,
|
|
853
|
+
transaction_data=SchedulerTransactionData(
|
|
854
|
+
scheduled_to=datetime.now(UTC),
|
|
855
|
+
cron_expression=scheduled_action.spec.cron,
|
|
856
|
+
triggered_at=datetime.now(UTC),
|
|
857
|
+
),
|
|
858
|
+
)
|
|
859
|
+
):
|
|
845
860
|
|
|
846
|
-
|
|
861
|
+
await scheduled_action.callable(*args, **kwargs)
|
|
847
862
|
|
|
848
863
|
|
|
849
864
|
class MessageHandlerCallback:
|
|
@@ -1133,83 +1148,86 @@ class MessageHandlerCallback:
|
|
|
1133
1148
|
incoming_message_spec = MessageHandler.get_message_incoming(handler)
|
|
1134
1149
|
assert incoming_message_spec is not None
|
|
1135
1150
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1151
|
+
with provide_shutdown_state(self.consumer.shutdown_state):
|
|
1152
|
+
async with self.consumer.uow_context_provider(
|
|
1153
|
+
AppTransactionContext(
|
|
1154
|
+
controller_member_reflect=handler_data.controller_member,
|
|
1155
|
+
transaction_data=MessageBusTransactionData(
|
|
1156
|
+
message=builded_message,
|
|
1157
|
+
topic=routing_key,
|
|
1158
|
+
),
|
|
1159
|
+
)
|
|
1160
|
+
):
|
|
1161
|
+
ctx: AsyncContextManager[Any]
|
|
1162
|
+
if incoming_message_spec.timeout is not None:
|
|
1163
|
+
ctx = asyncio.timeout(incoming_message_spec.timeout)
|
|
1164
|
+
else:
|
|
1165
|
+
ctx = none_context()
|
|
1166
|
+
async with ctx:
|
|
1167
|
+
try:
|
|
1168
|
+
with provide_bus_message_controller(
|
|
1169
|
+
AioPikaMessageBusController(aio_pika_message)
|
|
1170
|
+
):
|
|
1171
|
+
await handler(builded_message)
|
|
1172
|
+
if not incoming_message_spec.auto_ack:
|
|
1173
|
+
with suppress(aio_pika.MessageProcessError):
|
|
1174
|
+
# Use channel context for acknowledgement
|
|
1175
|
+
async with self.consumer.get_channel_ctx(
|
|
1176
|
+
self.queue_name
|
|
1177
|
+
):
|
|
1178
|
+
await aio_pika_message.ack()
|
|
1179
|
+
except BaseException as base_exc:
|
|
1180
|
+
# Get message id for logging
|
|
1181
|
+
message_id = aio_pika_message.message_id or str(uuid.uuid4())
|
|
1182
|
+
|
|
1183
|
+
# Extract retry count from headers if available
|
|
1184
|
+
headers = aio_pika_message.headers or {}
|
|
1185
|
+
retry_count = int(str(headers.get("x-retry-count", 0)))
|
|
1186
|
+
|
|
1187
|
+
# Process exception handler if configured
|
|
1188
|
+
if incoming_message_spec.exception_handler is not None:
|
|
1189
|
+
try:
|
|
1190
|
+
incoming_message_spec.exception_handler(base_exc)
|
|
1191
|
+
except Exception as nested_exc:
|
|
1192
|
+
logger.exception(
|
|
1193
|
+
f"Error processing exception handler for message {message_id}: {base_exc} | {nested_exc}"
|
|
1194
|
+
)
|
|
1195
|
+
else:
|
|
1174
1196
|
logger.exception(
|
|
1175
|
-
f"Error processing
|
|
1197
|
+
f"Error processing message {message_id} on topic {routing_key}: {str(base_exc)}"
|
|
1176
1198
|
)
|
|
1177
|
-
else:
|
|
1178
|
-
logger.exception(
|
|
1179
|
-
f"Error processing message {message_id} on topic {routing_key}: {str(base_exc)}"
|
|
1180
|
-
)
|
|
1181
1199
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
else:
|
|
1197
|
-
# Message processed successfully, log and clean up any retry state
|
|
1198
|
-
message_id = aio_pika_message.message_id or str(uuid.uuid4())
|
|
1199
|
-
if message_id in self.retry_state:
|
|
1200
|
-
del self.retry_state[message_id]
|
|
1201
|
-
|
|
1202
|
-
# Log success with retry information if applicable
|
|
1203
|
-
headers = aio_pika_message.headers or {}
|
|
1204
|
-
if "x-retry-count" in headers:
|
|
1205
|
-
retry_count = int(str(headers.get("x-retry-count", 0)))
|
|
1206
|
-
logger.info(
|
|
1207
|
-
f"Message {message_id}#{self.queue_name} processed successfully after {retry_count} retries"
|
|
1208
|
-
)
|
|
1200
|
+
# Handle rejection with retry logic
|
|
1201
|
+
if incoming_message_spec.requeue_on_exception:
|
|
1202
|
+
# Use our retry with backoff mechanism
|
|
1203
|
+
await self.handle_reject_message(
|
|
1204
|
+
aio_pika_message,
|
|
1205
|
+
requeue=False, # Don't requeue directly, use our backoff mechanism
|
|
1206
|
+
retry_count=retry_count,
|
|
1207
|
+
exception=base_exc,
|
|
1208
|
+
)
|
|
1209
|
+
else:
|
|
1210
|
+
# Message shouldn't be retried, reject it
|
|
1211
|
+
await self.handle_reject_message(
|
|
1212
|
+
aio_pika_message, requeue=False, exception=base_exc
|
|
1213
|
+
)
|
|
1209
1214
|
else:
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1215
|
+
# Message processed successfully, log and clean up any retry state
|
|
1216
|
+
message_id = aio_pika_message.message_id or str(uuid.uuid4())
|
|
1217
|
+
if message_id in self.retry_state:
|
|
1218
|
+
del self.retry_state[message_id]
|
|
1219
|
+
|
|
1220
|
+
# Log success with retry information if applicable
|
|
1221
|
+
headers = aio_pika_message.headers or {}
|
|
1222
|
+
if "x-retry-count" in headers:
|
|
1223
|
+
retry_count = int(str(headers.get("x-retry-count", 0)))
|
|
1224
|
+
logger.info(
|
|
1225
|
+
f"Message {message_id}#{self.queue_name} processed successfully after {retry_count} retries"
|
|
1226
|
+
)
|
|
1227
|
+
else:
|
|
1228
|
+
logger.info(
|
|
1229
|
+
f"Message {message_id}#{self.queue_name} processed successfully"
|
|
1230
|
+
)
|
|
1213
1231
|
|
|
1214
1232
|
|
|
1215
1233
|
@asynccontextmanager
|
|
@@ -325,6 +325,48 @@ def provide_container(container: Container) -> Generator[None, None, None]:
|
|
|
325
325
|
current_container_ctx.reset(token)
|
|
326
326
|
|
|
327
327
|
|
|
328
|
+
class ShutdownState(Protocol):
|
|
329
|
+
|
|
330
|
+
def request_shutdown(self) -> None: ...
|
|
331
|
+
|
|
332
|
+
def is_shutdown_requested(self) -> bool: ...
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
shutdown_state_ctx = ContextVar[ShutdownState]("shutdown_state")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def is_shutting_down() -> bool:
|
|
339
|
+
"""
|
|
340
|
+
Check if the application is in the process of shutting down.
|
|
341
|
+
"""
|
|
342
|
+
return shutdown_state_ctx.get().is_shutdown_requested()
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def request_shutdown() -> None:
|
|
346
|
+
"""
|
|
347
|
+
Request the application to shut down.
|
|
348
|
+
This will set the shutdown event, allowing the application to gracefully shut down.
|
|
349
|
+
"""
|
|
350
|
+
shutdown_state_ctx.get().request_shutdown()
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@contextmanager
|
|
354
|
+
def provide_shutdown_state(
|
|
355
|
+
state: ShutdownState,
|
|
356
|
+
) -> Generator[None, None, None]:
|
|
357
|
+
"""
|
|
358
|
+
Context manager to provide the shutdown state.
|
|
359
|
+
This is used to manage the shutdown event for the application.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
token = shutdown_state_ctx.set(state)
|
|
363
|
+
try:
|
|
364
|
+
yield
|
|
365
|
+
finally:
|
|
366
|
+
with suppress(ValueError):
|
|
367
|
+
shutdown_state_ctx.reset(token)
|
|
368
|
+
|
|
369
|
+
|
|
328
370
|
__all__ = [
|
|
329
371
|
"AppTransactionContext",
|
|
330
372
|
"AppInterceptor",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import signal
|
|
3
|
+
import threading
|
|
1
4
|
from contextlib import asynccontextmanager
|
|
5
|
+
from signal import SIGINT, SIGTERM
|
|
2
6
|
from typing import Any, AsyncGenerator
|
|
3
7
|
|
|
4
8
|
from fastapi import Depends, FastAPI, Request, WebSocket
|
|
@@ -10,7 +14,9 @@ from jararaca.lifecycle import AppLifecycle
|
|
|
10
14
|
from jararaca.microservice import (
|
|
11
15
|
AppTransactionContext,
|
|
12
16
|
HttpTransactionData,
|
|
17
|
+
ShutdownState,
|
|
13
18
|
WebSocketTransactionData,
|
|
19
|
+
provide_shutdown_state,
|
|
14
20
|
)
|
|
15
21
|
from jararaca.presentation.decorators import RestController
|
|
16
22
|
from jararaca.presentation.http_microservice import HttpMicroservice
|
|
@@ -76,10 +82,49 @@ class HttpAppLifecycle:
|
|
|
76
82
|
yield
|
|
77
83
|
|
|
78
84
|
|
|
85
|
+
class HttpShutdownState(ShutdownState):
|
|
86
|
+
def __init__(self) -> None:
|
|
87
|
+
self._requested = False
|
|
88
|
+
self.old_signal_handlers = {
|
|
89
|
+
SIGINT: signal.getsignal(SIGINT),
|
|
90
|
+
SIGTERM: signal.getsignal(SIGTERM),
|
|
91
|
+
}
|
|
92
|
+
self.thread_lock = threading.Lock()
|
|
93
|
+
|
|
94
|
+
def request_shutdown(self) -> None:
|
|
95
|
+
if not self._requested:
|
|
96
|
+
self._requested = True
|
|
97
|
+
os.kill(os.getpid(), SIGINT)
|
|
98
|
+
|
|
99
|
+
def is_shutdown_requested(self) -> bool:
|
|
100
|
+
return self._requested
|
|
101
|
+
|
|
102
|
+
def handle_signal(self, signum: int, frame: Any) -> None:
|
|
103
|
+
print(f"Received signal {signum}, initiating shutdown...")
|
|
104
|
+
if self._requested:
|
|
105
|
+
print("Shutdown already requested, ignoring signal.")
|
|
106
|
+
return
|
|
107
|
+
print("Requesting shutdown...")
|
|
108
|
+
self._requested = True
|
|
109
|
+
|
|
110
|
+
# remove the signal handler to prevent recursion
|
|
111
|
+
for sig in (SIGINT, SIGTERM):
|
|
112
|
+
if self.old_signal_handlers[sig] is not None:
|
|
113
|
+
signal.signal(sig, self.old_signal_handlers[sig])
|
|
114
|
+
|
|
115
|
+
signal.raise_signal(signum)
|
|
116
|
+
|
|
117
|
+
def setup_signal_handlers(self) -> None:
|
|
118
|
+
signal.signal(SIGINT, self.handle_signal)
|
|
119
|
+
signal.signal(SIGTERM, self.handle_signal)
|
|
120
|
+
|
|
121
|
+
|
|
79
122
|
class HttpUowContextProviderDependency:
|
|
80
123
|
|
|
81
124
|
def __init__(self, uow_provider: UnitOfWorkContextProvider) -> None:
|
|
82
125
|
self.uow_provider = uow_provider
|
|
126
|
+
self.shutdown_state = HttpShutdownState()
|
|
127
|
+
self.shutdown_state.setup_signal_handlers()
|
|
83
128
|
|
|
84
129
|
async def __call__(
|
|
85
130
|
self, websocket: WebSocket = None, request: Request = None # type: ignore
|
|
@@ -101,17 +146,18 @@ class HttpUowContextProviderDependency:
|
|
|
101
146
|
"ControllerMemberReflect, but got: {}".format(type(member))
|
|
102
147
|
)
|
|
103
148
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
with provide_shutdown_state(self.shutdown_state):
|
|
150
|
+
async with self.uow_provider(
|
|
151
|
+
AppTransactionContext(
|
|
152
|
+
controller_member_reflect=member,
|
|
153
|
+
transaction_data=(
|
|
154
|
+
HttpTransactionData(request=request)
|
|
155
|
+
if request
|
|
156
|
+
else WebSocketTransactionData(websocket=websocket)
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
):
|
|
160
|
+
yield
|
|
115
161
|
|
|
116
162
|
|
|
117
163
|
def create_http_server(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.jpeg
RENAMED
|
File without changes
|
{jararaca-0.3.12a4 → jararaca-0.3.12a5}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.12a4 → jararaca-0.3.12a5}/src/jararaca/presentation/websocket/websocket_interceptor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|