Risolvere i problemi relativi all'uso di Azure Cosmos DB Java SDK v4 con gli account API per NoSQL

SI APPLICA A: NoSQL

Importante

Questo articolo illustra la risoluzione dei problemi solo di Azure Cosmos DB Java SDK v4. Per altre informazioni, vedere le Note sulla versione di Azure Cosmos DB Java SDK v4, il repository Maven e suggerimenti sulle prestazioni. Se si usa attualmente una versione precedente alla versione 4, vedere la guida Eseguire la migrazione ad Azure Cosmos DB Java SDK v4 per informazioni sull'aggiornamento alla versione 4.

Questo articolo illustra i problemi comuni, le soluzioni alternative, i passaggi di diagnostica e gli strumenti quando si usa Azure Cosmos DB Java SDK v4 con gli account Azure Cosmos DB per NoSQL. Azure Cosmos DB Java SDK v4 fornisce una rappresentazione logica lato client per accedere ad Azure Cosmos DB per NoSQL. Questo articolo descrive strumenti e approcci utili ad affrontare eventuali problemi.

Iniziamo con un elenco:

  • Diamo un'occhiata alla sezione Problemi e soluzioni alternative comuni in questo articolo.
  • Vedere Java SDK nel repository centrale di Azure Cosmos DB, disponibile open source in GitHub. Include una sezione per i problemi monitorata attivamente. Verificare se è già stato pubblicato un problema simile con una soluzione alternativa. Un suggerimento utile consiste nel filtrare i problemi in base al *cosmos:v4-item* tag.
  • Esaminare i suggerimenti per le prestazioni relativi ad Azure Cosmos DB Java SDK v4 e seguire le procedure consigliate.
  • Leggere la parte restante di questo articolo, se non si trova una soluzione. Registrare poi un problema in GitHub. Se è disponibile un'opzione per aggiungere tag al problema di GitHub, aggiungere un *cosmos:v4-item* tag.

Acquisire la diagnostica

Database, contenitore, elemento e risposte di query nell'SDK Java V4 hanno una proprietà Diagnostics. Questa proprietà registra tutte le informazioni correlate alla singola richiesta, inclusi i tentativi effettuati o eventuali errori temporanei.

La diagnostica viene restituita come stringa. La stringa cambia con ogni versione, perché viene migliorata per la risoluzione più efficace dei problemi relativi a scenari diversi. Con ogni versione dell'SDK, la stringa potrebbe interromperne il formato. Non analizzare la stringa per evitare modifiche che causano interruzioni.

L'esempio di codice seguente illustra come leggere i log di diagnostica usando l’SDK Java V4:

Importante

È consigliabile convalidare la versione minima consigliata di Java V4 SDK e assicurarsi di usare questa versione o versione successiva. È possibile controllare la versione consigliata qui.

Operazioni sui database

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

Operazioni sui contenitori

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

Operazioni sugli elementi

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

Operazioni di query

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

Eccezioni di 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);
}

Registrazione della diagnostica

Java V4 SDK versione 4.43.0 e successive supportano la registrazione automatica di Diagnostica Cosmos per tutte le richieste o gli errori se soddisfano determinati criteri. Gli sviluppatori di applicazioni possono definire soglie per latenza (per il punto (creazione, lettura, sostituzione, upsert, patch) o operazioni non di punto (query, feed di modifiche, bulk e batch)), addebito della richiesta e dimensioni del payload. Se le richieste superano queste soglie definite, la diagnostica cosmos per tali richieste verrà generata automaticamente.

Per impostazione predefinita, Java v4 SDK registra automaticamente la diagnostica in un formato specifico. Tuttavia, questo può essere modificato implementando l'interfaccia CosmosDiagnosticsHandler e fornendo un gestore di diagnostica personalizzato.

Questi CosmosDiagnosticsThresholds e CosmosDiagnosticsHandler possono quindi essere usati nell'oggetto CosmosClientTelemetryConfig , che deve essere passato durante CosmosClientBuilder la creazione della sincronizzazione o del client asincrono.

NOTA: queste soglie di diagnostica vengono applicate in diversi tipi di diagnostica, tra cui la registrazione, la traccia e i dati di telemetria del client.

Gli esempi di codice seguenti illustrano come definire soglie di diagnostica, logger di diagnostica personalizzato e usarli tramite la configurazione dei dati di telemetria del client:

Definizione di soglie di diagnostica personalizzate

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

Definizione del gestore di diagnostica personalizzato

//  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());
    }
};

Definizione di 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();

Riprovare a progettare

Vedere la guida per la progettazione di applicazioni resilienti con Azure Cosmos DB SDK per indicazioni su come progettare applicazioni resilienti e conoscere la semantica di retry dell'SDK.

Problemi comuni e soluzioni alternative

Controllare le metriche del portale

Controllare le metriche del portale per determinare se si tratta di un problema sul lato client o se si è verificato un problema con il servizio. Ad esempio, se le metriche contengono una frequenza elevata di richieste a velocità limitata (codice di stato HTTP 429), significa che la richiesta viene limitata. Controllare la sezione Frequenza richieste troppo grande.

Problemi di rete, errore di timeout nella lettura di Netty, ridotta velocità effettiva, latenza elevata

Suggerimenti generici

Per prestazioni ottimali:

  • Assicurarsi che l'app sia in esecuzione nella stessa area dell'account Azure Cosmos DB.
  • Controllare l'utilizzo della CPU nell'host in cui viene eseguita l'app. Se l'utilizzo della CPU è 50% o oltre, eseguire l'app in un host con una configurazione superiore oppure distribuire il carico su più computer.

Limitazione della connessione

La limitazione delle connessioni può verificarsi a causa di un limite di connessioni nel computer host o di un esaurimento delle porte SNAT (PAT) di Azure.

Limite di connessioni nel computer host

Alcuni sistemi Linux, come Red Hat, prevedono un limite massimo per il numero totale di file aperti. I socket in Linux vengono implementati come file, per cui questo numero limita anche il numero totale di connessioni. Esegui il comando seguente:

ulimit -a

Il numero massimo consentito di file aperti, identificati come "nofile", deve essere almeno il doppio della dimensione del pool di connessioni. Per altre informazioni, vedere i suggerimenti per le prestazioni relativi ad Azure Cosmos DB Java SDK v4.

Esaurimento delle porte SNAT (PAT) di Azure

Se l'app viene distribuita in macchine virtuali di Azure senza un indirizzo IP pubblico, per impostazione predefinita le porte SNAT di Azure stabiliscono le connessioni a qualsiasi endpoint all'esterno della macchina virtuale. Il numero di connessioni consentite dalla macchina virtuale all'endpoint di Azure Cosmos DB è limitato dalla configurazione SNAT di Azure.

Le porte SNAT di Azure vengono usate solo quando la macchina virtuale ha un indirizzo IP privato e un processo dalla macchina virtuale prova a connettersi a un indirizzo IP pubblico. Esistono due soluzioni alternative per evitare la limitazione per le porte SNAT di Azure:

  • Aggiungere l'endpoint del servizio Azure Cosmos DB alla subnet della rete virtuale di macchine virtuali di Azure. Per altre informazioni, vedere Endpoint servizio di rete virtuale di Azure.

    Quando l'endpoint del servizio è abilitato, le richieste non vengono più inviate da un indirizzo IP pubblico ad Azure Cosmos DB. Vengono invece inviate le identità di rete virtuale e subnet. Questa modifica può comportare blocchi del firewall se sono consentiti solo indirizzi IP pubblici. Se si usa un firewall, quando si abilita l'endpoint del servizio, aggiungere una subnet al firewall tramite ACL di rete virtuale.

  • Assegnare un indirizzo IP pubblico alla macchina virtuale di Azure.

Non è possibile raggiungere il servizio - Firewall

ConnectTimeoutException indica che l'SDK non può raggiungere il servizio. Quando si usa la modalità diretta, è possibile che si verifichi un errore simile al seguente:

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 nel computer dell'app è in esecuzione un firewall, aprire l'intervallo di porte da 10.000 a 20.000, che vengono usate dalla modalità diretta. Seguire anche il Limite di connessione nel computer host.

UnknownHostException

UnknownHostException significa che il framework Java non è in grado di risolvere la voce DNS per l'endpoint Azure Cosmos DB nel computer interessato. È necessario verificare che il computer sia in grado di risolvere la voce DNS o se si dispone di un software di risoluzione DNS personalizzato (ad esempio VPN o proxy o una soluzione personalizzata), assicurarsi che contenga la configurazione giusta per l'endpoint DNS che l'errore afferma non essere risolvibile. Se l'errore è costante, è possibile verificare la risoluzione DNS del computer tramite un comando curl all'endpoint descritto nell'errore.

Proxy HTTP

Se si usa un proxy HTTP, assicurarsi che possa supportare il numero di connessioni configurate in ConnectionPolicy dell'SDK. In caso contrario, verranno riscontrati problemi di connessione.

Modello di codifica non valido: blocco del thread I/O Netty

L'SDK usa la libreria di I/O Netty per comunicare con Azure Cosmos DB. L'SDK include un'API asincrona e usa le API di I/O non bloccanti di Netty. Le operazioni di I/O dell'SDK vengono eseguite su thread di I/O di Netty. Il numero di thread di I/O di Netty viene configurato in modo da corrispondere al numero di core della CPU del computer dell'app.

I thread di I/O di Netty sono concepiti esclusivamente per le operazioni di I/O non bloccante di Netty. L'SDK restituisce al codice dell'app il risultato della chiamata dell'API in uno dei thread di I/O di Netty. Se l'app esegue un'operazione di lunga durata dopo la ricezione dei risultati sul thread di Netty, l'SDK potrebbe non avere un numero di thread di I/O sufficiente a eseguire le operazioni di I/O interne. Questa codifica dell'app potrebbe comportare ridotta velocità effettiva, latenza elevata ed errori io.netty.handler.timeout.ReadTimeoutException. La soluzione alternativa consiste nel cambiare il thread quando si prevede che l'operazione richieda tempo.

Ad esempio, esaminare il frammento di codice seguente, che aggiunge elementi a un contenitore (vedere qui per indicazioni sulla configurazione del database e del contenitore). È possibile eseguire operazioni di lunga durata che richiedono più di pochi millisecondi nel thread Netty. In questo caso, si potrebbe raggiungere uno stato in cui non è presente alcun thread di I/O di Netty per elaborare le operazioni di I/O, con conseguente generazione dell'errore ReadTimeoutException.

API asincrona Java SDK 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);

La soluzione alternativa consiste nel cambiare il thread su cui si eseguono operazioni di lunga durata. Definire un'istanza singleton dell'utilità di pianificazione per l'app.

API asincrona Java SDK 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);

Potrebbe essere necessario eseguire operazioni che richiedono tempo, ad esempio azioni intensive a livello di calcolo oppure I/O di blocco. In questo caso, passare il thread su un ruolo di lavoro fornito da customScheduler tramite l'API .publishOn(customScheduler).

API asincrona Java SDK V4 (Maven com.azure::azure-cosmos)

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

Usando publishOn(customScheduler), il thread di I/O di Netty viene rilasciato e si passa al thread personalizzato specificato dall'utilità di pianificazione personalizzata. Questa modifica risolve il problema. Non verrà più generato un errore io.netty.handler.timeout.ReadTimeoutException.

La frequenza delle richieste è troppo elevata

Questo errore è un errore sul lato server. Indica l'esaurimento della velocità effettiva di cui è stato effettuato il provisioning. Riprovare in un secondo momento. Se questo errore si verifica spesso, provare ad aumentare la velocità effettiva della raccolta.

  • Implementare il backoff in base agli intervalli definiti dal parametro getRetryAfterInMilliseconds

    Durante il test delle prestazioni, è necessario aumentare il carico fino a limitare un numero ridotto di richieste. Se limitata, l'applicazione client deve eseguire il backoff per l'intervallo tra tentativi specificato dal server. Rispettando il backoff si garantiscono tempi di attesa minimi tra i tentativi.

Gestione degli errori di Java SDK Reactive Chain

La gestione degli errori da Azure Cosmos DB Java SDK è importante quando si tratta della logica dell'applicazione del client. Esistono diversi meccanismi di gestione degli errori forniti dal framework reactor-core che possono essere usati in scenari diversi. È consigliabile che i clienti comprendano in dettaglio questi operatori di gestione degli errori e usino quelli che meglio si adattano agli scenari della logica di ripetizione dei tentativi.

Importante

Non è consigliabile usare l'operatore onErrorContinue() perché non è supportato in tutti gli scenari. Considerare che onErrorContinue() è un operatore specifico che può rendere poco chiaro il comportamento della catena reattiva. Opera su operatori upstream, non downstream, richiede un supporto specifico dell'operatore e l'ambito può propagarsi facilmente upstream nel codice della libreria che non lo prevedeva, generando un comportamento imprevisto. Per altre informazioni su questo operatore speciale, vedere la documentazione di onErrorContinue().

Errore di connessione all'emulatore di Azure Cosmos DB

Il certificato HTTPS dell'emulatore di Azure Cosmos DB è autofirmato. Per poter usare l'SDK con l'emulatore, importare il certificato dell'emulatore in un Java TrustStore. Per altre informazioni, vedere Esportare i certificati dell'emulatore di Azure Cosmos DB.

Problemi di conflitto di dipendenze

Azure Cosmos DB Java SDK esegue il pull in molte dipendenze; in generale, se l'albero delle dipendenze del progetto include una versione precedente di un artefatto da cui dipende Java SDK di Azure Cosmos DB, questo potrebbe causare errori imprevisti generati quando si esegue l'applicazione. Se si esegue il debug del motivo per cui l'applicazione genera un'eccezione in modo imprevisto, è consigliabile verificare che l'albero delle dipendenze non eselabori accidentalmente una versione precedente di una o più dipendenze di Java SDK di Azure Cosmos DB.

Per risolvere questo problema, è possibile identificare le dipendenze del progetto che introducono la versione precedente ed escludere la dipendenza transitiva dalla versione precedente, in modo da consentire ad Azure Cosmos DB Java SDK di introdurre la versione più recente.

Per identificare le dipendenze del progetto che introducono una versione precedente di un elemento da cui dipende Azure Cosmos DB Java SDK, eseguire il comando seguente sul file pom.xml del progetto:

mvn dependency:tree

Per altre informazioni, vedere la guida all'albero delle dipendenze di Maven.

Dopo aver identificato la dipendenza del progetto che dipende da una versione precedente, è possibile modificare la dipendenza dalla libreria nel file POM ed escludere la dipendenza transitiva, seguendo l'esempio riportato di seguito (in cui si presuppone che reactor-core sia la dipendenza obsoleta):

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

Per altre informazioni, vedere la guida all'esclusione delle dipendenze transitive.

Abilitare la registrazione dell'SDK del client

Azure Cosmos DB Java SDK v4 usa SLF4j come interfaccia per supportare la registrazione in framework di registrazione diffusi come SLF4j e logback.

Ad esempio, se si vuole usare log4j come framework di registrazione, aggiungere le librerie seguenti nel 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>

Aggiungere anche una configurazione 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

Per altre informazioni, vedere il manuale di registrazione di sfl4j.

Statistiche di rete del sistema operativo

Eseguire il comando netstat per farsi un'idea di quante connessioni sono in stati come ESTABLISHED e CLOSE_WAIT.

In Linux è possibile eseguire il comando seguente.

netstat -nap

In Windows è possibile eseguire lo stesso comando con flag di argomento diversi:

netstat -abn

Filtrare i risultati per visualizzare solo le connessioni all'endpoint di Azure Cosmos DB.

Il numero di connessioni all'endpoint di Azure Cosmos DB in stato ESTABLISHED non deve essere superiore alle dimensioni del pool di connessione configurato.

Molte connessioni all'endpoint di Azure Cosmos DB potrebbero trovarsi nello stato CLOSE_WAIT. Potrebbero essercene più di 1.000. Un numero così alto indica che le connessioni vengono stabilite ed eliminate rapidamente. Questa situazione può causare potenzialmente problemi. Per altre informazioni, vedere la sezione Problemi comuni e soluzioni alternative.

Problemi di query comuni

Le metriche di query consentono di determinare dove la query sta spendendo la maggior parte del tempo. Dalle metriche di query è possibile visualizzare la quantità di spesa per il back-end rispetto al client. Per altre informazioni, vedere la guida alle prestazioni delle query.

Passaggi successivi

  • Informazioni sulle linee guida sulle prestazioni per Java SDK v4
  • Informazioni sulle procedure consigliate per Java SDK v4