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:
- Saat Anda memanggil Future.get(long timeout, TimeUnit unit), waktu tunggu terpisah dari batas waktu RPC yang ditetapkan saat Anda membuat
AsyncDatastoreService
. Untuk mengetahui informasi selengkapnya, lihat Konsistensi Data dalam Kueri Cloud Datastore. - Saat Anda memanggil Future.cancel(boolean mayInterruptIfRunning) dan panggilan tersebut menampilkan
true
, bukan berarti status datastore Anda tidak berubah. Dengan kata lain, membatalkanFuture
tidak sama dengan melakukan roll back transaksi.
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.