App Engine Datastore API untuk paket layanan lama

Dokumen ini menjelaskan model data untuk objek yang disimpan di Datastore, bagaimana kueri disusun menggunakan API, dan bagaimana transaksi diproses. Untuk melihat isi paket datastore, lihat referensi paket datastore.

Entity

Objek di Datastore dikenal sebagai entity. Entity memiliki satu atau beberapa properti yang telah diberi nama, yang masing-masing dapat memiliki satu atau beberapa nilai. Berbagai jenis data dapat memiliki nilai properti, ini mencakup bilangan bulat, angka floating point, string, tanggal, dan data biner. Kueri di properti dengan beberapa nilai menguji apakah salah satu nilai memenuhi kriteria kueri. Hal ini membuat properti tersebut berguna untuk pengujian keanggotaan.

Jenis, kunci, dan ID

Setiap entity Datastore memiliki jenis tertentu, yang mengategorikan entity untuk tujuan kueri; misalnya, aplikasi sumber daya manusia mungkin mewakili setiap karyawan di sebuah perusahaan dengan entity jenis Employee. Selain itu, setiap entity memiliki kunci sendiri, yang mengidentifikasinya secara unik. Kunci ini terdiri dari komponen berikut:

  • Jenis entity
  • ID, yang bisa berupa
    • string nama kunci
    • ID bilangan bulat
  • Jalur ancestor opsional yang mencari entity dalam hierarki Datastore

ID ditetapkan saat entity dibuat. Karena merupakan bagian dari kunci entity, maka entity akan dikaitkan secara permanen dengan entity dan tidak dapat diubah. Ini dapat ditetapkan dengan salah satu dari dua cara berikut:

  • Aplikasi Anda dapat menentukan string nama kunci sendiri untuk entity.
  • Anda dapat meminta Datastore menetapkan ID numerik bilangan bulat secara otomatis ke entity.

Menetapkan ID

Server runtime dapat dikonfigurasi untuk membuat ID secara otomatis menggunakan dua kebijakan ID otomatis yang berbeda:

  • Kebijakan default menghasilkan urutan ID yang didistribusikan secara kira-kira dengan seragam. Panjang setiap ID dapat mencapai 16 digit.
  • Kebijakan legacy membuat urutan ID bilangan bulat yang lebih kecil dan tidak berurutan.

Jika Anda ingin menampilkan ID entity kepada pengguna, dan/atau bergantung pada urutannya, sebaiknya Anda menggunakan alokasi manual.

Jalur ancestor

Entity dalam Cloud Datastore membentuk ruang yang terstruktur secara hierarkis, yang menyerupai struktur direktori sistem file. Saat membuat entity, Anda dapat memilih untuk menetapkan entity lain sebagai parent;; entity barunya adalah parent; dari parent entity tersebut (perlu diperhatikan bahwa tidak seperti dalam sistem file, parent entity tidak harus benar-benar ada). Entity tanpa parent adalah root entity. Kaitan entity dan induknya bersifat permanen, dan tidak dapat diubah setelah entity tersebut dibuat. Cloud Datastore tidak akan pernah menetapkan ID numerik yang sama ke dua entity dengan parent yang sama, atau ke dua root entity (yang tidak memiliki parent).

Induk, induk dari induk, dan seterusnya dari suatu entity secara rekursif adalah ancestor-nya; turunannya, turunan dari turunannya, dan seterusnya adalah turunannya. Root entity dan semua turunannya termasuk dalam entity group yang sama. Urutan entity yang dimulai dengan root entity dan berlanjut dari parent ke turunan, yang mengarah ke entity tertentu, merupakan jalur ancestor entity tersebut. Kunci lengkap yang mengidentifikasi entity terdiri dari urutan pasangan jenis-ID yang menentukan jalur ancestor-nya dan berakhir dengan entity itu sendiri:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Untuk root entity, jalur ancestor kosong dan kuncinya hanya terdiri dari jenis dan ID entity itu sendiri:

[Person:GreatGrandpa]

Konsep ini diilustrasikan oleh diagram berikut:

Menampilkan hubungan root entity dengan entity
  turunan dalam entity group

Kueri dan indeks

Selain mengambil entity dari Datastore secara langsung dengan kuncinya, aplikasi dapat menjalankan kueri untuk mengambilnya berdasarkan nilai propertinya ini. Kueri beroperasi pada entity dari jenis tertentu; kueri dapat menentukan filter pada entity' properti nilai kunci, dan ancestor, serta dapat menampilkan nol entity atau beberapa entity sebagai hasil. Kueri juga dapat menentukan memilah urutan untuk mengurutkan hasil berdasarkan nilai propertinya. Hasilnya mencakup semua entity yang memiliki setidaknya satu nilai untuk setiap properti yang disebutkan dalam filter dan tata urutan, serta yang nilai propertinya memenuhi semua kriteria filter yang ditentukan. Kueri dapat menampilkan seluruh entity, proyeksi entitas , atau hanya entitas kunci.

Kueri tipikal mencakup hal berikut:

  • Jenis entity tempat kueri diterapkan
  • Nol atau beberapa filter berdasarkan nilai properti, kunci, dan ancestor entity
  • Nol atau beberapa pilah urutan untuk mengurutkan hasil
Saat dieksekusi, kueri akan mengambil semua entity dari jenis tertentu yang memenuhi semua filter yang diberikan, yang diurutkan dalam urutan yang ditentukan. Kueri dijalankan sebagai hanya baca.

Catatan: Untuk menghemat memori dan meningkatkan performa, kueri harus, jika memungkinkan, menentukan batas jumlah hasil yang ditampilkan.

Kueri juga dapat menyertakan filter ancestor yang membatasi hasil hanya untuk grup entity yang diturunkan dari ancestor yang ditentukan. Kueri semacam ini dikenal sebagai kueri ancestor. Secara default, kueri ancestor menampilkan hasil yang sangat konsisten, yang dijamin selalu diperbarui dengan perubahan terbaru dalam data. Sebaliknya, kueri non-ancestor dapat mencakup seluruh Datastore bukan hanya satu grup entity, tetapi baru.akan menjadi konsisten kemudian dan mungkin menampilkan hasil yang tidak relevan lagi. Jika konsistensi yang kuat penting untuk aplikasi Anda, mungkin Anda perlu mempertimbangkan hal ini saat menyusun data, menempatkan entity terkait dalam grup entity yang sama sehingga dapat diambil dengan ancestor, bukan kueri non-ancestor untuk informasi selengkapnya.

App Engine menentukan terlebih dahulu indeks sederhana di setiap properti entity. Aplikasi App Engine dapat menentukan indeks kustom lebih lanjut dalam file konfigurasi indeks bernama index.yaml. Server pengembangan secara otomatis menambahkan saran ke file ini saat menemukan kueri yang tidak dapat dijalankan dengan indeks yang ada. Anda dapat menyesuaikan indeks secara manual dengan mengedit file sebelum mengupload aplikasi.

Catatan: Mekanisme kueri berbasis indeks mendukung berbagai kueri dan cocok untuk sebagian besar aplikasi. Namun, mekanisme ini tidak mendukung beberapa jenis kueri yang umum digunakan dalam teknologi database lainnya: khususnya, join dan kueri agregat tidak didukung dalam mesin kueri Datastore. Lihat halaman Kueri Datastore untuk mengetahui batasan pada kueri Datastore.

Transaksi

Setiap upaya untuk menyisipkan, memperbarui, atau menghapus entity terjadi dalam konteks transaksi. Satu transaksi dapat mencakup sejumlah operasi tersebut. Untuk menjaga konsistensi data, transaksi tersebut memastikan bahwa semua operasi yang ada di dalamnya diterapkan ke Datastore sebagai unit atau, jika salah satu operasi gagal, tidak ada satu pun operasi yang diterapkan.

Anda dapat melakukan beberapa tindakan terhadap sebuah entity dalam satu transaksi. Misalnya, untuk menambahkan kolom penghitung dalam objek, Anda perlu membaca nilai penghitung, menghitung nilai baru, lalu menyimpannya kembali. Tanpa transaksi, proses lain mungkin dapat menambah penghitung antara waktu Anda membaca nilai dan saat Anda memperbaruinya, sehingga aplikasi Anda menimpa nilai yang diperbarui. Melakukan pembacaan, perhitungan, dan penulisan dalam satu transaksi memastikan bahwa tidak ada proses lain yang dapat mengganggu proses penambahan.

Transaksi dan entity group

Hanya kueri ancestor yang diizinkan dalam transaksi: yaitu, setiap kueri transaksi harus dibatasi ke satu entity group. Transaksi itu sendiri dapat berlaku untuk beberapa entity, yang dapat dimiliki oleh satu grup entity atau (dalam kasus transaksi lintas grup) hingga maksimum dua puluh lima entity group yang berbeda.

Datastore menggunakan konkurensi optimis untuk mengelola transaksi. Jika dua transaksi atau lebih mencoba mengubah entity group yang sama secara bersamaan (baik memperbarui entity yang sudah ada atau membuat entity baru), transaksi pertama yang di-commit akan berhasil dan transaksi lainnya akan gagal saat di-commit. Transaksi lain ini kemudian dapat dicoba lagi dalam data yang diperbarui. Perlu diperhatikan bahwa hal ini akan membatasi jumlah penulisan serentak yang dapat Anda lakukan terhadap entity mana pun dalam entity group tertentu.

Transaksi lintas grup

Transaksi di entity yang termasuk dalam entity group yang berbeda disebut transaksi lintas grup (XG). Transaksi dapat diterapkan pada maksimum dua puluh lima entity group, dan akan berhasil selama tidak ada transaksi serentak yang menyentuh salah satu entity group tempat transaksi tersebut diterapkan. Hal ini memberikan lebih banyak fleksibilitas kepada Anda dalam mengatur data, karena Anda tidak dipaksa untuk menempatkan potongan data yang berbeda dalam ancestor yang sama hanya untuk melakukan penulisan atomik di data tersebut.

Seperti dalam transaksi grup tunggal, Anda tidak dapat melakukan kueri non-ancestor dalam transaksi XG. Namun, Anda dapat menjalankan kueri ancestor di entity group yang terpisah. Kueri nontransaksi (non-ancestor) dapat melihat semua, beberapa, atau tidak ada hasil dari transaksi yang sudah di-commit sebelumnya. (Untuk latar belakang tentang masalah ini, lihat Penulisan Datastore dan Visibilitas Data.) Namun, kueri non-transaksi seperti ini lebih cenderung melihat hasil dari transaksi XG yang di-commit sebagian daripada dari transaksi grup tunggal yang di-commit sebagian.

Transaksi XG yang hanya menyentuh satu entity group memiliki performa dan biaya yang sama persis dengan transaksi non-XG grup tunggal. Dalam transaksi XG yang menyentuh beberapa entity group, biaya operasi akan sama seperti jika dilakukan dalam transaksi non-XG, tetapi mungkin mengalami latensi yang lebih tinggi.

Penulisan dan visibilitas data Datastore

Data ditulis ke Datastore dalam dua fase:

  1. Dalam fase Commit, data entity dicatat dalam log transaksi dari sebagian besar replika, dan setiap replika yang tidak dicatat akan ditandai sebagai tidak memiliki log terbaru.
  2. Fase Penerapan terjadi secara independen di setiap replika, dan terdiri dari dua tindakan yang dilakukan secara paralel:
    • Data entity ditulis dalam replika tersebut.
    • Baris indeks untuk entity ditulis dalam replika tersebut. (Perhatikan bahwa ini bisa memakan waktu lebih lama daripada penulisan data itu sendiri.)

Operasi tulis segera ditampilkan setelah fase Commit, lalu fase Penerapan kemudian terjadi secara asinkron, mungkin pada waktu yang berbeda di setiap replika, dan mungkin dengan penundaan beberapa ratus milidetik atau lebih sejak penyelesaian fase Commit. Jika terjadi kegagalan selama fase Commit, ada percobaan ulang otomatis; tetapi jika kegagalan berlanjut, Datastore akan menampilkan pesan error yang diterima aplikasi Anda sebagai pengecualian. Jika fase Commit berhasil, tetapi Apply gagal dalam replika tertentu, Penerapan akan di-roll hingga selesai dalam replika tersebut jika salah satu hal berikut terjadi:

  • Datastore berkala memeriksa tugas Commit yang belum selesai dan menerapkannya.
  • Operasi tertentu (get, put, delete, dan kueri ancestor) yang menggunakan grup entity yang terpengaruh menyebabkan perubahan apa pun yang telah di-commit tetapi belum diterapkan untuk diselesaikan dalam replika tempatnya dieksekusi sebelum melanjutkan dengan operasi baru.

Perilaku tulis ini dapat memiliki beberapa implikasi tentang bagaimana dan kapan data terlihat oleh aplikasi Anda di berbagai bagian dari fase Commit dan Penerapan:

  • Jika operasi tulis melaporkan error waktu tunggu, operasi tersebut tidak dapat ditentukan (tanpa mencoba membaca data) apakah operasi berhasil atau gagal.
  • Karena Datastore mendapatkan dan kueri ancestor menerapkan modifikasi yang belum dilakukan pada replika tempatnya dieksekusi, operasi ini akan selalu melihat tampilan yang konsisten dari semua transaksi sebelumnya yang berhasil. Hal ini berarti bahwa operasi get (mencari entity yang diupdate berdasarkan kuncinya) dijamin melihat versi terbaru entity tersebut.
  • Kueri non-ancestor dapat menampilkan hasil yang sudah tidak berlaku karena mungkin dieksekusi di replika yang transaksi terbarunya belum diterapkan. Hal ini dapat terjadi meskipun operasi yang dilakukan dipastikan akan menerapkan transaksi yang belum diselesaikan karena kueri dapat dieksekusi dalam replika yang berbeda dari operasi sebelumnya.
  • Waktu perubahan serentak dapat memengaruhi hasil kueri non-ancestor. Jika entity awalnya memenuhi kueri, tetapi kemudian diubah sehingga tidak lagi memenuhi kueri, entity tersebut mungkin masih disertakan dalam kumpulan hasil kueri jika perubahan belum diterapkan ke indeks dalam replika tempat kueri dieksekusi.

Statistik Datastore

Datastore menyimpan statistik tentang data yang disimpan untuk aplikasi, seperti berapa banyak entity yang ada dari jenis tertentu atau berapa banyak ruang yang digunakan oleh nilai properti dari jenis tertentu. Anda dapat melihat statistik ini di halaman Dasbor Datastore konsol Google Cloud. Anda juga dapat menggunakan Datastore API untuk mengakses nilai ini secara terprogram dari dalam aplikasi dengan membuat kueri untuk entity bernama khusus; lihat Statistik Datastore di Go 1.11 untuk informasi selengkapnya.

Contoh Datastore Go 1.11

Di Datastore Go, entity dibuat dari nilai struct. Kolom struktur menjadi properti entity. Untuk membuat entity baru, siapkan nilai yang ingin Anda simpan, buat kunci, dan teruskan keduanya ke datastore.Put(). Memperbarui entity yang ada sama dengan melakukan Put() lain menggunakan kunci yang sama. Untuk mengambil entity dari Datastore, pertama-tama Anda harus menyiapkan nilai pada entity yang akan diekstrak, lalu meneruskan kunci dan pointer ke nilai tersebut ke datastore.Get().

Contoh ini menyimpan dan mengambil beberapa data dari Datastore:

import (
	"fmt"
	"net/http"
	"time"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/user"
)

type Employee struct {
	Name     string
	Role     string
	HireDate time.Time
	Account  string
}

func handle(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	e1 := Employee{
		Name:     "Joe Citizen",
		Role:     "Manager",
		HireDate: time.Now(),
		Account:  user.Current(ctx).String(),
	}

	key, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "employee", nil), &e1)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var e2 Employee
	if err = datastore.Get(ctx, key, &e2); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "Stored and retrieved the Employee named %q", e2.Name)
}

Lihat Referensi Datastore untuk detail selengkapnya.