Implementasi temporary storage menggunakan Redis

Redis adalah sebuah database NoSQL dimana relasi data dieskpresikan dalam bentuk key/value. Dalam kata lain, kita tidak akan menemukan konsep tabel, primary/foreign key, atau bahkan database di Redis.

Karena sifatnya yang berupa KV Store, hal tersebut menjadikan Redis sangat cocok digunakan untuk menyimpan data-data yang bersifat sementara. Beberapa tipe data yang sering disimpan dalam Redis adalah:

  1. Session user yang telah login
  2. File yang di-generate on-demand dan di-keep untuk beberapa waktu
  3. Data yang sifatnya mahal/resource-expensive untuk di-generate

Katakanlah kita memiliki dua kegunaan untuk Redis, saat ini:

  1. Menyimpan data hotel ke Redis untuk kemudian diproses oleh Thread
  2. Menyimpan data transaksi incomplete, yang dapat dilanjutkan ke langkah selanjutnya oleh customer

Kita perlu melakukan suatu engineering yang memungkinkan modul yang akan kita buat dapat dipergunakan dengan mudah oleh kedua kegunaan tersebut secara fleksibel.

Langkah awal: pembuatan module TempStorage

Langkah awalnya, kita membuat module TempStorage dengan fungsi-fungsi sederhana sebagai berikut:

module TempStorage  
  extend self

  def save(object)
  end

  def fetch(key)
  end

  def destroy(key)
  end
end  

Agar mencapai titik dimana module bisa digunakan secara fleksibel, maka urusan serialisasi dan de-serialisasi data saat masuk/keluar ke dalam Redis, perlu dilakukan/diimplementasi di luar TempStorage. Dalam arti lain, kontrak yang dibutuhkan oleh module pemakai TempStorage adalah adanya fungsi serialize_data dan deserialize_data:

# di module lain haru mengimplementasi:
def deserialize_data(raw_value)  
end

def serialize_data(key, object)  
end  

Saat melakukan serialisasi, kemungkinan nilai key akan dibutuhkan dalam prosesnya (meskipun ini bersifat kadang-kadang). Oleh karenanya, fungsi serialize_data menerima dua parameter: key dan object.

Dengan kontrak tersebut, kita bisa mendefinisikan fungsi save:

  def save(object)
    key = SecureRandom.uuid
    data = serialize_data(key, object)
    Red.is.set(key, data)
    key
  end

Demikian pula, fungsi fetch dapat di definisikan sebagai berikut:

  def fetch(key)
    raw_val = Red.is.get(key)
    val = deserialize_data(raw_val)
    val
  end

Fungsi destroy:

  def destroy(key)
    Red.is.del(key)
  end

Penggunaan TempStorage

Merujuk pada kedua kasus penggunaan diatas, maka kita dapat membuat dua module:

  1. HotelInserterStorage
  2. TransactionStorage

Pada HotelInserterStorage kita menggunakan MsgPack untuk melakukan serialisasi dan de-serialisasi:

module HotelInserterStorage  
  include TempStorage
  extend self

  def deserialize_data(raw_val)
    MessagePack.unpack(raw_val)
  end

  def serialize_data(key, hotel_hash_data)
    hotel_hash_data.to_msgpack
  end
end  

Pada TransactionStorage kita akan melakukan pre-processing sebelum melakukan serialisasi, juga, data akan disimpan dalam bentuk JSON daripada (semi)binary.

module TransactionStorage  
  include TempStorage
  extend self

  def deserialize_data(raw_val)
    Oj.load(raw_val)
  end

  def serialize_data(key, object)
    hash = object.to_h
    hash[:key] = key
    Oj.dump(data, mode: :compat)
  end
end  

Dengan kedua contoh diatas, maka design decision kita telah terpenuhi, bahwasanya TempStorage harus bersifat re-usable oleh module-module yang akan menggunakannya.

TempStorage tidak menerima asumsi apapun tentang bagaimana struktur data yang akan disimpan, ataupun bagaimana data harus diproses setelah dipanggil dari Redis.

Fungsi untuk penghapusan data yang lebih mudah

Fungsi tambahan lain yang mungkin akan sangat membantu adalah membolehkan data yang di-extract untuk di-delete dengan lebih mudah:

trans = TransactionStorage.fetch("my-data-id")  
trans.destroy_temp_storage!  

Untuk membuat destroy_temp_storage! maka kita harus mendefinisikan fungsi tersebut on-the-fly saat suatu object selesai di-deserialisasi:

  def fetch(key)
    # ... kode lain di atas
    val.instance_eval do
      @__temp_storage_key = key
      def destroy_temp_storage!
        TempStorage.destroy @__temp_storage_key
      end
    end

    val
  end

Bottom line

Redis sebagai suatu mekanisme penyimpanan NoSQL kategori key/value store, sangat sering digunakan untuk menyimpan data-data yang bersifat sementara/ephemeral. Dengan menerapkan teknik dalam artikel ini, pengguna dapat mencapai high-reusability untuk kegunaan yang dimaksud, dengan kode yang lebih singkat, sehingga codebase akan terasa lebih DRY (Don't Repeat Yourself).

Pada akhirnya, kode TempStorage kita akan menjadi seperti berikut:

module TempStorage  
  extend self

  def save(object)
    key = SecureRandom.uuid
    data = serialize_data(key, object)
    Red.is.set(key, data)
    key
  end

  def fetch(key)
    raw_val = Red.is.get(key)
    val = deserialize_data(raw_val)

    val.instance_eval do
      @__temp_storage_key = key
      def destroy_temp_storage!
        TempStorage.destroy @__temp_storage_key
      end
    end

    val
  end

  def destroy(key)
    Red.is.del(key)
  end
end  

Artikel - Artikel Terkait