Rekomendasi pengemudi Amazon QLDB - Amazon Quantum Ledger Database (Amazon QLDB)

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Rekomendasi pengemudi Amazon QLDB

Bagian ini menjelaskan praktik terbaik untuk mengonfigurasi dan menggunakan driver Amazon QLDB untuk bahasa apa pun yang didukung. Contoh kode yang disediakan khusus untuk Java.

Rekomendasi ini berlaku untuk kasus penggunaan yang paling umum, tetapi satu ukuran tidak cocok untuk semua. Gunakan rekomendasi berikut sesuai keinginan Anda untuk aplikasi Anda.

Mengonfigurasi QldbDriver objek

ParameterQldbDriverobjek mengelola koneksi ke buku besar Anda dengan mempertahankan kolamsidangyang digunakan kembali di seluruh transaksi. SEBUAHsesimewakili koneksi tunggal ke buku besar. QLDB mendukung satu transaksi aktif berjalan per sesi.

penting

Untuk versi driver yang lebih lama, fungsi penyatuan sesi masih dalamPooledQldbDriverobjek bukanQldbDriver. Jika Anda menggunakan salah satu versi berikut, ganti apa punQldbDriverbersamaPooledQldbDriveruntuk sisa topik ini.

Driver Versi
Java 1.1.0atau sebelumnya
.NET 0.1.0-beta
Node.js 1.0.0-rc.1atau sebelumnya
Python 2.0.2atau sebelumnya

ParameterPooledQldbDrivertidak digunakan lagi dalam versi terbaru dari driver. Kami merekomendasikan agar Anda melakukan peningkatan ke versi terbaruPooledQldbDriverkepadaQldbDriver.

Konfigurasi QldbDriver sebagai objek global

Untuk mengoptimalkan penggunaan driver dan sesi, pastikan bahwa hanya satu contoh global driver yang ada di instans aplikasi Anda. Misalnya di Jawa, Anda dapat menggunakaninjeksi ketergantunganKerangka kerja sepertiSpring,Google Guice, atauBelati. Contoh kode berikut menunjukkan cara mengkonfigurasiQldbDriversebagai singleton.

@Singleton public QldbDriver qldbDriver (AWSCredentialsProvider credentialsProvider, @Named(LEDGER_NAME_CONFIG_PARAM) String ledgerName) { QldbSessionClientBuilder builder = QldbSessionClient.builder(); if (null != credentialsProvider) { builder.credentialsProvider(credentialsProvider); } return QldbDriver.builder() .ledger(ledgerName) .transactionRetryPolicy(RetryPolicy .builder() .maxRetries(3) .build()) .sessionClientBuilder(builder) .build(); }

Konfigurasikan upaya coba ulang

Pengemudi secara otomatis mencoba kembali transaksi ketika pengecualian transien umum (sepertiSocketTimeoutExceptionatauNoHttpResponseException) terjadi. Untuk mengatur jumlah maksimum upaya coba lagi, Anda dapat menggunakanmaxRetriesparametertransactionRetryPolicyobjek konfigurasi saat membuat sebuah instanceQldbDriver. (Untuk versi driver yang lebih lama seperti yang tercantum di bagian sebelumnya, gunakanretryLimitparameterPooledQldbDriver.)

Nilai default maxRetries adalah 4.

Kesalahan sisi klien sepertiInvalidParameterExceptiontidak bisa dicoba lagi. Ketika mereka terjadi, transaksi dibatalkan, sesi dikembalikan ke kolam renang, dan pengecualian dilemparkan ke klien pengemudi.

Mengonfigurasi jumlah maksimum sesi dan transaksi

Jumlah maksimum sesi buku besar yang digunakan oleh instanceQldbDriveruntuk menjalankan transaksi didefinisikan olehmaxConcurrentTransactionsparameter. (Untuk versi driver yang lebih lama seperti yang tercantum di bagian sebelumnya, ini didefinisikan olehpoolLimitparameterPooledQldbDriver.)

Batas ini harus lebih besar dari nol dan kurang dari atau sama dengan jumlah maksimum koneksi HTTP terbuka yang memungkinkan klien sesi, sebagaimana didefinisikan oleh spesifikAWSSDK. Misalnya di Jawa, jumlah maksimum koneksi diatur dalamClientConfigurationobjek.

Nilai defaultmaxConcurrentTransactionsadalah pengaturan koneksi maksimumAWSSDK.

Ketika Anda mengkonfigurasiQldbDriverdalam aplikasi Anda, ambil pertimbangan penskalaan berikut:

  • Kolam renang Anda harus selalu memiliki setidaknya sebanyak jumlah transaksi yang berjalan secara bersamaan yang Anda rencanakan.

  • Dalam model multi-threaded di mana thread supervisor mendelegasikan ke thread pekerja, pengemudi harus memiliki setidaknya sebanyak sesi jumlah thread pekerja. Jika tidak, pada beban puncak, benang akan mengantri untuk sesi yang tersedia.

  • Batas layanan sesi aktif bersamaan per buku besar didefinisikan dalamKuota dan batasan di Amazon QLDB. Pastikan bahwa Anda tidak mengkonfigurasi lebih dari batas sesi bersamaan ini untuk digunakan untuk satu buku besar di semua klien.

Mencoba kembali pada pengecualian

Ketika mencoba kembali pada pengecualian yang terjadi di QLDB, mempertimbangkan rekomendasi berikut.

Mencoba kembali pada OCCConflictException

Kontrol konkurensi yang optimis(OCC) pengecualian konflik terjadi ketika data yang transaksi mengakses telah berubah sejak awal transaksi. QLDB melempar pengecualian ini ketika mencoba untuk melakukan transaksi. Pengemudi mencoba kembali transaksi hingga sebanyakmaxRetriesdikonfigurasi.

Untuk informasi lebih lanjut tentang OCC dan praktik terbaik untuk menggunakan indeks untuk membatasi konflik OCC, lihatModel Konkurensi Amazon QLDB.

Mencoba ulang pengecualian lain di luar QLDBDriver

Untuk mencoba kembali transaksi di luar driver ketika kustom, pengecualian yang ditetapkan aplikasi dilemparkan selama runtime, Anda harus membungkus transaksi. Misalnya di Jawa, kode berikut menunjukkan bagaimana menggunakanReslience4Jperpustakaan untuk mencoba kembali transaksi di QLDB.

private final RetryConfig retryConfig = RetryConfig.custom() .maxAttempts(MAX_RETRIES) .intervalFunction(IntervalFunction.ofExponentialRandomBackoff()) // Retry this exception .retryExceptions(InvalidSessionException.class, MyRetryableException.class) // But fail for any other type of exception extended from RuntimeException .ignoreExceptions(RuntimeException.class) .build(); // Method callable by a client public void myTransactionWithRetries(Params params) { Retry retry = Retry.of("registerDriver", retryConfig); Function<Params, Void> transactionFunction = Retry.decorateFunction( retry, parameters -> transactionNoReturn(params)); transactionFunction.apply(params); } private Void transactionNoReturn(Params params) { try (driver.execute(txn -> { // Transaction code }); } return null; }
catatan

Mencoba kembali transaksi di luar driver QLDB memiliki efek multiplier. Misalnya, jikaQldbDriverdikonfigurasi untuk mencoba lagi tiga kali, dan logika coba ulang kustom juga mencoba tiga kali, transaksi yang sama dapat dicoba ulang hingga sembilan kali.

Melakukan idempoten transaksi

Sebagai praktik terbaik, buat transaksi tulis Anda idempoten untuk menghindari efek samping yang tak terduga dalam kasus percobaan ulang. Transaksi adalahidempotenjika dapat berjalan beberapa kali dan menghasilkan hasil yang identik setiap kali.

Untuk mempelajari selengkapnya, lihat Model Konkurensi Amazon QLDB.

Mengoptimalkan performa

Untuk mengoptimalkan kinerja saat Anda menjalankan transaksi menggunakan driver, ambil pertimbangan berikut:

  • Parameterexecuteoperasi selalu membuat minimal tigaSendCommandAPI panggilan ke QLDB, termasuk perintah berikut:

    1. StartTransaction

    2. ExecuteStatement

      Perintah ini dipanggil untuk setiap pernyataan partiQL yang Anda jalankan diexecuteblok.

    3. CommitTransaction

    Pertimbangkan jumlah total panggilan API yang dibuat saat Anda menghitung keseluruhan beban kerja aplikasi Anda.

  • Secara umum, kami sarankan memulai dengan penulis single-threaded dan mengoptimalkan transaksi dengan batching beberapa pernyataan dalam satu transaksi. Maksimalkan kuota pada ukuran transaksi, ukuran dokumen, dan jumlah dokumen per transaksi, sebagaimana didefinisikan dalamKuota dan batasan di Amazon QLDB.

  • Jika batching tidak cukup untuk beban transaksi besar, Anda dapat mencoba multi-threading dengan menambahkan penulis tambahan. Namun, Anda harus mempertimbangkan dengan cermat persyaratan aplikasi Anda untuk urutan dokumen dan transaksi dan kompleksitas tambahan yang diperkenalkan ini.

Menjalankan beberapa pernyataan per transaksi

Seperti yang dijelaskan dalambagian sebelumnya, Anda dapat menjalankan beberapa pernyataan per transaksi untuk mengoptimalkan kinerja aplikasi Anda. Dalam contoh kode berikut, Anda query tabel dan kemudian memperbarui dokumen dalam tabel dalam transaksi. Anda melakukan ini dengan melewatkan ekspresi lambda keexecuteoperasi.

Java
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. public static boolean InsureCar(QldbDriver qldbDriver, final String vin) { final IonSystem ionSystem = IonSystemBuilder.standard().build(); final IonString ionVin = ionSystem.newString(vin); return qldbDriver.execute(txn -> { Result result = txn.execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin); if (!result.isEmpty()) { txn.execute("UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin); return true; } return false; }); }
.NET
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. public static async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin) { ValueFactory valueFactory = new ValueFactory(); IIonValue ionVin = valueFactory.NewString(vin); return await driver.Execute(async txn => { // Check if the vehicle is insured. Amazon.QLDB.Driver.IAsyncResult result = await txn.Execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin); if (await result.CountAsync() > 0) { // If the vehicle is not insured, insure it. await txn.Execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin); return true; } return false; }); }
Go
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. func InsureCar(driver *qldbdriver.QLDBDriver, vin string) (bool, error) { insured, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin) if err != nil { return false, err } hasNext := result.Next(txn) if !hasNext && result.Err() != nil { return false, result.Err() } if hasNext { _, err = txn.Execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin) if err != nil { return false, err } return true, nil } return false, nil }) if err != nil { panic(err) } return insured.(bool), err }
Node.js
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. async function insureCar(driver: QldbDriver, vin: string): Promise<boolean> { return await driver.executeLambda(async (txn: TransactionExecutor) => { const results: dom.Value[] = (await txn.execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)).getResultList(); if (results.length > 0) { await txn.execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin); return true; } return false; }); };
Python
# This code snippet is intentionally trivial. In reality you wouldn't do this because you'd # set your UPDATE to filter on vin and insured, and check if you updated something or not. def do_insure_car(transaction_executor, vin): cursor = transaction_executor.execute_statement( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin) first_record = next(cursor, None) if first_record: transaction_executor.execute_statement( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin) return True else: return False def insure_car(qldb_driver, vin_to_insure): return qldb_driver.execute_lambda( lambda executor: do_insure_car(executor, vin_to_insure))

Pengemudiexecuteoperasi implisit memulai sesi dan transaksi dalam sesi itu. Setiap pernyataan yang Anda jalankan dalam ekspresi lambda dibungkus dalam transaksi. Setelah semua pernyataan berjalan, driver otomatis melakukan transaksi. Jika ada pernyataan yang gagal setelah batas coba ulang otomatis habis, transaksi dibatalkan.

Memperbanyak pengecualian dalam transaksi

Saat menjalankan beberapa pernyataan per transaksi, kami umumnya tidak menyarankan Anda catch dan menelan pengecualian dalam transaksi.

Misalnya di Jawa, program berikut menangkap setiap contohRuntimeException, log kesalahan, dan terus. Contoh kode ini dianggap praktik buruk karena transaksi berhasil bahkan ketikaUPDATEgagal. Jadi, klien mungkin berasumsi bahwa pembaruan berhasil ketika tidak.

Awas

Jangan gunakan contoh kode ini. Ini disediakan untuk menunjukkan contoh anti-pola yang dianggap praktik buruk.

// DO NOT USE this code example because it is considered bad practice public static void main(final String... args) { ConnectToLedger.getDriver().execute(txn -> { final Result selectTableResult = txn.execute("SELECT * FROM Vehicle WHERE VIN ='123456789'"); // Catching an error inside the transaction is an anti-pattern because the operation might // not succeed. // In this example, the transaction succeeds even when the update statement fails. // So, the client might assume that the update succeeded when it didn't. try { processResults(selectTableResult); String model = // some code that extracts the model final Result updateResult = txn.execute("UPDATE Vehicle SET model = ? WHERE VIN = '123456789'", Constants.MAPPER.writeValueAsIonValue(model)); } catch (RuntimeException e) { log.error("Exception when updating the Vehicle table {}", e.getMessage()); } }); log.info("Vehicle table updated successfully."); }

Menyebarkan (gelembung up) pengecualian sebagai gantinya. Jika ada bagian dari transaksi gagal, biarkanexecuteoperasi batalkan transaksi sehingga klien dapat menangani pengecualian sesuai.