diff --git a/API.md b/API.md index a01061d..d3274dc 100644 --- a/API.md +++ b/API.md @@ -4,7 +4,7 @@ Внутри объекта ведётся учёт скорости отправки запросов к серверу, поэтому важно, чтобы все запросы приложения в отношении одного аккаунта с одного IP-адреса отправлялись из одного экземпляра `Bitrix`. -### Метод ` __init__(self, webhook: str, token_func: Awaitable = None, verbose: bool = True, respect_velocity_policy: bool = True, request_pool_size: int = 50, requests_per_second: float = 2.0, ssl: bool = True, client: aiohttp.ClientSession = None):` +### Метод ` __init__(self, webhook: str, token_func: Awaitable = None, verbose: bool = True, respect_velocity_policy: bool = True, request_pool_size: int = 50, requests_per_second: float = 2.0, batch_size: int = 50, operating_time_limit: int = 480, ssl: bool = True, client: aiohttp.ClientSession = None):` Создаёт клиента для доступа к Битрикс24. #### Параметры @@ -16,6 +16,11 @@ можно отправить на сервер без ожидания - `requests_per_second: float = 2.0` - максимальная скорость запросов, которая будет использоваться после переполнения пула +- `batch_size: int = 50` - максимальное количество запросов, которые +будут отправляться на сервер в одном батче +- `operating_time_limit: int = 480` - максимальное допустимое время отработки +запросов к одному методу REST API в секундах, допустимое за 10 минут, +после которого запросы будут замедляться - `ssl: bool = True` - использовать ли проверку SSL-сертификата при HTTP-соединениях с сервером Битрикс. - `client: aiohttp.ClientSession = None` - использовать для HTTP-вызовов клиента, инициализированного и настроенного пользователем. diff --git a/README.md b/README.md index a5465ec..febf9fe 100644 --- a/README.md +++ b/README.md @@ -239,8 +239,9 @@ import logging logging.getLogger('fast_bitrix24').addHandler(logging.StreamHandler()) ``` -### Я хочу добавить несколько лидов списком, но получаю ошибку сервера. -Оберните вызов `call()` в `slow`: +### Я получаю ошибку сервера, когда сильно его нагружаю. Что делать? +#### Способ 1 +Оберните обращения к Битриксу в `slow`: ```python with bx.slow(): @@ -249,6 +250,19 @@ with bx.slow(): [См. подробнее](API.md#контекстный-менеджер-slowmaxconcurrentrequests-int--1) о `slow`. +#### Способ 2 +При инициализации клиента поэкспериментируйте со значениями параметров, регулирующих нагрузку на сервер: + +```python +bx = Bitrix( + webhook, + request_pool_size = 20, # по умолчанию - 50 + requests_per_second = 1.0, # по умолчанию - 1.0 + batch_size = 20, # по умолчанию - 50 + operating_time_limit = 100, # по умолчанию - 480 +) +``` + ### Я хочу вызвать `call()` только один раз, а не по списку. Передавайте параметры запроса методу `call()`, он может делать как запросы по списку, так и единичный запрос: diff --git a/fast_bitrix24/bitrix.py b/fast_bitrix24/bitrix.py index 8b6173a..c4f4fce 100644 --- a/fast_bitrix24/bitrix.py +++ b/fast_bitrix24/bitrix.py @@ -35,6 +35,8 @@ def __init__( respect_velocity_policy: bool = True, request_pool_size: int = 50, requests_per_second: float = 2.0, + batch_size: int = 50, + operating_time_limit: int = 480, client: aiohttp.ClientSession = None, ssl: bool = True, ): @@ -53,6 +55,11 @@ def __init__( можно отправить на сервер без ожидания - `requests_per_second: float = 2.0` - максимальная скорость запросов, которая будет использоваться после переполнения пула + - `batch_size: int = 50` - максимальное количество запросов, которые + будут отправляться на сервер в одном батче + - `operating_time_limit: int = 480` - максимальное допустимое время отработки + запросов к одному методу REST API в секундах, допустимое за 10 минут, + после которого запросы будут замедляться - `ssl: bool = True` - использовать ли проверку SSL-сертификата при HTTP-соединениях с сервером Битрикс. - `client: aiohttp.ClientSession = None` - использовать для HTTP-вызовов @@ -69,10 +76,12 @@ def __init__( respect_velocity_policy=respect_velocity_policy, request_pool_size=request_pool_size, requests_per_second=requests_per_second, + operating_time_limit=operating_time_limit, ssl=ssl, client=client, ) self.verbose = verbose + self.batch_size = batch_size @log async def get_all(self, method: str, params: dict = None) -> Union[list, dict]: diff --git a/fast_bitrix24/mult_request.py b/fast_bitrix24/mult_request.py index a4e2233..eb20ad7 100644 --- a/fast_bitrix24/mult_request.py +++ b/fast_bitrix24/mult_request.py @@ -5,7 +5,7 @@ from tqdm.auto import tqdm from .server_response import ServerResponseParser -from .srh import BITRIX_MAX_BATCH_SIZE, ServerRequestHandler +from .srh import ServerRequestHandler from .utils import http_build_query @@ -38,7 +38,7 @@ def generate_tasks(self): batches = ( self.package_batch(chunk) - for chunk in chunked(self.item_list, BITRIX_MAX_BATCH_SIZE) + for chunk in chunked(self.item_list, self.bitrix.batch_size) ) for batch in batches: diff --git a/fast_bitrix24/srh.py b/fast_bitrix24/srh.py index b612fa0..25f933d 100644 --- a/fast_bitrix24/srh.py +++ b/fast_bitrix24/srh.py @@ -12,10 +12,8 @@ from .logger import logger from .utils import _url_valid -BITRIX_MAX_BATCH_SIZE = 50 BITRIX_MAX_CONCURRENT_REQUESTS = 50 -BITRIX_MAX_REQUEST_RUNNING_TIME = 480 BITRIX_MEASUREMENT_PERIOD = 10 * 60 MAX_RETRIES = 10 @@ -50,9 +48,6 @@ class ServerRequestHandler: Основная цель - вести учет количества запросов, которые можно передать серверу Битрикс без получения ошибки `5XX`. - - Используется как контекстный менеджер, оборачивающий несколько - последовательных запросов к серверу. """ def __init__( @@ -62,6 +57,7 @@ def __init__( respect_velocity_policy: bool, request_pool_size: int, requests_per_second: float, + operating_time_limit: int, client, ssl: bool = True, ): @@ -78,6 +74,8 @@ def __init__( self.active_runs = 0 + self.operating_time_limit = operating_time_limit + # если пользователь при инициализации передал клиента со своими настройками, # то будем использовать его клиента self.client_provided_by_user = bool(client) @@ -249,7 +247,7 @@ async def acquire(self, method: str): if self.respect_velocity_policy: if method not in self.method_throttlers: self.method_throttlers[method] = SlidingWindowThrottler( - BITRIX_MAX_REQUEST_RUNNING_TIME, BITRIX_MEASUREMENT_PERIOD + self.operating_time_limit, BITRIX_MEASUREMENT_PERIOD ) async with self.method_throttlers[method].acquire(): diff --git a/tests/test_auth.py b/tests/test_auth.py index 6ecb0a9..e07be17 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -4,12 +4,13 @@ from typing import Dict, List, Union + class MockSRH(ServerRequestHandler): def __init__(self, token_func, response: Union[Dict, List[Dict]]): self.response = response if isinstance(response, list) else [response] self.element_no = 0 - super().__init__("https://google.com/path", token_func, False, 50, 2, None) + super().__init__("https://google.com/path", token_func, False, 50, 2, 480, None) async def request_attempt(self, *args, **kwargs): result = self.response[self.element_no] @@ -28,18 +29,21 @@ def test_first_request(): raise AssertionError + @pytest.mark.skip(reason="TODO") def test_auth_success(): # нужно проверить, что серверу передается токен, полученный от token_func raise AssertionError + @pytest.mark.skip(reason="TODO") def test_auth_failure(): # нужно проверить, что вызывается функция запроса токена, если сервер вернул ошибку токена raise AssertionError + @pytest.mark.skip(reason="TODO") def test_abort_on_multiple_failures(): # нужно проверить, что если token_func регулярно возвращает токен, который отвергается сервером, @@ -47,6 +51,7 @@ def test_abort_on_multiple_failures(): raise AssertionError + def test_expired_token(bx_dummy): # нужно проверить, что вызывается функция запроса токена, если токен истек diff --git a/tests/test_server_responses.py b/tests/test_server_responses.py index 313df0e..d54211c 100644 --- a/tests/test_server_responses.py +++ b/tests/test_server_responses.py @@ -17,7 +17,7 @@ def __init__(self, response: Union[Dict, List[Dict]]): self.response = response if isinstance(response, list) else [response] self.element_no = -1 - super().__init__("https://google.com/path", None, False, 50, 2, None) + super().__init__("https://google.com/path", None, False, 50, 2, 480, None) async def single_request(self, *args, **kwargs): self.element_no += 1 diff --git a/tests/test_srh.py b/tests/test_srh.py index e3cb535..62321ce 100644 --- a/tests/test_srh.py +++ b/tests/test_srh.py @@ -4,12 +4,13 @@ from fast_bitrix24.srh import ServerRequestHandler import aiohttp + @pytest.mark.asyncio async def test_request_attempt(): # Create a mock response mock_response = Mock(spec=aiohttp.ClientResponse) mock_response.status = 200 - mock_response.json.return_value = {'time': {'operating': 1000}} + mock_response.json.return_value = {"time": {"operating": 1000}} @contextlib.asynccontextmanager async def mock_post(url, json, ssl): @@ -18,11 +19,13 @@ async def mock_post(url, json, ssl): mock_session = AsyncMock() mock_session.post = mock_post - handler = ServerRequestHandler('https://google.com/webhook', None, True, 50, 2, mock_session) + handler = ServerRequestHandler( + "https://google.com/webhook", None, True, 50, 2, 480, mock_session + ) # Call the method - result = await handler.request_attempt('method', {'param': 'value'}) + result = await handler.request_attempt("method", {"param": "value"}) # Assert the expected behavior - assert result == {'time': {'operating': 1000}} + assert result == {"time": {"operating": 1000}} assert "method" in handler.method_throttlers