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

APLICA-SE A: NoSQL

Importante

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

Este artigo aborda problemas comuns, soluções alternativas, etapas de diagnóstico e ferramentas ao usar o SDK do Java do Azure Cosmos DB v4 com contas do Azure Cosmos DB for NoSQL. O SDK do Java do Azure Cosmos DB v4 fornece representação lógica do lado do cliente para acessar o Azure Cosmos DB for NoSQL. Este artigo descreve as ferramentas e as abordagens para ajudá-lo se você tiver algum problema.

Comece com esta lista:

  • Dê uma olhada na seção Problemas comuns e soluções alternativas neste artigo.
  • Examine o SDK do Java no repositório central do Azure Cosmos DB, disponível no software livre no GitHub. Ele tem um seção de problemas que está sendo ativamente monitorada. Verifique se você encontrar algum problema semelhante com uma solução alternativa já arquivada. Uma dica útil é filtrar os problemas pela tag *cosmos:v4-item*.
  • Consulte as dicas de desempenho do SDK do Java v4 do Azure Cosmos DB e siga as práticas sugeridas.
  • Se você não encontrar uma solução, leia o restante deste artigo. Em seguida, arquive um problema do GitHub. Se houver uma opção para adicionar marcas ao problema do GitHub, adicione uma tag *cosmos:v4-item*.

Capturar o diagnóstico

As respostas de banco de dados, contêiner, item e consulta no SDK do Java V4 têm uma propriedade Diagnostics. Ela registra todas as informações relacionadas à solicitação única, incluindo se houve novas tentativas ou quaisquer falhas temporárias.

Os diagnósticos são retornados como uma cadeia de caracteres. A cadeia de caracteres muda com cada versão à medida que é aprimorada para solucionar melhor os diferentes cenários. Com cada versão do SDK, a cadeia de caracteres pode quebrar o formato dela. Não analise a cadeia de caracteres para evitar alterações interruptivas.

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

Importante

É recomendável validar a versão mínima recomendada do SDK do Java V4 e garantir que você esteja usando essa versão ou uma superior. É possível verificar a versão recomendada aqui.

Operações de Banco de dados

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

Operações de Contêiner

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);

Operadores 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);
}

Registro em log de diagnósticos

As versões do SDK do Java V4 v4.43.0 e posteriores dão suporte ao registro em log automático do Diagnóstico do Cosmos para todas as solicitações ou erros, se elas atenderem a determinados critérios. Os desenvolvedores de aplicativos podem definir limites de latência (para operações pontuais (criação, leitura, substituição, upsert, patch) ou não pontuais (consulta, feed de alterações, em massa e lote)), custo da solicitação e tamanho da carga. Se as solicitações excederem esses limites definidos, o diagnóstico do Cosmos para essas solicitações será emitido automaticamente.

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

Esses CosmosDiagnosticsThresholds e CosmosDiagnosticsHandler podem ser usados no objeto CosmosClientTelemetryConfig, que deve ser passado para CosmosClientBuilder ao criar um cliente síncrono ou assíncrono.

OBSERVAÇÃO: esses limites de diagnóstico são aplicados em diferentes tipos de diagnóstico, incluindo registro em log, rastreamento e telemetria do cliente.

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

Como definir 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);

Como definir um 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());
    }
};

Como definir 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();

Design para repetição

Confira nosso guia para obter diretrizes sobre como criar aplicativos resilientes com SDKs do Azure Cosmos DB e para saber qual é a semântica de repetição do SDK.

Problemas comuns e soluções alternativas

Verificar as métricas do portal

A verificação das métricas do portal ajudará a determinar se esse é um problema do lado do cliente ou se há um problema com o serviço. Por exemplo, se as métricas contêm uma alta taxa de solicitações com taxa limitada (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 alta.

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

Sugestões gerais

Para obter o melhor desempenho:

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

Limitação de conexão

A limitação de conexão pode ocorrer devido a um limite de conexão no computador host ou a Esgotamento da porta SNAT (PAT) do Azure.

Limite de conexão no computador host

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

ulimit -a

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

Esgotamento da porta SNAT (PAT) do Azure

Se o seu aplicativo for desenvolvido nas Máquinas Virtuais do Microsoft Azure sem um endereço IP público, por padrão as portas do Azure SNAT estabelecem conexõ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 Azure SNAT são usadas somente quando sua VM do Azure tiver um endereço IP privado e um processo da VM tenta estabelecer uma conexão com 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 para a sub-rede da sua rede virtual de Máquinas Virtuais do Azure. Para saber mais, consulte pontos de extremidade de serviço de Rede Virtual do Microsoft Azure.

    Quando o ponto de extremidade for 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 de sub-rede são enviadas. Essa alteração poderá resultar em quedas de firewall se apenas IPs públicos forem permitidos. Se você usar um firewall, quando você habilitar o ponto de extremidade de serviço, adicione uma sub-rede para o firewall usando as 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 em seu computador do aplicativo, abra o intervalo de portas 10.000 a 20.000, que são usadas pelo modo direto. Além disso, siga o Limite de conexão em um computador host.

UnknownHostException

UnknownHostException significa que a estrutura Java não consegue resolver a entrada DNS do ponto de extremidade do Azure Cosmos DB no computador afetado. Você deve verificar se o computador pode resolver a entrada DNS ou se há algum software de resolução DNS personalizado (como VPN ou proxy ou uma solução personalizada) e se ele contém a configuração certa do ponto de extremidade DNS que o erro está indicando que não pode ser resolvido. Se o erro for constante, você poderá verificar a resolução DNS do computador com o comando curl para o ponto de extremidade descrito no erro.

Proxy HTTP

Se você usar um proxy HTTP, certifique-se que 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: Bloqueio do thread de E/S do Netty

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

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

Por exemplo, observe o snippet de código a seguir, que adiciona itens a um contêiner (procure aqui por diretrizes sobre como configurar o banco de dados e o contêiner.) Você pode executar o trabalho de longa duração que leva mais de alguns milissegundos no thread de Netty. Nesse caso, você, eventualmente, pode colocar em um estado em que nenhum thread de e/s do Netty está presente para processar o trabalho de e/s. Como resultado, você obterá uma falha de ReadTimeoutException.

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


//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 em que você executa o trabalho demorado. Defina uma instância singleton do agendador para seu aplicativo.

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

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

Você talvez precise fazer o trabalho que leva tempo, por exemplo, recursos computacionais pesados corporativos ou bloqueio de e/s. Nesse caso, alterne o thread para um trabalhador fornecido pelo seu customScheduler usando a .publishOn(customScheduler) API.

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

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

Usando publishOn(customScheduler), você libera o thread de E/S Netty e muda para seu próprio thread personalizado fornecido pelo agendador personalizado. Essa modificação resolve o problema. Você não obterá uma io.netty.handler.timeout.ReadTimeoutException falha mais.

Taxa de solicitação grande demais

Essa falha é uma falha no lado do servidor. Indica que você consumiu sua taxa de transferência provisionada. Tente novamente mais tarde. Se você recebe essa falha com frequência, considere aumentar a taxa de transferência da coleção.

  • Implementar a retirada em intervalos de getRetryAfterInMilliseconds

    Durante o teste de desempenho, você deve aumentar a carga até que uma pequena taxa de solicitações seja restringida. Se restringida, o aplicativo cliente deve retirar a limitação para a nova tentativa do servidor especificado. Respeitar a retirada garante que você perca o mínimo de tempo de espera entre as tentativas.

Tratamento de erro da cadeia reativa do SDK do Java

O tratamento de erros do SDK do Java do Azure Cosmos DB é importante quando se trata da lógica do aplicativo do cliente. Há diferentes mecanismos de tratamento de erro fornecidos pela estrutura de núcleo do reator, que podem ser usados em diferentes cenários. Recomendamos que os clientes compreendam esses operadores de tratamento de erros em detalhes e usem aqueles que melhor se ajustem aos cenários de lógica de repetição.

Importante

Não recomendamos o uso onErrorContinue() do operador, pois não há suporte para ele em todos os cenários. Observe que onErrorContinue() é um operador especialista que pode tornar o comportamento de sua cadeia reativa confuso. Ele opera em upstream, não em operadores downstream, requer suporte de operador específico para funcionar e o escopo pode facilmente se propagar para upstream em código de biblioteca que não tinha previsão para isso (resultando em um comportamento indesejado). Consulte a documentação do onErrorContinue() para obter mais detalhes sobre este operador especial.

Falha ao conectar-se ao Emulador do Azure Cosmos DB

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

Problemas de conflito de dependência

O SDK do Java Azure Cosmos DB recebe várias dependências. Em termos gerais, se a árvore de dependências do projeto incluir uma versão mais antiga de um artefato do qual o SDK do Java do Azure Cosmos DB depende, isso poderá resultar na geração de erros inesperados ao executar o aplicativo. Se você estiver depurando o motivo de seu aplicativo lançar uma exceção inesperadamente, verifique se a árvore de dependências não está trazendo acidentalmente uma versão mais antiga de uma ou mais dependências do SDK do Java do Azure Cosmos DB.

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

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

mvn dependency:tree

Para obter mais informações, consulte o guia de árvore de dependências do Maven.

Depois de saber qual dependência do projeto depende de uma versão antiga, você pode modificar a dependência nesse lib no arquivo pom e excluir a dependência transitiva, seguindo o exemplo abaixo (que pressupõe que reator-core é 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 Excluir dependência transitiva.

Habilitar o log do SDK do cliente

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

Por exemplo, se você quiser usar log4j como a estrutura de registros, adicione estas bibliotecas ao seu classpath de 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 de 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 log sfl4j.

Estatísticas de rede do sistema operacional

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

No Linux, você pode executar o comando a seguir.

netstat -nap

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

netstat -abn

Filtre o resultado somente para conexões ao ponto de extremidade do Azure Cosmos DB.

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

Muitas conexões para o ponto de extremidade do Azure Cosmos DB podem estar no CLOSE_WAIT estado. Pode haver mais de 1.000. Um número tão alto indica que as conexões são estabelecidas e interrompidas rapidamente. Essa situação potencialmente causa 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 em que item a consulta está gastando a maior parte do tempo. Nas métricas de consulta, você pode ver quanto da consulta está sendo gasto no back-end em comparação com o cliente. Saiba mais sobre o guia de desempenho da consulta.

Próximas etapas

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