Solucionar problemas ao usar o SDK Java v4 do Azure Cosmos DB com a API para contas NoSQL

APLICA-SE A: NoSQL

Importante

Este artigo aborda a solução de problemas somente para o SDK Java v4 do Azure Cosmos DB. Consulte as Notas de versão do SDK Java v4 do Azure Cosmos DB, o repositório Maven e as dicas de desempenho para obter mais informações. Se você estiver usando uma versão mais antiga do que a v4, consulte o guia Migrar para o SDK Java do Azure Cosmos DB v4 para obter ajuda na atualização para a v4.

Este artigo aborda problemas comuns, soluções alternativas, etapas de diagnóstico e ferramentas quando você usa o SDK Java v4 do Azure Cosmos DB com contas do Azure Cosmos DB para NoSQL. O SDK Java v4 do Azure Cosmos DB fornece representação lógica do lado do cliente para acessar o Azure Cosmos DB para NoSQL. Este artigo descreve as ferramentas e abordagens para o ajudar se encontrar problemas.

Comece com esta lista:

  • Dê uma olhada na seção Problemas comuns e soluções alternativas neste artigo.
  • Observe o SDK do Java no repositório central do Azure Cosmos DB, que está disponível em código aberto no GitHub. Ele tem uma seção de problemas que é monitorada ativamente. Verifique se algum problema semelhante com uma solução alternativa já foi arquivado. Uma dica útil é filtrar os *cosmos:v4-item* problemas pela tag .
  • Analise as dicas de desempenho para o SDK Java v4 do Azure Cosmos DB e siga as práticas sugeridas.
  • Leia o resto deste artigo, se não encontrou uma solução. Em seguida, registre um problema no GitHub. Se houver uma opção para adicionar tags ao seu problema no GitHub, adicione uma *cosmos:v4-item* tag.

Capture os diagnósticos

A base de dados, o contentor, o item e as respostas de consulta no SDK Java V4 têm uma propriedade de Diagnóstico. Essa propriedade registra todas as informações relacionadas à solicitação única, incluindo se houve novas tentativas ou falhas transitórias.

Os Diagnósticos são retornados como uma cadeia de caracteres. A cadeia de caracteres muda a cada versão à medida que é melhorada para melhor solucionar problemas de diferentes cenários. Com cada versão do SDK, a cadeia de caracteres pode quebrar seu formato. Não analise a cadeia de caracteres para evitar a quebra de alterações.

O exemplo de código a seguir mostra como ler logs de diagnóstico usando o Java V4 SDK:

Importante

Recomendamos validar a versão mínima recomendada do Java V4 SDK e garantir que você esteja usando esta versão ou superior. Você pode verificar a versão recomendada aqui.

Operações com Base de Dados

CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists(databaseName);
CosmosDiagnostics diagnostics = databaseResponse.getDiagnostics();
logger.info("Create database diagnostics : {}", diagnostics); 

Operações de Contentores

CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerProperties,
                  throughputProperties);
CosmosDiagnostics diagnostics = containerResponse.getDiagnostics();
logger.info("Create container diagnostics : {}", diagnostics);

Operações de item

// Write Item
CosmosItemResponse<Family> item = container.createItem(family, new PartitionKey(family.getLastName()),
                    new CosmosItemRequestOptions());
        
CosmosDiagnostics diagnostics = item.getDiagnostics();
logger.info("Create item diagnostics : {}", diagnostics);
        
// Read Item
CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
        
CosmosDiagnostics diagnostics = familyCosmosItemResponse.getDiagnostics();
logger.info("Read item diagnostics : {}", diagnostics);

Operações de consulta

String sql = "SELECT * FROM c WHERE c.lastName = 'Witherspoon'";
        
CosmosPagedIterable<Family> filteredFamilies = container.queryItems(sql, new CosmosQueryRequestOptions(),
                    Family.class);
        
//  Add handler to capture diagnostics
filteredFamilies = filteredFamilies.handle(familyFeedResponse -> {
    logger.info("Query Item diagnostics through handle : {}", 
    familyFeedResponse.getCosmosDiagnostics());
});
        
//  Or capture diagnostics through iterableByPage() APIs.
filteredFamilies.iterableByPage().forEach(familyFeedResponse -> {
    logger.info("Query item diagnostics through iterableByPage : {}",
    familyFeedResponse.getCosmosDiagnostics());
});

Exceções do Azure Cosmos DB

try {
  CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
} catch (CosmosException ex) {
  CosmosDiagnostics diagnostics = ex.getDiagnostics();
  logger.error("Read item failure diagnostics : {}", diagnostics);
}

Registrando o diagnóstico

As versões do Java V4 SDK v4.43.0 e superiores suportam o registro automático do Cosmos Diagnostics para todas as solicitações ou erros que atendam a determinados critérios. Os desenvolvedores de aplicativos podem definir limites para latência (para operações pontuais (criar, ler, substituir, upsert, patch) ou não pontuais (consulta, alterar feed, volume e lote)), solicitar cobrança e tamanho da carga útil. Se as solicitações excederem esses limites definidos, o cosmos diagnostics para essas solicitações será emitido automaticamente.

Por padrão, o Java v4 SDK registra esses diagnósticos automaticamente em um formato específico. No entanto, isso pode ser alterado implementando CosmosDiagnosticsHandler a interface e fornecendo seu próprio manipulador de diagnóstico personalizado.

Estes CosmosDiagnosticsThresholds e CosmosDiagnosticsHandler podem ser usados no CosmosClientTelemetryConfig objeto, que deve ser passado para durante CosmosClientBuilder a criação de sincronização ou cliente assíncrono.

NOTA: Esses limites de diagnóstico são aplicados em diferentes tipos de diagnóstico, incluindo registro, rastreamento e telemetria do cliente.

Os exemplos de código a seguir mostram como definir limites de diagnóstico, registrador de diagnóstico personalizado e usá-los por meio da configuração de telemetria do cliente:

Definindo limites de diagnóstico personalizados

//  Create diagnostics threshold
CosmosDiagnosticsThresholds cosmosDiagnosticsThresholds = new CosmosDiagnosticsThresholds();
//  These thresholds are for demo purposes
//  NOTE: Do not use the same thresholds for production
cosmosDiagnosticsThresholds.setPayloadSizeThreshold(100_00);
cosmosDiagnosticsThresholds.setPointOperationLatencyThreshold(Duration.ofSeconds(1));
cosmosDiagnosticsThresholds.setNonPointOperationLatencyThreshold(Duration.ofSeconds(5));
cosmosDiagnosticsThresholds.setRequestChargeThreshold(100f);

Definindo o manipulador de diagnóstico personalizado

//  By default, DEFAULT_LOGGING_HANDLER can be used
CosmosDiagnosticsHandler cosmosDiagnosticsHandler = CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER;

//  App developers can also define their own diagnostics handler
cosmosDiagnosticsHandler = new CosmosDiagnosticsHandler() {
    @Override
    public void handleDiagnostics(CosmosDiagnosticsContext diagnosticsContext, Context traceContext) {
        logger.info("This is custom diagnostics handler: {}", diagnosticsContext.toJson());
    }
};

Definindo CosmosClientTelemetryConfig

//  Create Client Telemetry Config
CosmosClientTelemetryConfig cosmosClientTelemetryConfig =
    new CosmosClientTelemetryConfig();
cosmosClientTelemetryConfig.diagnosticsHandler(cosmosDiagnosticsHandler);
cosmosClientTelemetryConfig.diagnosticsThresholds(cosmosDiagnosticsThresholds);

//  Create sync client
CosmosClient client = new CosmosClientBuilder()
    .endpoint(AccountSettings.HOST)
    .key(AccountSettings.MASTER_KEY)
    .clientTelemetryConfig(cosmosClientTelemetryConfig)
    .buildClient();

Repetir design

Consulte nosso guia para projetar aplicativos resilientes com SDKs do Azure Cosmos DB para obter orientação sobre como projetar aplicativos resilientes e saiba quais são as semânticas de repetição do SDK.

Problemas comuns e soluções

Verifique as métricas do portal

Verificar as métricas do portal ajudará a determinar se é um problema do lado do cliente ou se há um problema com o serviço. Por exemplo, se as métricas contiverem uma alta taxa de solicitações limitadas por taxa (código de status HTTP 429), o que significa que a solicitação está sendo limitada, verifique a seção Taxa de solicitação muito grande .

Problemas de rede, falha de tempo limite de leitura Netty, baixa taxa de transferência, alta latência

Sugestões gerais

Para o melhor desempenho:

  • Verifique se o aplicativo está sendo executado na mesma região que sua conta do Azure Cosmos DB.
  • Verifique o uso da CPU no host onde o aplicativo está sendo executado. Se o uso da CPU for de 50% ou mais, execute seu aplicativo em um host com uma configuração superior. Ou você pode distribuir a carga em mais máquinas.

Limitação de conexão

A limitação de conexão pode acontecer devido a um limite de conexão em uma máquina host ou ao esgotamento da porta do Azure SNAT (PAT).

Limite de conexão em uma máquina host

Alguns sistemas Linux, como o Red Hat, têm um limite superior no número total de arquivos abertos. Soquetes no Linux são implementados como arquivos, então esse número limita o número total de conexões também. Execute o seguinte comando.

ulimit -a

O número máximo de arquivos abertos permitidos, que são identificados como "nofile", precisa ser pelo menos o dobro do tamanho do pool de conexões. Para obter mais informações, consulte as dicas de desempenho do SDK Java do Azure Cosmos DB v4.

Exaustão da porta do Azure SNAT (PAT)

Se a sua aplicação for implementada em Máquinas Virtuais do Azure sem um endereço IP público, por predefinição , as portas SNAT do Azure estabelecem ligações a qualquer ponto de extremidade fora da sua VM. O número de conexões permitidas da VM para o ponto de extremidade do Azure Cosmos DB é limitado pela configuração do Azure SNAT.

As portas SNAT do Azure são usadas somente quando sua VM tem um endereço IP privado e um processo da VM tenta se conectar a um endereço IP público. Há duas soluções alternativas para evitar a limitação do Azure SNAT:

  • Adicione seu ponto de extremidade de serviço do Azure Cosmos DB à sub-rede de sua rede virtual de Máquinas Virtuais do Azure. Para obter mais informações, consulte Pontos de extremidade de serviço da Rede Virtual do Azure.

    Quando o ponto de extremidade de serviço está habilitado, as solicitações não são mais enviadas de um IP público para o Azure Cosmos DB. Em vez disso, a rede virtual e a identidade da sub-rede são enviadas. Essa alteração pode resultar em quedas de firewall se apenas IPs públicos forem permitidos. Se você usar um firewall, ao habilitar o ponto de extremidade de serviço, adicione uma sub-rede ao firewall usando ACLs de Rede Virtual.

  • Atribua um IP público à sua VM do Azure.

Não é possível acessar o Serviço - firewall

ConnectTimeoutException indica que o SDK não pode acessar o serviço. Você pode obter uma falha semelhante à seguinte ao usar o modo direto:

GoneException{error=null, resourceAddress='https://cdb-ms-prod-westus-fd4.documents.azure.com:14940/apps/e41242a5-2d71-5acb-2e00-5e5f744b12de/services/d8aa21a5-340b-21d4-b1a2-4a5333e7ed8a/partitions/ed028254-b613-4c2a-bf3c-14bd5eb64500/replicas/131298754052060051p//', statusCode=410, message=Message: The requested resource is no longer available at the server., getCauseInfo=[class: class io.netty.channel.ConnectTimeoutException, message: connection timed out: cdb-ms-prod-westus-fd4.documents.azure.com/101.13.12.5:14940]

Se você tiver um firewall em execução na máquina do aplicativo, abra o intervalo de portas de 10.000 a 20.000, que são usados pelo modo direto. Siga também o Limite de conexão em uma máquina host.

UnknownHostException

UnknownHostException significa que a estrutura Java não pode resolver a entrada DNS para o ponto de extremidade do Azure Cosmos DB na máquina afetada. Você deve verificar se a máquina pode resolver a entrada DNS ou, se você tiver algum software de resolução DNS personalizado (como VPN ou Proxy, ou uma solução personalizada), certifique-se de que ele contém a configuração correta para o ponto de extremidade DNS que o erro está alegando que não pode ser resolvido. Se o erro for constante, você poderá verificar a resolução DNS da máquina por meio de um curl comando para o ponto de extremidade descrito no erro.

Proxy HTTP

Se você usar um proxy HTTP, verifique se ele pode suportar o número de conexões configuradas no SDK ConnectionPolicy. Caso contrário, você enfrentará problemas de conexão.

Padrão de codificação inválido: bloqueando thread Netty IO

O SDK usa a biblioteca Netty IO para se comunicar com o Azure Cosmos DB. O SDK tem uma API assíncrona e usa APIs de E/S sem bloqueio do Netty. O trabalho de E/S do SDK é executado em threads do IO Netty. O número de threads do IO Netty é configurado para ser o mesmo que o número de núcleos de CPU da máquina do aplicativo.

Os threads Netty IO destinam-se a ser usados apenas para trabalho Netty IO sem bloqueio. O SDK retorna o resultado da invocação da API em um dos threads de E/S do Netty para o código do aplicativo. Se o aplicativo executar uma operação de longa duração depois de receber resultados no thread Netty, o SDK pode não ter threads de E/S suficientes para executar seu trabalho de E/S interno. Essa codificação de aplicativo pode resultar em baixa taxa de transferência, alta latência e io.netty.handler.timeout.ReadTimeoutException falhas. A solução alternativa é alternar o thread quando você sabe que a operação leva tempo.

Por exemplo, dê uma olhada no trecho de código a seguir, que adiciona itens a um contêiner (veja aqui orientações sobre como configurar o banco de dados e o contêiner.) Você pode executar um trabalho de longa duração que leva mais do que alguns milissegundos no thread Netty. Se assim for, você eventualmente pode entrar em um estado em que nenhum thread Netty IO está presente para processar o trabalho de E/S. Como resultado, você obtém uma falha ReadTimeoutException.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona


//Bad code with read timeout exception

int requestTimeoutInSeconds = 10;

/* ... */

AtomicInteger failureCount = new AtomicInteger();
// Max number of concurrent item inserts is # CPU cores + 1
Flux<Family> familyPub =
        Flux.just(Families.getAndersenFamilyItem(), Families.getAndersenFamilyItem(), Families.getJohnsonFamilyItem());
familyPub.flatMap(family -> {
    return container.createItem(family);
}).flatMap(r -> {
    try {
        // Time-consuming work is, for example,
        // writing to a file, computationally heavy work, or just sleep.
        // Basically, it's anything that takes more than a few milliseconds.
        // Doing such operations on the IO Netty thread
        // without a proper scheduler will cause problems.
        // The subscriber will get a ReadTimeoutException failure.
        TimeUnit.SECONDS.sleep(2 * requestTimeoutInSeconds);
    } catch (Exception e) {
    }
    return Mono.empty();
}).doOnError(Exception.class, exception -> {
    failureCount.incrementAndGet();
}).blockLast();
assert(failureCount.get() > 0);

A solução alternativa é alterar o thread no qual você executa o trabalho que leva tempo. Defina uma instância singleton do agendador para seu aplicativo.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(ex);

Você pode precisar fazer um trabalho que leva tempo, por exemplo, trabalho computacionalmente pesado ou bloqueio de E/S. Nesse caso, alterne o thread para um trabalhador fornecido pelo seu customScheduler usando a .publishOn(customScheduler) API.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

container.createItem(family)
        .publishOn(customScheduler) // Switches the thread.
        .subscribe(
                // ...
        );

publishOn(customScheduler)Usando o , você libera o thread Netty IO e alterna para seu próprio thread personalizado fornecido pelo agendador personalizado. Esta modificação resolve o problema. Você não terá mais um io.netty.handler.timeout.ReadTimeoutException fracasso.

A taxa de pedidos é demasiado grande

Esta falha é uma falha do lado do servidor. Ele indica que você consumiu sua taxa de transferência provisionada. Tente novamente mais tarde. Se você tiver essa falha com frequência, considere um aumento na taxa de transferência da coleta.

  • Implementar backoff em intervalos getRetryAfterInMilliseconds

    Durante o teste de desempenho, você deve aumentar a carga até que uma pequena taxa de solicitações seja limitada. Se limitado, o aplicativo cliente deve recuar para o intervalo de repetição especificado pelo servidor. Respeitar o backoff garante que você passe uma quantidade mínima de tempo esperando entre as tentativas.

Tratamento de erros a partir da cadeia reativa do Java SDK

O tratamento de erros do SDK Java do Azure Cosmos DB é importante quando se trata da lógica do aplicativo do cliente. Existem diferentes mecanismos de tratamento de erros fornecidos pelo quadro do núcleo do reator que podem ser utilizados em diferentes cenários. Recomendamos que os clientes entendam esses operadores de tratamento de erros em detalhes e usem os que melhor se ajustam aos seus cenários de lógica de repetição.

Importante

Não recomendamos o uso do onErrorContinue() operador, pois ele não é suportado em todos os cenários. Note que onErrorContinue() é um operador especializado que pode tornar o comportamento da sua cadeia reativa pouco claro. Ele opera em operadores upstream, não downstream, requer suporte específico do operador para funcionar, e o escopo pode facilmente se propagar upstream em código de biblioteca que não o previu (resultando em comportamento não intencional). Consulte a documentação do onErrorContinue() para obter mais detalhes sobre este operador especial.

Falha na conexão com o emulador do Azure Cosmos DB

O certificado HTTPS do Emulador do Azure Cosmos DB é autoassinado. Para que o SDK funcione com o emulador, importe o certificado do emulador para um Java TrustStore. Para obter mais informações, consulte Exportar certificados do emulador do Azure Cosmos DB.

Problemas de conflito de dependência

O SDK Java do Azure Cosmos DB atrai muitas dependências; De um modo geral, se a árvore de dependência do projeto incluir uma versão mais antiga de um artefato do qual o SDK Java do Azure Cosmos DB depende, isso poderá resultar em erros inesperados gerados quando você executar seu aplicativo. Se você estiver depurando por que seu aplicativo inesperadamente lança uma exceção, é uma boa ideia verificar novamente se sua árvore de dependência não está puxando acidentalmente uma versão mais antiga de uma ou mais dependências do SDK Java do Azure Cosmos DB.

A solução alternativa para esse problema é identificar quais das dependências do projeto trazem a versão antiga e excluir a dependência transitiva dessa versão mais antiga e permitir que o SDK Java do Azure Cosmos DB traga a versão mais recente.

Para identificar quais das dependências do seu projeto trazem uma versão mais antiga de algo do qual o SDK Java do Azure Cosmos DB depende, execute o seguinte comando no arquivo de pom.xml do projeto:

mvn dependency:tree

Para obter mais informações, consulte o guia da árvore de dependência maven.

Depois de saber qual dependência do seu projeto depende de uma versão mais antiga, você pode modificar a dependência dessa lib em seu arquivo pom e excluir a dependência transitiva, seguindo o exemplo abaixo (que assume que o núcleo do reator é a dependência desatualizada):

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-reactor}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-reactor}</artifactId>
  <version>${version-of-lib-which-brings-in-reactor}</version>
  <exclusions>
    <exclusion>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-core</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Para obter mais informações, consulte o guia de dependência transitiva de exclusão.

Habilitar o log do SDK do cliente

O SDK Java v4 do Azure Cosmos DB usa SLF4j como a fachada de log que dá suporte ao logon em estruturas de log populares, como log4j e logback.

Por exemplo, se você quiser usar log4j como a estrutura de log, adicione as seguintes libs em seu classpath Java.

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>${log4j.version}</version>
</dependency>

Adicione também uma configuração log4j.

# this is a sample log4j configuration

# Set root logger level to INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.azure.cosmos=INFO
#log4j.category.io.netty=OFF
#log4j.category.io.projectreactor=OFF
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n

Para obter mais informações, consulte o manual de registro em log sfl4j.

Estatísticas da rede do SO

Execute o comando netstat para ter uma noção de quantas conexões estão em estados como ESTABLISHED e CLOSE_WAIT.

No Linux, você pode executar o seguinte comando.

netstat -nap

No Windows, você pode executar o mesmo comando com diferentes sinalizadores de argumento:

netstat -abn

Filtre o resultado apenas para conexões com o ponto de extremidade do Azure Cosmos DB.

O número de conexões com o ponto de extremidade do Azure Cosmos DB no estado não pode ser maior do que o ESTABLISHED tamanho do pool de conexões configurado.

Muitas conexões com o ponto de extremidade do Azure Cosmos DB podem estar no CLOSE_WAIT estado. Pode haver mais de 1.000. Um número alto indica que as conexões são estabelecidas e derrubadas rapidamente. Esta situação pode causar problemas. Para obter mais informações, consulte a seção Problemas comuns e soluções alternativas .

Problemas comuns de consulta

As métricas de consulta ajudarão a determinar onde a consulta está gastando a maior parte do tempo. A partir das métricas de consulta, você pode ver quanto está sendo gasto no back-end versus o cliente. Saiba mais no guia de desempenho da consulta.

Próximos passos

  • Saiba mais sobre as diretrizes de desempenho para o Java SDK v4
  • Saiba mais sobre as práticas recomendadas para o Java SDK v4