Memecahkan masalah saat Anda menggunakan Azure Cosmos DB Java SDK v4 dengan API untuk akun NoSQL

BERLAKU UNTUK: NoSQL

Penting

Artikel ini hanya membahas pemecahan masalah untuk Azure Cosmos DB Java SDK v4. Silakan lihat catatan Rilis, repositori Maven, dan tips performa Azure Cosmos DB Java SDK v4 untuk informasi selengkapnya. Jika saat ini Anda menggunakan versi yang lebih lama dari v4, lihat panduan Migrasi ke Azure Cosmos DB Java SDK v4 untuk bantuan peningkatan ke v4.

Artikel ini membahas masalah umum, solusi, langkah diagnostik, dan alat saat Anda menggunakan Azure Cosmos DB Java SDK v4 dengan Azure Cosmos DB untuk akun NoSQL. Azure Cosmos DB Java SDK v4 menyediakan representasi logis sisi klien untuk mengakses Azure Cosmos DB for NoSQL. Artikel ini menjelaskan alat dan pendekatan untuk membantu Anda jika mengalami masalah.

Mulai dengan daftar ini:

  • Lihatlah bagian Masalah umum dan solusi dalam artikel ini.
  • Lihatlah Java SDK di repo pusat Azure Cosmos DB, yang tersedia open source di GitHub. Ini memiliki bagian masalah yang dipantau secara aktif. Periksa untuk melihat apakah ada masalah serupa dengan solusi yang sudah diajukan. Salah satu tips bermanfaat adalah memfilter masalah dengan tag cosmos:v4-item.
  • Tinjau tips performa untuk Azure Cosmos DB Java SDK v4, dan ikuti praktik yang disarankan.
  • Baca artikel lainnya, jika Anda tidak menemukan solusi. Kemudian mengajukan masalah GitHub. Jika ada opsi untuk menambahkan tag ke masalah GitHub Anda, tambahkan label cosmos:v4-item.

Menangkap diagnostik

Respons database, kontainer, item, dan kueri di SDK Java V4 memiliki properti Diagnostik. Properti ini mencatat semua informasi yang terkait dengan permintaan tunggal termasuk jika ada pencobaan ulang atau kegagalan sementara.

Diagnostik ditampilkan sebagai string. String berubah dengan setiap versi karena ditingkatkan untuk memecahkan masalah skenario yang berbeda dengan lebih baik. Dengan setiap versi SDK, string akan memiliki perubahan melanggar format. Jangan mengurai string untuk menghindari melanggar perubahan.

Sampel kode berikut menunjukkan cara membaca log diagnostik menggunakan SDK Java V4:

Penting

Sebaiknya validasi versi minimum yang direkomendasikan dari SDK Java V4 dan pastikan Anda menggunakan versi ini atau yang lebih tinggi. Anda dapat memeriksa versi yang direkomendasikan di sini.

Operasi Database.

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

Operasi Kontainer

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

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

Operasi Kueri

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

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

Logika coba lagi

Lihat panduan kami untuk merancang aplikasi tangguh dengan Azure Cosmos DB SDK untuk panduan tentang cara merancang aplikasi tangguh dan mempelajari semantik coba lagi dari SDK.

Masalah umum dan solusi

Masalah jaringan, kegagalan waktu habis baca Netty, throughput rendah, latensi tinggi

Saran umum

Untuk performa terbaik:

  • Pastikan aplikasi berjalan di wilayah yang sama dengan akun Azure Cosmos DB Anda.
  • Periksa penggunaan CPU di host tempat aplikasi berjalan. Jika penggunaan CPU 50 persen atau lebih, jalankan aplikasi Anda di host dengan konfigurasi yang lebih tinggi. Atau Anda dapat mendistribusikan beban pada lebih banyak komputer.

Pembatasan koneksi

Pembatasan koneksi dapat terjadi karena batas koneksi pada komputer host atau kelelahan port Azure SNAT (PAT).

Batas koneksi pada komputer host

Beberapa sistem Linux, seperti `Red Hat, memiliki batas atas jumlah total file terbuka. Soket di Linux diimplementasikan sebagai file, sehingga angka ini membatasi jumlah total koneksi juga. Jalankan perintah berikut.

ulimit -a

Jumlah file terbuka maksimum yang diizinkan, yang diidentifikasi sebagai "nofile," harus setidaknya dua kali lipat ukuran kumpulan koneksi Anda. Untuk informasi selengkapnya, lihat tips performa Azure Cosmos DB Java SDK v4.

Kelelahan port Azure SNAT (PAT)

Jika aplikasi Anda diterapkan di Azure Virtual Machines tanpa alamat IP publik, secara default port Azure SNAT membangun koneksi ke titik akhir di luar VM Anda. Jumlah koneksi yang diizinkan dari VM ke titik akhir Azure Cosmos DB dibatasi oleh konfigurasi Azure SNAT.

Port Azure SNAT hanya digunakan saat VM Anda memiliki alamat IP pribadi dan proses dari VM mencoba menyambungkan ke alamat IP publik. Ada dua solusi untuk menghindari batasan Azure SNAT:

  • Menambahkan titik akhir layanan Azure Cosmos DB Anda ke subnet jaringan virtual Azure Virtual Machines Anda. Untuk informasi selengkapnya, lihat Titik akhir layanan Microsoft Azure Virtual Network.

    Ketika titik akhir layanan diaktifkan, permintaan tidak lagi dikirimkan dari IP publik ke Azure Cosmos DB. Sebagai gantinya, jaringan virtual dan identitas subnet dikirim. Perubahan ini dapat mengakibatkan firewall turun jika hanya IP publik yang diizinkan. Jika Anda menggunakan firewall, saat Anda mengaktifkan titik akhir layanan, tambahkan subnet ke firewall menggunakan Virtual Network ACL.

  • Tetapkan IP publik ke Azure VM Anda.

Tidak dapat menjangkau Layanan - firewall

ConnectTimeoutException menunjukkan bahwa SDK tidak dapat menjangkau layanan. Anda mungkin mengalami kegagalan yang mirip dengan yang berikut ini saat menggunakan mode langsung:

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]

Jika Anda memiliki firewall yang berjalan di komputer aplikasi Anda, buka rentang port 10.000 hingga 20.000 yang digunakan oleh mode langsung. Ikuti juga Batas koneksi pada komputer host.

UnknownHostException

UnknownHostException berarti bahwa kerangka kerja Java tidak dapat menyelesaikan entri DNS untuk titik akhir Azure Cosmos DB di komputer yang terpengaruh. Anda harus memverifikasi bahwa komputer dapat mengatasi entri DNS atau jika Anda memiliki perangkat lunak resolusi DNS kustom (seperti VPN atau Proksi, atau solusi kustom), pastikan solusi Anda berisi konfigurasi yang tepat untuk titik akhir DNS yang menampilkan kesalahan tidak dapat diatasi. Jika kesalahan tetap ada, Anda dapat memverifikasi resolusi DNS komputer melalui perintah curl ke titik akhir yang dijelaskan dalam kesalahan.

Proksi HTTP

Jika Anda menggunakan proksi HTTP, pastikan dapat mendukung jumlah koneksi yang dikonfigurasi di SDK ConnectionPolicy. Jika tidak, Anda akan menghadapi masalah koneksi.

Pola pengkodean tidak valid: Memblokir utas IO Netty

SDK menggunakan pustaka IO Netty untuk berkomunikasi dengan Azure Cosmos DB. SDK memiliki Async API dan menggunakan IO API non-blokir Netty. Fungsi IO SDK dilakukan pada utas IO Netty. Jumlah utas IO Netty dikonfigurasi agar sama dengan jumlah core CPU komputer aplikasi.

Utas IO Netty dimaksudkan untuk digunakan hanya untuk pekerjaan IO non-blokir Netty. SDK mengembalikan hasil pemanggilan API pada salah satu utas IO Netty ke kode aplikasi. Jika aplikasi melakukan operasi jangka panjang setelah menerima hasil pada utas Netty, SDK mungkin tidak memiliki cukup thread IO untuk melakukan pekerjaan IO internalnya. Pengodean aplikasi tersebut dapat mengakibatkan throughput rendah, latensi tinggi, dan kegagalan io.netty.handler.timeout.ReadTimeoutException. Solusinya adalah mengganti utas ketika Anda tahu operasi membutuhkan waktu.

Misalnya, lihat cuplikan kode berikut yang menambahkan item ke kontainer (lihat di sini untuk panduan tentang pengaturan database dan kontainer.) Anda mungkin melakukan pekerjaan jangka panjang yang membutuhkan lebih dari beberapa milidetik pada utas Netty. Jika demikian, Anda akhirnya bisa masuk ke status di mana tidak ada thread IO Netty yang hadir untuk memproses pekerjaan IO. Akibatnya, Anda mendapatkan kegagalan ReadTimeoutException.

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


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

Solusinya adalah mengubah utas tempat Anda melakukan pekerjaan yang membutuhkan waktu. Tentukan instans singleton penjadwal untuk aplikasi Anda.

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

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

Anda mungkin perlu melakukan pekerjaan yang membutuhkan waktu, misalnya, pekerjaan berat secara komputasi atau memblokir IO. Dalam hal ini, alihkan untuk ke pekerja yang disediakan oleh customScheduler Anda menggunakan .publishOn(customScheduler) API.

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

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

Dengan menggunakan publishOn(customScheduler), Anda merilis thread IO Netty dan beralih ke utas kustom Anda sendiri yang disediakan oleh penjadwal kustom. Modifikasi ini memecahkan masalah. Anda tidak akan mendapatkan kegagalan io.netty.handler.timeout.ReadTimeoutException lagi.

Rentang permintaan terlalu besar

Kegagalan ini adalah kegagalan sisi server. Ini menunjukkan bahwa Anda mengonsumsi throughput yang disediakan. Coba lagi nanti. Jika Anda sering mengalami kegagalan ini, pertimbangkan melakukan peningkatan throughput koleksi.

  • Menerapkan backoff di interval getRetryAfterInMilliseconds

    Selama pengujian performa, Anda harus meningkatkan beban sampai tingkat permintaan yang kecil dibatasi. Jika dibatasi, aplikasi klien harus melakukan backoff untuk interval coba lagi yang ditentukan server. Memperhatikan backoff akan memastikan bahwa Anda menghabiskan sedikit waktu menunggu di antara upaya ulang.

Penanganan kesalahan dari Java SDK Reactive Chain

Penanganan kesalahan dari Azure Cosmos DB Java SDK penting dalam hal logika aplikasi klien. Ada mekanisme penanganan kesalahan yang berbeda yang disediakan oleh kerangka kerja inti reaktor yang dapat digunakan dalam skenario yang berbeda. Sebaiknya pelanggan memahami operator penanganan kesalahan ini secara mendetail dan menggunakan operator yang paling sesuai dengan skenario logika percobaan ulang mereka.

Penting

Kami tidak merekomendasikan penggunaan operator onErrorContinue(), karena tidak didukung di semua skenario. Perhatikan bahwa onErrorContinue() adalah operator spesialis yang dapat membuat perilaku rantai reaktif Anda menjadi tidak jelas. Operator ini beroperasi pada operator hulu, bukan hilir, memerlukan dukungan operator khusus untuk berfungsi, dan ruang lingkup dapat dengan mudah menyebar ke hulu ke kode pustaka yang tidak mengantisipasinya (menghasilkan perilaku yang tidak diinginkan.). Lihat dokumentasi dari onErrorContinue() untuk mengetahui detail selengkapnya tentang operator khusus ini.

Kegagalan menyambungkan ke Azure Cosmos DB Emulator

Sertifikat HTTPS Azure Cosmos DB Emulator ditandatangani sendiri. Agar SDK dapat digunakan dengan emulator, impor sertifikat emulator ke Java TrustStore. Untuk informasi selengkapnya, lihat Mengekspor sertifikat Azure Cosmos DB Emulator.

Masalah Konflik Dependensi

Azure Cosmos DB Java SDK menarik sejumlah dependensi; secara umum, jika pohon dependensi proyek Anda menyertakan versi artefak yang lebih lama yang bergantung pada Azure Cosmos DB Java SDK, ini dapat mengakibatkan kesalahan tak terduga yang dihasilkan saat Anda menjalankan aplikasi Anda. Jika Anda men-debug mengapa aplikasi Anda secara tak terduga melemparkan pengecualian, ada baiknya untuk memeriksa ulang bahwa pohon dependensi Anda tidak secara tidak sengaja menarik versi lama dari satu atau lebih dependensi Azure Cosmos DB Java SDK.

Solusi untuk masalah tersebut adalah mengidentifikasi dependensi proyek mana yang membawa versi lama dan mengecualikan dependensi transitif pada versi lama tersebut, dan memungkinkan Azure Cosmos DB Java SDK untuk membawa versi yang lebih baru.

Untuk mengidentifikasi dependensi proyek mana yang membawa versi lama dari sesuatu yang menjadi bergantungnya Azure Cosmos DB Java SDK, jalankan perintah berikut terhadap file pom.xml proyek Anda:

mvn dependency:tree

Untuk informasi selengkapnya, lihat panduan pohon ketergantungan maven.

Setelah mengetahui dependensi proyek mana yang bergantung pada versi yang lebih lama, Anda dapat memodifikasi dependensi pada lib tersebut dalam file pom Anda dan mengecualikan dependensi transitif, dengan mengikuti contoh di bawah ini (yang mengasumsikan bahwa reactor-core adalah dependensi yang sudah usang):

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

Untuk informasi selengkapnya, lihat panduan dependensi transitif yang dikecualikan.

Mengaktifkan pengelogan SDK klien

Azure Cosmos DB Java SDK v4 menggunakan SLF4j sebagai fasad logging yang mendukung pencatatan ke kerangka kerja pencatatan populer seperti log4j dan logback.

Misalnya, jika Anda ingin menggunakan log4j sebagai kerangka kerja pengelogan, tambahkan libs berikut di classpath Java Anda.

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

Tambahkan juga konfigurasi 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

Untuk informasi selengkapnya, lihat manual pengelogan sfl4j.

Statistik jaringan OS

Jalankan perintah netstat untuk merasakan berapa banyak koneksi dalam status seperti ESTABLISHED dan CLOSE_WAIT.

Di Linux, Anda dapat menjalankan perintah berikut.

netstat -nap

Di Windows, Anda bisa menjalankan perintah yang sama dengan bendera argumen yang berbeda:

netstat -abn

Filter hasilnya hanya ke koneksi ke titik akhir Azure Cosmos DB.

Jumlah koneksi ke titik akhir Azure Cosmos DB dalam status ESTABLISHED tidak boleh lebih besar dari ukuran kumpulan koneksi yang telah dikonfigurasi.

Banyak koneksi ke titik akhir Azure Cosmos DB mungkin berada dalam status CLOSE_WAIT. Mungkin ada lebih dari 1.000. Angka yang tinggi menunjukkan bahwa koneksi dibuat dan dihentikan dengan cepat. Situasi ini berpotensi menimbulkan masalah. Untuk informasi selengkapnya, lihat bagian Masalah umum dan solusi.