nkrkv / pyinsales

InSales e-commerce platform API bindings
MIT License
13 stars 16 forks source link

Твики подключения #23

Closed knopki closed 3 years ago

knopki commented 3 years ago

Теперь поддерживается подключение по HTTPS. По-умолчанию остаётся HTTP.

Ошибки, возвращаемые InSales, теперь декодируются из байт в текст и их может прочитать человек.

Если после исчерпания лимита на количество запросов к API мы получаем заголовок Retry-After с количеством секунд, то мы повторяем не через retry_timeout (1s by default), а ждём столько, сколько нам рекомендовали. Однако, ограничиваем время ожидания 60 секундами, чтобы не быть уязвимыми к какой-нибудь ошибке с другой стороны, когда нас попросили подождать миллион лет. Благодаря этому значительно меньше тратятся ресурсы с нашей стороны и со стороны InSales, особенно при многопоточной работе, когда у каждого потока свой retry_after. Плюс запросы, возвращающие 503, тоже увеличивают счетчик запросов.

Далее немного спорная опция. InSales даёт делать 500 запросов в 5 минут. Причём этот лимит не размазан как-то во времени, а прям строго через пять минут после первого запроса в серии обнуляется счетчик сделанных запросов и у тебя опять есть 500 запросов. В некоторых сценариях получается, что скрипт употребляет весь лимит за несколько секунд, а потом почти пять минут "висит". CircleCI, например, несколько раз прибивал job из-за отсутствия вывода :) Плюс, если трудится несколько скриптов одновременно, то один может просто напрочь заблокировать остальных.

InSales передаёт в заголовке API-Usage-Limit максимальное количество запросов в этот промежуток времени и сколько их уже было. Т.е. фактически известен баланс после каждого запроса.

Добавляется возможность включить флаг throttle (выключен по умолчанию) и даже передать функцию замедления throttle_fn. Текущий баланс передаётся в throttle_fn, она возвращает задержку в секундах. Можно нафантазировать себе линейный рейт запросов 5*60/500, например. По-умолчанию подобрана такая функция, что сумма числового ряда от 0 до 500 даёт как раз 300 секунд ожидания. При этом большую часть пути ожидания нет (потому что ожидание меньше round trip) и только в самом конце ожидание между запросами радикально прогрессирует до максимальных 10 секунд. При работе в один поток баланс никогда не выгребается, потому что после последнего запроса добавляется новые 500 запросов и всё опять ускоряется.

Побочное явление: Использование datetime для Connection.retry_after делает удобным недобросовестное использование, когда делается пул подключений с разными credentials и запрос делается через то, где ждать меньше.

knopki commented 3 years ago

@nkrkv Ребейзнул последний коммит. throttle_fn теперь принимает current и total из API-Usage-Limit и может быть задана аргументом throttle. Саму функцию подкрутил, чтобы начинала с 100мс задержки, а потом накидывала по чуть-чуть. В сумме опять же 5 минут.

Навставлял локов где попало.

Задержка считается не от "сейчас", а от начала предыдущего запроса.

Для 503 и Retry-After выставляется именно "покуда ждать". Для API-Usage-Limit увеличивается текущее "покуда ждать". Благодаря этому корректно работает с тредами - треды стартуют одновременно и они начинают постепенно отставать друг от друга из-за удаляющейся даты ожидания. Когда счётчик обнуляется, то опять начинают строем ходить.