Do you want to pick up from where you left of?
Take me there

Mnesia

Mnesia adalah sebuah sistem manajemen database terdistribusi real-time yang kuat.

Sekilas

Mnesia adalah sebuah Sistem Manajemen Database (DBSM) yang disertakan bersama Erlang Runtime System yang tentu saja bisa kita gunakan dengan Elixir. Model data hibrid antara relasional dan object Mnesia adalah apa yang membuatnya cocok untuk membuat aplikasi terdistribusi pada skala apapun.

Kapan Digunakan

Kapan kita menggunakan sebuah teknologi seringkali jadi masalah yang membingungkan. Jika anda bisa menjawab ‘ya’ pada satu saja pertanyaan berikut, maka ini adalah indikasi bagus untuk menggunakan Mnesia sebagai ganti ETS atau DETS.

Schema

Karena Mnesia adalah bagian dari Erlang core, bukannya Elixir, kita harus mengaksesnya dengan sintaks colon (Lihat Pelajaran: Erlang Interoperability) seperti ini:


iex> :mnesia.create_schema([node()])

# atau jika anda lebih suka rasa Elixir...

iex> alias :mnesia, as: Mnesia
iex> Mnesia.create_schema([node()])

Untuk pelajaran ini, kita akan mengambil pendekatan yang terakhir ketika bekerja dengan API Mnesia. Mnesia.create_schema/1 menginisialisasikan sebuah schema kosong yang baru dan memasukkan sebuah Node List. Dalam kasus ini, kita memasukkan node yang terasosiasikan dengan sesi IEx kita.

Node

Begitu kita sudah menjalankan perintah Mnesia.create_schema([node()]) via IEx, anda mestinya melihat sebuah folder bernama Mnesia.nonode@nohost atau yang serupa di dalam direktori kerja yang sekarang ada. Anda mungkin bertanya-tanya apa artinya nonode@nohost karena sebelumnya kita belum pernah melihatnya. Mari kita lihat.

$ iex --help
Usage: iex [options] [.exs file] [data]

  -v                Prints version
  -e "command"      Evaluates the given command (*)
  -r "file"         Requires the given files/patterns (*)
  -S "script"       Finds and executes the given script
  -pr "file"        Requires the given files/patterns in parallel (*)
  -pa "path"        Prepends the given path to Erlang code path (*)
  -pz "path"        Appends the given path to Erlang code path (*)
  --app "app"       Start the given app and its dependencies (*)
  --erl "switches"  Switches to be passed down to Erlang (*)
  --name "name"     Makes and assigns a name to the distributed node
  --sname "name"    Makes and assigns a short name to the distributed node
  --cookie "cookie" Sets a cookie for this distributed node
  --hidden          Makes a hidden node
  --werl            Uses Erlang's Windows shell GUI (Windows only)
  --detached        Starts the Erlang VM detached from console
  --remsh "name"    Connects to a node using a remote shell
  --dot-iex "path"  Overrides default .iex.exs file and uses path instead;
                    path can be empty, then no file will be loaded

** Options marked with (*) can be given more than once
** Options given after the .exs file or -- are passed down to the executed code
** Options can be passed to the VM using ELIXIR_ERL_OPTIONS or --erl

Ketika kita berikan opsi --help ke IEx dari command line kita ditunjuki semua opsi yang mungkin. Kita bisa melihat bahwa ada opsi --name dan --sname untuk memberikan informasi ke node. Sebuah node hanyalah sebuah Erlang Virtual Machine yang sedang berjalan yang menangani sendiri komunikasi, pembersihan sampah (garbage collection), penjadwalan, memori, dan lain-lain. Secara default node dinamai nonode@nohost.

$ iex --name learner@elixirschool.com

Erlang/OTP {{ site.erlang.OTP }} [erts-{{ site.erlang.erts }}] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir ({{ site.elixir.version }}) - press Ctrl+C to exit (type h() ENTER for help)
iex(learner@elixirschool.com)> Node.self
:"learner@elixirschool.com"

Sebagaimana bisa kita lihat, node yang sedang kita jalankan adalah sebuah atom bernama :"learner@elixirschool.com". Kalau kita jalankan Mnesia.create_schema([node()]) lagi, kita akan lihat bahwa ia membuat folder lagi bernama Mnesia.learner@elixirschool.com. Tujuan dari hal ini adalah sederhana. Node di Erlang digunakan untuk mengkoneksi ke node lain untuk berbagi (distribusi) informasi dan sumber daya (resource). Hal ini tidak harus dibatasi pada mesin yang sama dan dapat berkomunikasi lewat LAN, internet, dan lain-lain.

Menjalankan Mnesia

Sekarang setelah kita mahami dasar-dasarnya dan sudah mensetup databasenya, kita sekarang bisa jalankan DBMS Mnesia dengan perintah Mnesia.start/0 command.

iex> alias :mnesia, as: Mnesia
iex> Mnesia.create_schema([node()])
:ok
iex> Mnesia.start()
:ok

Patut diingat ketika menjalankan sebuah sistem terdistribusi dengan dua node atau lebih, fungsi Mnesia.start/1 harus dijalankan pada semua node yang berpartisipasi.

Membuat Tabel

Fungsi Mnesia.create_table/2 digunakan untuk membuat tabel dalam database kita. Di bawah ini kita membuat tabel bernama Person dan kemudian memasukkan keyword list yang mendefinisikan schema tabelnya.

iex> Mnesia.create_table(Person, [attributes: [:id, :name, :job]])
{:atomic, :ok}

Kita mendefinisikan kolom-kolom menggunakan atom-atom :id, :name, dan :job. Ketika kita menjalankan Mnesia.create_table/2, fungsi tersebut akan mengembalikan salah satu dari respons berikut:

Cara yang Kotor

Pertama-tama kita akan melihat cara yang kotor untuk membaca dan menulis ke sebuah tabel Mnesia. Hali ini secara umum mesti dihindari karena tidak dijamin berhasil, tetapi ini dapat menolong kita mempelajari dan menjadi terbiasa bekerja dengan Mnesia. MAri tambahkan beberapa etri ke tabel Person kita.

iex> Mnesia.dirty_write({Person, 1, "Seymour Skinner", "Principal"})
:ok

iex> Mnesia.dirty_write({Person, 2, "Homer Simpson", "Safety Inspector"})
:ok

iex> Mnesia.dirty_write({Person, 3, "Moe Szyslak", "Bartender"})
:ok

…dan untuk mengambil entri-entri tersebut kita bisa gunakan Mnesia.dirty_read/1:

iex> Mnesia.dirty_read({Person, 1})
[{Person, 1, "Seymour Skinner", "Principal"}]

iex> Mnesia.dirty_read({Person, 2})
[{Person, 2, "Homer Simpson", "Safety Inspector"}]

iex> Mnesia.dirty_read({Person, 3})
[{Person, 3, "Moe Szyslak", "Bartender"}]

iex> Mnesia.dirty_read({Person, 4})
[]

Kalau kita coba melakukan query ke sebuah record yang tidak ada Mnesia akan merespon dengan sebuah list kosong.

Transaksi

Secara tradisional kita menggunakan transaksi untuk mengenkapsulasi pembacaan dan penulisan kita ke database. Transaksi adalah bagian yang penting dalam mendisain sistem sangat terdistribusi yang toleran terhadap kegagalan. Sebuah transaksi Mnesia adalah sebuah mekanisme yang melaluinya serangkaian operasi database bisa dieksekusi sebagai sebuah blok fungsional. Pertama-tama kita membuat sebuah fungsi anonim, dalam hal ini data_to_write dan kemudian memasukkannya ke Mnesia.transaction.

iex> data_to_write = fn ->
...>   Mnesia.write({Person, 4, "Marge Simpson", "home maker"})
...>   Mnesia.write({Person, 5, "Hans Moleman", "unknown"})
...>   Mnesia.write({Person, 6, "Monty Burns", "Businessman"})
...>   Mnesia.write({Person, 7, "Waylon Smithers", "Executive assistant"})
...> end
#Function<20.54118792/0 in :erl_eval.expr/5>

iex> Mnesia.transaction(data_to_write)
{:atomic, :ok}

Berdasarkan pesan transaksi ini, kita bisa dengan aman mengasumsikan bahwa kita telah menuliskan data ke tabel Person kita. Mari kita gunakan transaksi untuk membaca dari database untuk memastikan. Kita akan gunakan Mnesia.read/1 untuk membaca dari database, tetapi sekali lagi dari sebuah fungsi anonim.

iex> data_to_read = fn ->
...>   Mnesia.read({Person, 6})
...> end
#Function<20.54118792/0 in :erl_eval.expr/5>

iex> Mnesia.transaction(data_to_read)
{:atomic, [{Person, 6, "Monty Burns", "Businessman"}]}
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!