Что было. Приложение открывает к Redis соединение на каждую операцию и сразу закрывает. Примерно на 28 тысячах операций в минуту начинаются ошибки connect: cannot assign requested address, хотя Redis свободен и отвечает мгновенно.
Почему именно 28 тысяч. Каждое исходящее соединение занимает локальный порт из диапазона net.ipv4.ip_local_port_range, по умолчанию 32768–60999, то есть около 28 тысяч портов. Закрытое соединение держит порт ещё 60 секунд в состоянии TIME_WAIT. Если за минуту открыть больше 28 тысяч коротких соединений к одной паре адрес:порт, свободных портов не остаётся. Это ограничение TCP на стороне клиента, Redis тут ни при чём; то же будет с MySQL, HTTP-клиентом к внутреннему API, чем угодно.
Настоящее решение: не открывать соединения на каждую операцию. Пул соединений или одно долгоживущее соединение на рабочий поток. Во всех клиентских библиотеках Redis пул есть из коробки; в HTTP-клиентах это keep-alive и переиспользование сессии (requests.Session, агент с keepAlive: true в Node, http.Client с общим Transport в Go). После этого число портов перестаёт зависеть от числа операций.
Паллиативы на уровне ядра, если код поправить нельзя быстро:
net.ipv4.ip_local_port_range = 1024 65535: даёт около 64 тысяч портов вместо 28.net.ipv4.tcp_tw_reuse = 1: разрешает повторно использовать порт вTIME_WAITдля исходящих соединений, если временные метки TCP включены. Безопасно.net.ipv4.tcp_tw_recycle: не включать. Ломает соединения клиентов за NAT и удалён из ядра начиная с 4.12. Его до сих пор советуют в старых статьях.net.ipv4.tcp_fin_timeoutнаTIME_WAITне влияет, вопреки популярному совету: он проFIN_WAIT_2.
Как убедиться, что это ваш случай.
ss -s # сводка: сколько в timewait
ss -tan state time-wait | awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
Если десятки тысяч TIME_WAIT от одного локального адреса к одному удалённому порту, диагноз ясен.
Со стороны сервера (когда TIME_WAIT копятся на нём) вреда почти нет: это лишь память под записи, и портов сервера они не занимают. Бороться с ними на сервере не нужно.
Параметры описаны в документации ядра Linux.