Skip to content

100% cpu problem with retrying #299

@ArtDu

Description

@ArtDu

User, [07.11.2022 18:37]
дали небольшую нагрузку на cartridge-driver 0.9.1 (это java), и получили 100% CPU
java-клиент создан с политикой повторов. но до повторов не доходит, но тот код, который выполняется библиотекой для обработки потенциальных повторов, заваливает 6-ядерный процессор.
нагрузка небольшая: 200 тыс операций отправляются на роутер. после каждой 1000 асинхронных операций ждем их завершения (всех их), и так 200 раз. то есть синхронно-асинхронный пример. Все это добро работает 25 секунд. 100% CPU

User, [07.11.2022 18:37]
если убрать возможность повторов, то проблемы нет

User, [07.11.2022 18:39]
вот так создаем клиента:

        TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> client = TarantoolClientFactory.createClient()
                .withAddresses(tntProperties.getNodes().stream()
                        .map(node -> new TarantoolServerAddress(node.getHost(), node.getPort()))
                        .collect(Collectors.toList()))
                .withCredentials(tntProperties.getUser(), tntProperties.getPassword())
                .withConnectTimeout(1000 * connectTimeoutSec)
                .withReadTimeout(1000 * readTimeoutSec)
                .withRequestTimeout(1000 * requestTimeoutSec)
                .withConnectionSelectionStrategy(TarantoolConnectionSelectionStrategyType.PARALLEL_ROUND_ROBIN)
/*  если это раскомментировать, то привет 100% CPU              .withRetryingIndefinitely(TarantoolRequestRetryPolicies.retryNetworkErrors(),
                        policy -> policy
                                .withDelay(delayBeforeRetryMs)
                                .withRequestTimeout(1000 * requestTimeoutSec)
                                .withOperationTimeout(1000 * operationTimeoutSec)  // Попытки закончатся, как истечет этот таймаут
                )*/
                .withProxyMethodMapping()
                .build();

User, [07.11.2022 18:44]
виновата, судя по всему, TarantoolRequestRetryPolicies.wrapOperation

Artyom Dubinin, [07.11.2022 23:51]
а запросы без ретраинга прилетают успешно отработанные?

User, [07.11.2022 23:52]
все без ошибок

Artyom Dubinin, [07.11.2022 23:57]
а с ретраингом какие ошибки?

User, [08.11.2022 00:02]
я где то писал про ошибки ? ошибок нет вообще. проблема в 100% CPU

Artyom Dubinin, [08.11.2022 00:04]
Т.е. во врапере не разу ретраи не происходит?

User, [08.11.2022 00:05]
нет. да и не может дойти, потому что выполняется до истчения таймаута

Artyom Dubinin, [08.11.2022 00:07]
при чем тут таймаут?

User, [08.11.2022 00:14]

return operation.get().get(getRequestTimeout(), TimeUnit.MILLISECONDS);

User, [08.11.2022 00:40]
мне кажется проблема в том, как реализована обработка таймаута. в ForkJoinPool создается более 500 заданий которые висят на i/o. пул разбухает. каждый get с таймаутом видимо не бесплатный. и/или не бесплатно обслуживание самого пула. Детально не разбирался. но все время уходит на этот wrapper, как показывает jvm.

если вставлять асинхронно больше 1000, то и пул больше, там уже тысячи воркеров

User, [08.11.2022 14:27]
дополнительно:
TarantoolConnectionImpl: вот в этот листенер вообще не попадаем (в отладчике, да и в логе нет ничего):

        CompletableFuture<T> requestFuture = requestManager.submitRequest(request, resultMapper);
        channel.writeAndFlush(request).addListener(f -> {
            if (!f.isSuccess()) {
                requestFuture.completeExceptionally(
                        new RuntimeException("Failed to send the request to Tarantool server", f.cause()));
            } else {
                logger.debug("Request {} sent, status Success", request.getHeader().getSync());
            }
        });

и вызывает сомнение двойная обработка requestTimeout:

  • RequestFutureManager.submitRequest -> timeoutScheduler.schedule( () -> ...., requestTimeout, ...)
  • TarantoolRequestRetryPolicies.wrapOperation -> operation.get().get(getRequestTimeout(), ... )

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

но напрямую к "CPU 100%" это вроде бы не относится

Artyom Dubinin, [08.11.2022 14:35]
Там по идеи не должно быть никаких подводных камней с ретраингом, потому что ретраинг ничем не отличается от обычного вызова, кроме того, что он ловит эксепшены и заново запускает вызов. Поэтому пока гипотиза такая, что ретраинг постоянно вызывается и delay маленький между ретраями и поэтому cpu в 100. Я задачу добавил к нам, посмотрю

User, [08.11.2022 14:36]
не вызывается. в дебаге туда не попадаем

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions