Membuat Struktur Data untuk Konsistensi Kuat

Datastore memberikan ketersediaan, skalabilitas, dan ketahanan tinggi dengan mendistribusikan data ke banyak mesin dan menggunakan replikasi sinkron di area geografis yang luas. Namun, desain ini memiliki konsekuensinya sendiri, yaitu throughput operasi tulis untuk satu entity group dibatasi pada sekitar satu commit per detik. Selain itu, terdapat batasan pada kueri atau transaksi yang mencakup beberapa entity group. Halaman ini menjelaskan batasan tersebut secara lebih mendetail dan membahas praktik terbaik dalam menyusun struktur data untuk mendukung konsistensi yang kuat sambil tetap memenuhi persyaratan throughput operasi tulis aplikasi Anda.

Operasi baca yang sangat konsisten selalu menampilkan data saat ini, dan jika dilakukan dalam transaksi, akan tampak berasal dari satu snapshot yang konsisten. Namun, kueri harus menentukan filter ancestor agar konsisten atau berpartisipasi dalam transaksi, dan transaksi dapat melibatkan maksimal 25 entity group. Operasi baca yang konsisten pada akhirnya tidak memiliki batasan tersebut, dan cukup memadai dalam banyak kasus. Dengan menggunakan operasi baca yang konsisten, Anda dapat mendistribusikan data ke lebih banyak entity group, sehingga Anda dapat memperoleh throughput operasi tulis yang lebih besar dengan mengeksekusi commit secara paralel pada entity group yang berbeda. Namun, Anda perlu memahami karakteristik operasi baca yang konsisten agar dapat menentukan apakah operasi baca tersebut cocok untuk aplikasi Anda:

  • Hasil dari operasi baca ini mungkin tidak mencerminkan transaksi terbaru. Hal ini dapat terjadi karena operasi baca ini tidak memastikan bahwa replika tempatnya berjalan sudah yang terbaru. Sebagai gantinya, model ini menggunakan data apa pun yang tersedia di replika tersebut pada saat mengeksekusi kueri. Latensi replikasi hampir selalu kurang dari beberapa detik.
  • Transaksi yang di-commit dan mencakup beberapa entity mungkin terlihat telah diterapkan ke beberapa entity dan tidak untuk entity yang lainnya. Namun, perlu diperhatikan bahwa transaksi tidak akan pernah diterapkan sebagian dalam satu entity.
  • Hasil kueri dapat menyertakan entity yang seharusnya tidak disertakan sesuai dengan kriteria filter, dan mungkin mengecualikan entity yang seharusnya disertakan. Hal ini dapat terjadi karena indeks mungkin dibaca pada versi yang berbeda dengan versi yang dibaca oleh entity tersebut.

Untuk memahami cara menyusun data Anda demi konsistensi yang kuat, bandingkan dua pendekatan yang berbeda untuk aplikasi buku tamu yang sederhana. Pendekatan pertama akan membuat root entity baru untuk setiap entity yang dibuat:

protected Entity createGreeting(
    DatastoreService datastore, User user, Date date, String content) {
  // No parent key specified, so Greeting is a root entity.
  Entity greeting = new Entity("Greeting");
  greeting.setProperty("user", user);
  greeting.setProperty("date", date);
  greeting.setProperty("content", content);

  datastore.put(greeting);
  return greeting;
}

Kemudian, pendekatan tersebut akan membuat kueri mengenai jenis entity Greeting untuk sepuluh salam terbaru.

protected List<Entity> listGreetingEntities(DatastoreService datastore) {
  Query query = new Query("Greeting").addSort("date", Query.SortDirection.DESCENDING);
  return datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));
}

Namun, karena Anda menggunakan kueri non-ancestor, replika yang digunakan untuk menjalankan kueri dalam skema ini mungkin belum melihat salam baru pada saat kueri dieksekusi. Meskipun demikian, hampir semua operasi tulis akan tersedia untuk kueri non-ancestor dalam beberapa detik setelah commit. Untuk banyak aplikasi, solusi yang memberikan hasil kueri non-ancestor dalam konteks perubahan pengguna saat ini biasanya akan cukup untuk membuat latensi replikasi tersebut dapat diterima sepenuhnya.

Jika konsistensi yang kuat penting untuk aplikasi Anda, pendekatan alternatif yang bisa dipertimbangkan adalah menulis entity dengan jalur ancestor yang mengidentifikasi root entity yang sama di semua entity yang harus dibaca dalam satu kueri ancestor yang sangat konsisten:

protected Entity createGreeting(
    DatastoreService datastore, User user, Date date, String content) {
  // String guestbookName = "my guestbook"; -- Set elsewhere (injected to the constructor).
  Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);

  // Place greeting in the same entity group as guestbook.
  Entity greeting = new Entity("Greeting", guestbookKey);
  greeting.setProperty("user", user);
  greeting.setProperty("date", date);
  greeting.setProperty("content", content);

  datastore.put(greeting);
  return greeting;
}

Selanjutnya, Anda dapat menjalankan kueri ancestor yang sangat konsisten dalam entity group yang diidentifikasi oleh root entity umum:

protected List<Entity> listGreetingEntities(DatastoreService datastore) {
  Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);
  Query query =
      new Query("Greeting", guestbookKey)
          .setAncestor(guestbookKey)
          .addSort("date", Query.SortDirection.DESCENDING);
  return datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));
}

Pendekatan ini mencapai konsistensi yang kuat dengan melakukan operasi tulis ke satu entity group per buku tamu, tetapi juga membatasi perubahan pada buku tamu agar tidak lebih dari 1 operasi tulis per detik (batas yang didukung untuk entity group). Jika aplikasi Anda kemungkinan akan mengalami penggunaan operasi tulis yang lebih berat, sebaiknya pertimbangkan untuk menggunakan cara lain. Misalnya, Anda dapat menempatkan postingan terbaru dalam memcache yang memiliki masa berlaku dan menampilkan gabungan postingan terbaru dari memcache dan Datastore. Selain itu, Anda dapat menyimpan semuanya dalam cache di cookie, menempatkan beberapa status di URL, atau hal yang lainnya. Tujuannya adalah untuk menemukan solusi penyimpanan data ke dalam cache yang menyediakan data untuk pengguna saat ini ketika mereka memposting ke aplikasi Anda. Ingat, jika melakukan operasi get, kueri ancestor, atau operasi apa pun dalam transaksi, Anda akan selalu melihat data tertulis terbaru.