Judgement Day - The Great Refactoring
This commit is contained in:
parent
48eb2bb792
commit
de3899c69f
@ -1,96 +0,0 @@
|
|||||||
import json
|
|
||||||
import logging
|
|
||||||
import tempfile
|
|
||||||
from time import sleep
|
|
||||||
from pyinfra.config import CONFIG
|
|
||||||
|
|
||||||
from pyinfra.exceptions import AnalysisFailure, DataLoadingFailure
|
|
||||||
from pyinfra.rabbitmq import make_connection, make_channel, declare_queue
|
|
||||||
from pyinfra.storage.storages import get_storage
|
|
||||||
from pyinfra.utils.file import upload_compressed_response
|
|
||||||
|
|
||||||
|
|
||||||
def make_retry_callback(republish, max_attempts):
|
|
||||||
def get_n_previous_attempts(props):
|
|
||||||
return 0 if props.headers is None else props.headers.get("x-retry-count", 0)
|
|
||||||
|
|
||||||
def attempts_remain(n_attempts):
|
|
||||||
return n_attempts < max_attempts
|
|
||||||
|
|
||||||
def callback(channel, method, properties, body):
|
|
||||||
|
|
||||||
n_attempts = get_n_previous_attempts(properties) + 1
|
|
||||||
|
|
||||||
logging.error(f"Message failed to process {n_attempts}/{max_attempts} times: {body}")
|
|
||||||
|
|
||||||
if attempts_remain(n_attempts):
|
|
||||||
republish(channel, body, n_attempts)
|
|
||||||
channel.basic_ack(delivery_tag=method.delivery_tag)
|
|
||||||
|
|
||||||
else:
|
|
||||||
logging.exception(f"Adding to dead letter queue: {body}")
|
|
||||||
channel.basic_reject(delivery_tag=method.delivery_tag, requeue=False)
|
|
||||||
|
|
||||||
return callback
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_callback_in_retry_logic(callback, retry_callback):
|
|
||||||
def wrapped_callback(channel, method, properties, body):
|
|
||||||
try:
|
|
||||||
callback(channel, method, properties, body)
|
|
||||||
except (AnalysisFailure, DataLoadingFailure):
|
|
||||||
sleep(5)
|
|
||||||
retry_callback(channel, method, properties, body)
|
|
||||||
|
|
||||||
return wrapped_callback
|
|
||||||
|
|
||||||
|
|
||||||
def json_wrap(body_processor):
|
|
||||||
def inner(payload):
|
|
||||||
return json.dumps(body_processor(json.loads(payload)))
|
|
||||||
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
def make_callback_for_output_queue(json_wrapped_body_processor, output_queue_name):
|
|
||||||
|
|
||||||
connection = make_connection()
|
|
||||||
channel = make_channel(connection)
|
|
||||||
declare_queue(channel, output_queue_name)
|
|
||||||
|
|
||||||
def callback(channel, method, _, body):
|
|
||||||
"""
|
|
||||||
response is dossier_id, file_id and analysis result, if CONFIG.service.response.type == "file" the response only
|
|
||||||
contains file_id and dossier_id and the analysis result will be written in a json file
|
|
||||||
Args:
|
|
||||||
channel:
|
|
||||||
method:
|
|
||||||
_:
|
|
||||||
body:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
dossier_id, file_id, result = json_wrapped_body_processor(body)
|
|
||||||
|
|
||||||
result_key = CONFIG.service.response.key if CONFIG.service.response.key else "result"
|
|
||||||
result = json.dumps({"dossierId": dossier_id, "fileId": file_id, result_key: result})
|
|
||||||
|
|
||||||
if CONFIG.service.response.type == "file":
|
|
||||||
upload_compressed_response(
|
|
||||||
get_storage(CONFIG.storage.backend), CONFIG.storage.bucket, dossier_id, file_id, result
|
|
||||||
)
|
|
||||||
result = json.dumps({"dossierId": dossier_id, "fileId": file_id})
|
|
||||||
|
|
||||||
channel.basic_publish(exchange="", routing_key=output_queue_name, body=result)
|
|
||||||
channel.basic_ack(delivery_tag=method.delivery_tag)
|
|
||||||
|
|
||||||
return callback
|
|
||||||
|
|
||||||
|
|
||||||
def make_retry_callback_for_output_queue(json_wrapped_body_processor, output_queue_name, retry_callback):
|
|
||||||
callback = make_callback_for_output_queue(json_wrapped_body_processor, output_queue_name)
|
|
||||||
callback = wrap_callback_in_retry_logic(callback, retry_callback)
|
|
||||||
|
|
||||||
return callback
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
import logging
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import pika
|
|
||||||
from retry import retry
|
|
||||||
|
|
||||||
from pyinfra.exceptions import ProcessingFailure
|
|
||||||
from pyinfra.rabbitmq import make_connection, make_channel, declare_queue
|
|
||||||
|
|
||||||
|
|
||||||
class ConsumerError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@retry(ConsumerError, tries=3, delay=5, jitter=(1, 3))
|
|
||||||
def consume(queue_name: str, on_message_callback: Callable):
|
|
||||||
|
|
||||||
connection = make_connection()
|
|
||||||
channel = make_channel(connection)
|
|
||||||
declare_queue(channel, queue_name)
|
|
||||||
logging.info("Started infrastructure.")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
logging.info("Waiting for messages...")
|
|
||||||
channel.basic_consume(queue=queue_name, auto_ack=False, on_message_callback=on_message_callback)
|
|
||||||
channel.start_consuming()
|
|
||||||
|
|
||||||
except pika.exceptions.ConnectionClosedByBroker as err:
|
|
||||||
logging.critical(f"Caught a channel error: {err}, stopping.")
|
|
||||||
break
|
|
||||||
|
|
||||||
except pika.exceptions.AMQPChannelError as err:
|
|
||||||
logging.critical(f"Caught a channel error: {err}, stopping.")
|
|
||||||
break
|
|
||||||
|
|
||||||
except pika.exceptions.AMQPConnectionError as err:
|
|
||||||
logging.info("No AMPQ-connection found, retrying...")
|
|
||||||
logging.debug(err)
|
|
||||||
continue
|
|
||||||
|
|
||||||
except ProcessingFailure as err:
|
|
||||||
raise ConsumerError(f"Error while consuming {queue_name}.") from err
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
import gzip
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from pyinfra.config import CONFIG
|
|
||||||
from pyinfra.exceptions import DataLoadingFailure, AnalysisFailure, ProcessingFailure
|
|
||||||
from pyinfra.utils.file import combine_dossier_id_and_file_id_and_extension
|
|
||||||
|
|
||||||
|
|
||||||
def make_storage_data_loader(storage, bucket_name):
|
|
||||||
def get_object_name(payload: dict) -> str:
|
|
||||||
dossier_id, file_id = itemgetter("dossierId", "fileId")(payload)
|
|
||||||
object_name = combine_dossier_id_and_file_id_and_extension(
|
|
||||||
dossier_id, file_id, CONFIG.storage.target_file_extension
|
|
||||||
)
|
|
||||||
return object_name
|
|
||||||
|
|
||||||
def download(payload):
|
|
||||||
object_name = get_object_name(payload)
|
|
||||||
logging.debug(f"Downloading {object_name}...")
|
|
||||||
data = storage.get_object(bucket_name, object_name)
|
|
||||||
logging.debug(f"Downloaded {object_name}.")
|
|
||||||
return data
|
|
||||||
|
|
||||||
def decompress(data):
|
|
||||||
return gzip.decompress(data)
|
|
||||||
|
|
||||||
def load_data(payload):
|
|
||||||
try:
|
|
||||||
return decompress(download(payload))
|
|
||||||
except Exception as err:
|
|
||||||
logging.warning(f"Loading data from storage failed for {payload}.")
|
|
||||||
raise DataLoadingFailure() from err
|
|
||||||
|
|
||||||
return load_data
|
|
||||||
|
|
||||||
|
|
||||||
def make_analyzer(analysis_endpoint):
|
|
||||||
def analyze(data):
|
|
||||||
try:
|
|
||||||
logging.debug(f"Requesting analysis from {analysis_endpoint}...")
|
|
||||||
analysis_response = requests.post(analysis_endpoint, data=data)
|
|
||||||
analysis_response.raise_for_status()
|
|
||||||
analysis_response = analysis_response.json()
|
|
||||||
logging.debug(f"Received response.")
|
|
||||||
return analysis_response
|
|
||||||
except Exception as err:
|
|
||||||
logging.warning("Exception caught when calling analysis endpoint.")
|
|
||||||
raise AnalysisFailure() from err
|
|
||||||
|
|
||||||
return analyze
|
|
||||||
|
|
||||||
|
|
||||||
def make_payload_processor(load_data, analyze_file):
|
|
||||||
def process(payload: dict):
|
|
||||||
logging.info(f"Processing {payload}...")
|
|
||||||
try:
|
|
||||||
payload = json.loads(payload)
|
|
||||||
dossier_id, file_id = itemgetter("dossierId", "fileId")(payload)
|
|
||||||
data = load_data(payload)
|
|
||||||
predictions = analyze_file(data)
|
|
||||||
return dossier_id, file_id, predictions
|
|
||||||
except (DataLoadingFailure, AnalysisFailure) as err:
|
|
||||||
logging.warning(f"Processing of {payload} failed.")
|
|
||||||
raise ProcessingFailure() from err
|
|
||||||
|
|
||||||
return process
|
|
||||||
@ -20,3 +20,7 @@ class InvalidEndpoint(ValueError):
|
|||||||
|
|
||||||
class UnknownClient(ValueError):
|
class UnknownClient(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConsumerError(Exception):
|
||||||
|
pass
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
import abc
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(abc.ABC):
|
|
||||||
@abc.abstractmethod
|
|
||||||
def establish(self):
|
|
||||||
pass
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from pyampq.connection.connection import Connection
|
|
||||||
|
|
||||||
|
|
||||||
class MockConnection(Connection):
|
|
||||||
def establish(self):
|
|
||||||
pass
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
class PikaConnection:
|
|
||||||
def establish(self):
|
|
||||||
pass
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
from pyinfra.pyampq.queue_manager.queue_manager import QueueManager
|
from pyinfra.queue.queue_manager.queue_manager import QueueManager
|
||||||
|
|
||||||
|
|
||||||
class Consumer:
|
class Consumer:
|
||||||
@ -5,7 +5,7 @@ import pika
|
|||||||
|
|
||||||
from pyinfra.config import CONFIG
|
from pyinfra.config import CONFIG
|
||||||
from pyinfra.exceptions import ProcessingFailure
|
from pyinfra.exceptions import ProcessingFailure
|
||||||
from pyinfra.pyampq.queue_manager.queue_manager import QueueManager, QueueHandle
|
from pyinfra.queue.queue_manager.queue_manager import QueueHandle, QueueManager
|
||||||
|
|
||||||
logger = logging.getLogger("pika")
|
logger = logging.getLogger("pika")
|
||||||
logger.setLevel(logging.WARNING)
|
logger.setLevel(logging.WARNING)
|
||||||
@ -1,31 +0,0 @@
|
|||||||
import pika
|
|
||||||
|
|
||||||
from pyinfra.config import CONFIG
|
|
||||||
|
|
||||||
|
|
||||||
def make_channel(connection) -> pika.adapters.blocking_connection.BlockingChannel:
|
|
||||||
channel = connection.channel()
|
|
||||||
channel.basic_qos(prefetch_count=CONFIG.rabbitmq.prefetch_count)
|
|
||||||
return channel
|
|
||||||
|
|
||||||
|
|
||||||
def declare_queue(channel, queue: str):
|
|
||||||
args = {"x-dead-letter-exchange": "", "x-dead-letter-routing-key": CONFIG.rabbitmq.queues.dead_letter}
|
|
||||||
return channel.queue_declare(queue=queue, auto_delete=False, arguments=args)
|
|
||||||
|
|
||||||
|
|
||||||
def read_connection_params():
|
|
||||||
credentials = pika.PlainCredentials(CONFIG.rabbitmq.user, CONFIG.rabbitmq.password)
|
|
||||||
parameters = pika.ConnectionParameters(
|
|
||||||
host=CONFIG.rabbitmq.host,
|
|
||||||
port=CONFIG.rabbitmq.port,
|
|
||||||
heartbeat=CONFIG.rabbitmq.heartbeat,
|
|
||||||
credentials=credentials,
|
|
||||||
)
|
|
||||||
return parameters
|
|
||||||
|
|
||||||
|
|
||||||
def make_connection() -> pika.BlockingConnection:
|
|
||||||
parameters = read_connection_params()
|
|
||||||
connection = pika.BlockingConnection(parameters)
|
|
||||||
return connection
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
from pyinfra.pyampq.queue_manager.queue_manager import QueueManager, QueueHandle
|
from pyinfra.queue.queue_manager.queue_manager import QueueManager, QueueHandle
|
||||||
from pyinfra.test.queue_mock import QueueMock
|
from pyinfra.test.queue.queue_mock import QueueMock
|
||||||
|
|
||||||
|
|
||||||
def monkey_patch_queue_handle(queue) -> QueueHandle:
|
def monkey_patch_queue_handle(queue) -> QueueHandle:
|
||||||
File diff suppressed because it is too large
Load Diff
@ -6,15 +6,15 @@ import pytest
|
|||||||
|
|
||||||
from pyinfra.exceptions import UnknownClient
|
from pyinfra.exceptions import UnknownClient
|
||||||
from pyinfra.locations import TEST_DIR
|
from pyinfra.locations import TEST_DIR
|
||||||
from pyinfra.pyampq.queue_manager.pika_queue_manager import PikaQueueManager
|
from pyinfra.queue.queue_manager.pika_queue_manager import PikaQueueManager
|
||||||
from pyinfra.pyampq.queue_manager.queue_manager import QueueManager
|
from pyinfra.queue.queue_manager.queue_manager import QueueManager
|
||||||
from pyinfra.storage.adapters.azure import AzureStorageAdapter
|
from pyinfra.storage.adapters.azure import AzureStorageAdapter
|
||||||
from pyinfra.storage.adapters.s3 import S3StorageAdapter
|
from pyinfra.storage.adapters.s3 import S3StorageAdapter
|
||||||
from pyinfra.storage.clients.azure import get_azure_client
|
from pyinfra.storage.clients.azure import get_azure_client
|
||||||
from pyinfra.storage.clients.s3 import get_s3_client
|
from pyinfra.storage.clients.s3 import get_s3_client
|
||||||
from pyinfra.storage.storage import Storage
|
from pyinfra.storage.storage import Storage
|
||||||
from pyinfra.test.config import CONFIG
|
from pyinfra.test.config import CONFIG
|
||||||
from pyinfra.test.queue_manager_mock import QueueManagerMock
|
from pyinfra.test.queue.queue_manager_mock import QueueManagerMock
|
||||||
from pyinfra.test.storage.adapter_mock import StorageAdapterMock
|
from pyinfra.test.storage.adapter_mock import StorageAdapterMock
|
||||||
from pyinfra.test.storage.client_mock import StorageClientMock
|
from pyinfra.test.storage.client_mock import StorageClientMock
|
||||||
from pyinfra.visitor import StorageStrategy, ForwardingStrategy, QueueVisitor
|
from pyinfra.visitor import StorageStrategy, ForwardingStrategy, QueueVisitor
|
||||||
|
|||||||
@ -6,7 +6,7 @@ from operator import itemgetter
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pyinfra.exceptions import ProcessingFailure
|
from pyinfra.exceptions import ProcessingFailure
|
||||||
from pyinfra.pyampq.consumer import Consumer
|
from pyinfra.queue.consumer import Consumer
|
||||||
from pyinfra.visitor import get_object_descriptor, ForwardingStrategy
|
from pyinfra.visitor import get_object_descriptor, ForwardingStrategy
|
||||||
|
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class TestConsumer:
|
|||||||
|
|
||||||
requests = consumer.consume()
|
requests = consumer.consume()
|
||||||
|
|
||||||
logger = logging.getLogger("pyinfra.pyampq.queue_manager.pika_queue_manager")
|
logger = logging.getLogger("pyinfra.queue.queue_manager.pika_queue_manager")
|
||||||
logger.addFilter(lambda record: False)
|
logger.addFilter(lambda record: False)
|
||||||
|
|
||||||
with pytest.raises(DebugError):
|
with pytest.raises(DebugError):
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import json
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pyinfra.core import make_analyzer, make_payload_processor
|
|
||||||
from pyinfra.test.config import CONFIG
|
|
||||||
|
|
||||||
|
|
||||||
@patch("requests.post")
|
|
||||||
def test_analyse_returns_analysis(mock_post, storage_data, mock_response):
|
|
||||||
mock_post.return_value = mock_response
|
|
||||||
|
|
||||||
analyze = make_analyzer(CONFIG.mock_analysis_endpoint)
|
|
||||||
response = analyze(storage_data)
|
|
||||||
|
|
||||||
assert response == storage_data
|
|
||||||
|
|
||||||
|
|
||||||
@patch("requests.post")
|
|
||||||
def test_process_returns_dossier_id_file_id_predictions(
|
|
||||||
mock_post, mock_make_load_data, storage_data, mock_response, mock_payload
|
|
||||||
):
|
|
||||||
mock_post.return_value = mock_response
|
|
||||||
|
|
||||||
analyze = make_analyzer(CONFIG.mock_analysis_endpoint)
|
|
||||||
mock_load_data = mock_make_load_data
|
|
||||||
|
|
||||||
process = make_payload_processor(mock_load_data, analyze)
|
|
||||||
dossier_id, file_id, predictions = process(mock_payload)
|
|
||||||
|
|
||||||
assert dossier_id == json.loads(mock_payload)["dossierId"]
|
|
||||||
assert file_id == json.loads(mock_payload)["fileId"]
|
|
||||||
assert predictions == storage_data
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
"""Defines utilities for different operations on files."""
|
|
||||||
|
|
||||||
import gzip
|
|
||||||
import os
|
|
||||||
|
|
||||||
from pyinfra.config import CONFIG
|
|
||||||
|
|
||||||
|
|
||||||
def combine_dossier_id_and_file_id_and_extension(dossier_id, file_id, extension):
|
|
||||||
return f"{dossier_id}/{file_id}{extension}"
|
|
||||||
|
|
||||||
|
|
||||||
def upload_compressed_response(storage, bucket_name, dossier_id, file_id, result) -> None:
|
|
||||||
data = gzip.compress(result.encode())
|
|
||||||
path_gz = combine_dossier_id_and_file_id_and_extension(dossier_id, file_id, CONFIG.service.response.extension)
|
|
||||||
storage.put_object(bucket_name, path_gz, data)
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import logging
|
|
||||||
import time
|
|
||||||
from functools import partial, wraps
|
|
||||||
from math import exp
|
|
||||||
from typing import Tuple, Type, Callable
|
|
||||||
|
|
||||||
|
|
||||||
class NoAttemptsLeft(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MaxTimeoutReached(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class _MethodDecoratorAdaptor(object):
|
|
||||||
def __init__(self, decorator, func):
|
|
||||||
self.decorator = decorator
|
|
||||||
self.func = func
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
return self.decorator(self.func)(*args, **kwargs)
|
|
||||||
|
|
||||||
def __get__(self, obj, objtype):
|
|
||||||
return partial(self.__call__, obj)
|
|
||||||
|
|
||||||
|
|
||||||
def auto_adapt_to_methods(decorator):
|
|
||||||
"""Allows you to use the same decorator on methods and functions,
|
|
||||||
hiding the self argument from the decorator."""
|
|
||||||
|
|
||||||
def adapt(func):
|
|
||||||
return _MethodDecoratorAdaptor(decorator, func)
|
|
||||||
|
|
||||||
return adapt
|
|
||||||
|
|
||||||
|
|
||||||
def max_attempts(
|
|
||||||
n_attempts: int = 5, exceptions: Tuple[Type[Exception]] = None, timeout: float = 0.1, max_timeout: float = 10
|
|
||||||
) -> Callable:
|
|
||||||
"""Function decorator that attempts to run the wrapped function a certain number of times. Timeouts increase
|
|
||||||
exponentially according to `Tₖ ≔ t eᵏ`, where `t` is the timeout factor `timeout` and `k` is the attempt number.
|
|
||||||
If `∑ᵢ Tᵢ > mₜ` at the `i-th` attempt, where `mₜ` is the maximum timeout, then the function raises
|
|
||||||
MaxTimeoutReached. If `k > mₐ`, where `mₐ` is the maximum number of attempts allowed, then the function
|
|
||||||
raises NoAttemptsLeft.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
n_attempts: Number of times to attempt running the wrapped function.
|
|
||||||
exceptions: Exceptions to catch for a re-attempt.
|
|
||||||
timeout: Timeout factor in seconds.
|
|
||||||
max_timeout: Maximum allowed timeout.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
MaxTimeoutReached
|
|
||||||
NoAttemptsLeft
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Wrapped function.
|
|
||||||
"""
|
|
||||||
if not exceptions:
|
|
||||||
exceptions = (Exception,)
|
|
||||||
assert isinstance(exceptions, tuple)
|
|
||||||
|
|
||||||
@auto_adapt_to_methods
|
|
||||||
def inner(func):
|
|
||||||
@wraps(func)
|
|
||||||
def inner(*args, **kwargs):
|
|
||||||
def run_attempt(attempt, timeout_aggr=0):
|
|
||||||
if attempt:
|
|
||||||
try:
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
except exceptions as err:
|
|
||||||
|
|
||||||
attempt_num = n_attempts - attempt + 1
|
|
||||||
next_timeout = timeout * exp(attempt_num - 1) # start with timeout * e^0 = timeout
|
|
||||||
|
|
||||||
logging.warn(f"{func.__name__} failed; attempt {attempt_num} of {n_attempts}")
|
|
||||||
|
|
||||||
time_left = max(0, max_timeout - timeout_aggr)
|
|
||||||
if time_left:
|
|
||||||
sleep_for = min(next_timeout, time_left)
|
|
||||||
time.sleep(sleep_for)
|
|
||||||
return run_attempt(attempt - 1, timeout_aggr + sleep_for)
|
|
||||||
else:
|
|
||||||
logging.exception(err)
|
|
||||||
raise MaxTimeoutReached(
|
|
||||||
f"{func.__name__} reached maximum timeout ({max_timeout}) after {attempt_num} attempts."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise NoAttemptsLeft(f"{func.__name__} failed {n_attempts} times; all attempts expended.")
|
|
||||||
|
|
||||||
return run_attempt(n_attempts)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
|
|
||||||
return inner
|
|
||||||
@ -7,7 +7,6 @@ from tqdm import tqdm
|
|||||||
|
|
||||||
from pyinfra.config import CONFIG
|
from pyinfra.config import CONFIG
|
||||||
from pyinfra.storage.storages import get_s3_storage
|
from pyinfra.storage.storages import get_s3_storage
|
||||||
from pyinfra.utils.file import combine_dossier_id_and_file_id_and_extension
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
@ -58,3 +57,13 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
elif args.command == "purge":
|
elif args.command == "purge":
|
||||||
storage.clear_bucket(bucket_name)
|
storage.clear_bucket(bucket_name)
|
||||||
|
|
||||||
|
|
||||||
|
def combine_dossier_id_and_file_id_and_extension(dossier_id, file_id, extension):
|
||||||
|
return f"{dossier_id}/{file_id}{extension}"
|
||||||
|
|
||||||
|
|
||||||
|
def upload_compressed_response(storage, bucket_name, dossier_id, file_id, result) -> None:
|
||||||
|
data = gzip.compress(result.encode())
|
||||||
|
path_gz = combine_dossier_id_and_file_id_and_extension(dossier_id, file_id, CONFIG.service.response.extension)
|
||||||
|
storage.put_object(bucket_name, path_gz, data)
|
||||||
@ -1,7 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
import pika
|
||||||
|
|
||||||
from pyinfra.config import CONFIG
|
from pyinfra.config import CONFIG
|
||||||
from pyinfra.rabbitmq import make_channel, declare_queue, make_connection
|
|
||||||
from pyinfra.storage.storages import get_s3_storage
|
from pyinfra.storage.storages import get_s3_storage
|
||||||
|
|
||||||
|
|
||||||
@ -20,6 +21,23 @@ def build_message_bodies():
|
|||||||
).encode()
|
).encode()
|
||||||
|
|
||||||
|
|
||||||
|
def make_channel(connection) -> pika.adapters.blocking_connection.BlockingChannel:
|
||||||
|
channel = connection.channel()
|
||||||
|
channel.basic_qos(prefetch_count=CONFIG.rabbitmq.prefetch_count)
|
||||||
|
return channel
|
||||||
|
|
||||||
|
|
||||||
|
def declare_queue(channel, queue: str):
|
||||||
|
args = {"x-dead-letter-exchange": "", "x-dead-letter-routing-key": CONFIG.rabbitmq.queues.dead_letter}
|
||||||
|
return channel.queue_declare(queue=queue, auto_delete=False, arguments=args)
|
||||||
|
|
||||||
|
|
||||||
|
def make_connection() -> pika.BlockingConnection:
|
||||||
|
parameters = read_connection_params()
|
||||||
|
connection = pika.BlockingConnection(parameters)
|
||||||
|
return connection
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
connection = make_connection()
|
connection = make_connection()
|
||||||
@ -34,3 +52,14 @@ if __name__ == "__main__":
|
|||||||
for method_frame, _, body in channel.consume(queue=CONFIG.rabbitmq.queues.output):
|
for method_frame, _, body in channel.consume(queue=CONFIG.rabbitmq.queues.output):
|
||||||
print(f"Received {json.loads(body)}")
|
print(f"Received {json.loads(body)}")
|
||||||
channel.basic_ack(method_frame.delivery_tag)
|
channel.basic_ack(method_frame.delivery_tag)
|
||||||
|
|
||||||
|
|
||||||
|
def read_connection_params():
|
||||||
|
credentials = pika.PlainCredentials(CONFIG.rabbitmq.user, CONFIG.rabbitmq.password)
|
||||||
|
parameters = pika.ConnectionParameters(
|
||||||
|
host=CONFIG.rabbitmq.host,
|
||||||
|
port=CONFIG.rabbitmq.port,
|
||||||
|
heartbeat=CONFIG.rabbitmq.heartbeat,
|
||||||
|
credentials=credentials,
|
||||||
|
)
|
||||||
|
return parameters
|
||||||
@ -5,11 +5,10 @@ import requests
|
|||||||
from retry import retry
|
from retry import retry
|
||||||
|
|
||||||
from pyinfra.config import CONFIG, make_art
|
from pyinfra.config import CONFIG, make_art
|
||||||
from pyinfra.consume import ConsumerError
|
from pyinfra.exceptions import AnalysisFailure, ConsumerError
|
||||||
from pyinfra.exceptions import AnalysisFailure
|
|
||||||
from pyinfra.flask import run_probing_webserver, set_up_probing_webserver
|
from pyinfra.flask import run_probing_webserver, set_up_probing_webserver
|
||||||
from pyinfra.pyampq.consumer import Consumer
|
from pyinfra.queue.consumer import Consumer
|
||||||
from pyinfra.pyampq.queue_manager.pika_queue_manager import PikaQueueManager
|
from pyinfra.queue.queue_manager.pika_queue_manager import PikaQueueManager
|
||||||
from pyinfra.storage.storages import get_storage
|
from pyinfra.storage.storages import get_storage
|
||||||
from pyinfra.visitor import QueueVisitor, StorageStrategy
|
from pyinfra.visitor import QueueVisitor, StorageStrategy
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user