Datastore API Asinkron

Datastore API Asinkron memungkinkan Anda untuk melakukan panggilan paralel yang tidak memblokir ke datastore dan mengambil hasil panggilan ini di lain waktu dalam menangani permintaan. Dokumentasi ini menjelaskan aspek-aspek Datastore API Asinkron berikut:

Bekerja dengan Layanan Datastore Asinkron

Dengan API datastore asinkron, Anda melakukan panggilan datastore menggunakan metode antarmuka AsyncDatastoreService. Anda mendapatkan objek ini dengan memanggil metode class getAsyncDatastoreService() dari class DatastoreServiceFactory.

import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;

// ...
AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService();

AsyncDatastoreService mendukung operasi yang sama seperti DatastoreService, kecuali sebagian besar metodenya langsung menampilkan Future yang hasilnya dapat Anda blokir di lain waktu. Misalnya, DatastoreService.get() menampilkan Entity, tetapi AsyncDatastoreService.get() menampilkan Future<Entity>.

// ...

Key key = KeyFactory.createKey("Employee", "Max");
// Async call returns immediately
Future<Entity> entityFuture = datastore.get(key);

// Do other stuff while the get operation runs in the background...

// Blocks if the get operation has not finished, otherwise returns instantly
Entity entity = entityFuture.get();

Catatan: Pengecualian tidak akan ditampilkan sampai Anda memanggil metode get(). Dengan memanggil metode ini, Anda dapat memverifikasi bahwa operasi asinkron berhasil.

Jika Anda memiliki AsyncDatastoreService, tetapi perlu menjalankan operasi secara sinkron, panggil metode AsyncDatastoreService yang sesuai, lalu segera blokir hasilnya:

// ...

Entity entity = new Employee("Employee", "Alfred");
// ... populate entity properties

// Make a sync call via the async interface
Key key = datastore.put(key).get();

Bekerja dengan Transaksi Asinkron

Panggilan API datastore asinkron dapat berperan dalam transaksi sama seperti panggilan sinkron. Berikut adalah fungsi yang menyesuaikan gaji Employee dan menulis tambahan entity SalaryAdjustment dalam entity group yang sama dengan Employee, semuanya dalam satu transaksi.

void giveRaise(AsyncDatastoreService datastore, Key employeeKey, long raiseAmount)
        throws Exception {
    Future<Transaction> txn = datastore.beginTransaction();

    // Async call to lookup the Employee entity
    Future<Entity> employeeEntityFuture = datastore.get(employeeKey);

    // Create and put a SalaryAdjustment entity in parallel with the lookup
    Entity adjustmentEntity = new Entity("SalaryAdjustment", employeeKey);
    adjustmentEntity.setProperty("adjustment", raiseAmount);
    adjustmentEntity.setProperty("adjustmentDate", new Date());
    datastore.put(adjustmentEntity);

    // Fetch the result of our lookup to make the salary adjustment
    Entity employeeEntity = employeeEntityFuture.get();
    long salary = (Long) employeeEntity.getProperty("salary");
    employeeEntity.setProperty("salary", salary + raiseAmount);

    // Re-put the Employee entity with the adjusted salary.
    datastore.put(employeeEntity);
    txn.get().commit(); // could also call txn.get().commitAsync() here
}

Contoh ini mengilustrasikan perbedaan penting antara panggilan asinkron tanpa transaksi dan panggilan asinkron dengan transaksi. Jika Anda tidak menggunakan transaksi, satu-satunya cara untuk memastikan bahwa panggilan asinkron individual telah selesai adalah dengan mengambil nilai yang dihasilkan oleh Future yang muncul saat panggilan dilakukan. Saat Anda menggunakan transaksi, memanggil Transaction.commit() akan memblokir hasil dari semua panggilan asinkron yang dilakukan sejak transaksi dimulai sebelum melakukannya.

Jadi, dari contoh di atas, meskipun panggilan asinkron untuk menyisipkan entity SalaryAdjustment mungkin masih belum terselesaikan saat memanggil commit(), commit tidak akan terjadi hingga penyisipan selesai. Demikian pula, jika Anda memilih untuk memanggil commitAsync() dan bukan commit(), memanggil get() di Future akan dikembalikan oleh blok commitAsync() hingga semua panggilan asinkron selesai.

Catatan: Transaksi dikaitkan dengan thread tertentu, bukan instance tertentu dari DatastoreService atau AsyncDatastoreService. Ini berarti bahwa jika Anda memulai transaksi dengan DatastoreService dan melakukan panggilan asinkron dengan AsyncDatastoreService, panggilan asinkron akan berpartisipasi di transaksi tersebut. Atau, secara lebih ringkas, DatastoreService.getCurrentTransaction() dan AsyncDatastoreService.getCurrentTransaction() selalu menampilkan Transaction yang sama.

Bekerja dengan Masa Depan

Future Javadoc menjelaskan sebagian besar hal yang perlu Anda ketahui agar berhasil menangani Future yang ditampilkan oleh Datastore API Asinkron, tetapi ada beberapa hal khusus App Engine yang perlu Anda ketahui:

Kueri Asinkron

Saat ini kami tidak mengekspos API yang asinkron secara eksplisit untuk kueri. Namun, ketika Anda memanggil PreparedQuery.asIterable(), PreparedQuery.asIterator() atau PreparedQuery.asList(FetchOptions fetchOptions), baik DatastoreService maupun AsyncDatastoreService segera mengembalikan dan melakukan pengambilan data secara asinkron. Hal ini memungkinkan aplikasi Anda melakukan pekerjaan secara paralel saat hasil kueri diambil.

// ...

Query q1 = new Query("Salesperson");
q1.setFilter(new FilterPredicate("dateOfHire", FilterOperator.LESS_THAN, oneMonthAgo));

// Returns instantly, query is executing in the background.
Iterable<Entity> recentHires = datastore.prepare(q1).asIterable();

Query q2 = new Query("Customer");
q2.setFilter(new FilterPredicate("lastContact", FilterOperator.GREATER_THAN, oneYearAgo));

// Also returns instantly, query is executing in the background.
Iterable<Entity> needsFollowup = datastore.prepare(q2).asIterable();

schedulePhoneCall(recentHires, needsFollowUp);

Kapan Menggunakan Panggilan Datastore Asinkron

Operasi yang ditampilkan oleh antarmuka DatastoreService akan tersinkron. Misalnya, saat Anda memanggil DatastoreService.get(), kode Anda terblokir hingga panggilan ke datastore selesai. Jika satu-satunya hal yang perlu dilakukan aplikasi Anda adalah merender hasil get() dalam HTML, memblokir hingga panggilan selesai adalah hal yang sangat wajar untuk dilakukan. Namun, jika aplikasi memerlukan hasil get() ditambah hasil Query untuk merender respons, dan jika get() serta Query tidak memiliki dependensi data, maka menunggu hingga get() selesai untuk memulai Query adalah pemborosan waktu. Berikut adalah contoh beberapa kode yang dapat diperbaiki menggunakan API asinkron:

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Key empKey = KeyFactory.createKey("Employee", "Max");

// Read employee data from the Datastore
Entity employee = datastore.get(empKey); // Blocking for no good reason!

// Fetch payment history
Query query = new Query("PaymentHistory");
PreparedQuery pq = datastore.prepare(query);
List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10));
renderHtml(employee, result);

Daripada menunggu get() selesai, gunakan instance AsyncDatastoreService untuk menjalankan panggilan secara asinkron:

AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService();
Key empKey = KeyFactory.createKey("Employee", "Max");

// Read employee data from the Datastore
Future<Entity> employeeFuture = datastore.get(empKey); // Returns immediately!

// Fetch payment history for the employee
Query query = new Query("PaymentHistory", empKey);
PreparedQuery pq = datastore.prepare(query);

// Run the query while the employee is being fetched
List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10));
// Implicitly performs query asynchronously
Entity employee = employeeFuture.get(); // Blocking!
renderHtml(employee, result); 

Versi sinkron dan asinkron dari kode ini menggunakan CPU dalam jumlah yang sama (lagipula, keduanya melakukan jumlah pekerjaan yang sama), tetapi karena versi asinkron memungkinkan dua operasi datastore dijalankan secara paralel, versi asinkron memiliki latensi yang lebih rendah. Secara umum, jika Anda perlu melakukan beberapa operasi datastore yang tidak memiliki dependensi data, AsyncDatastoreService dapat meningkatkan latensi secara signifikan.