"Hanya ada dua masalah sulit dalam Ilmu Komputer: pembatalan cache dan penamaan."
Phil Karlton
Apakah ada solusi atau metode umum untuk membuat cache tidak valid; tahu kapan entri sudah basi, jadi dijamin Anda akan selalu mendapatkan data terbaru?
Misalnya, pertimbangkan fungsi getData()
yang mendapatkan data dari file. Itu cache berdasarkan waktu modifikasi terakhir dari file, yang diperiksa setiap kali dipanggil.
Kemudian Anda menambahkan fungsi kedua transformData()
yang mengubah data, dan menyimpan hasilnya untuk kali berikutnya fungsi tersebut dipanggil. Itu tidak memiliki pengetahuan tentang file - bagaimana Anda menambahkan ketergantungan bahwa jika file diubah, cache ini menjadi tidak valid?
Anda dapat memanggil getData()
setiap kali transformData()
dipanggil dan membandingkannya dengan nilai yang digunakan untuk membuat cache, tetapi itu bisa menjadi sangat mahal.
"The two hardest things in Computer Science are cache invalidation, naming things, and off-by-one errors."
Jawaban:
Apa yang Anda bicarakan adalah rantai ketergantungan seumur hidup, bahwa satu hal bergantung pada hal lain yang dapat dimodifikasi di luar kendalinya.
Jika Anda memiliki fungsi idempoten dari
a
,b
kec
mana, jikaa
danb
sama makac
sama tetapi biaya pemeriksaannyab
tinggi maka Anda:b
b
secepat mungkinAnda tidak dapat memiliki kue dan memakannya ...
Jika Anda dapat melapisi cache tambahan berdasarkan di
a
atas maka ini mempengaruhi masalah awal tidak sedikit pun. Jika Anda memilih 1 maka Anda memiliki kebebasan apa pun yang Anda berikan pada diri Anda sendiri dan dengan demikian dapat menyimpan lebih banyak, tetapi harus ingat untuk mempertimbangkan validitas nilai yang di-cacheb
. Jika Anda memilih 2, Anda masih harus memeriksab
setiap saat tetapi dapat kembali ke cachea
jikab
check out.Jika Anda melapisi cache, Anda harus mempertimbangkan apakah Anda telah melanggar 'aturan' sistem sebagai akibat dari perilaku gabungan.
Jika Anda tahu bahwa
a
selalu memiliki validitas jikab
demikian maka Anda dapat mengatur cache Anda seperti itu (pseudocode):Jelas layering berturut-turut (katakanlah
x
) adalah sepele selama, pada setiap tahap validitas input baru ditambahkan sesuai dengana
:b
hubungan untukx
:b
danx
:a
.Namun sangat mungkin bahwa Anda bisa mendapatkan tiga masukan yang validitasnya sepenuhnya independen (atau siklik), jadi tidak ada pelapisan yang mungkin dilakukan. Ini berarti garis bertanda // penting harus diubah menjadi
sumber
Masalah dalam pembatalan cache adalah hal-hal berubah tanpa kita menyadarinya. Jadi, dalam beberapa kasus, solusi dimungkinkan jika ada hal lain yang mengetahui tentang hal itu dan dapat memberi tahu kami. Dalam contoh yang diberikan, fungsi getData dapat menghubungkan ke sistem file, yang mengetahui tentang semua perubahan pada file, terlepas dari proses apa yang mengubah file, dan komponen ini pada gilirannya dapat memberi tahu komponen yang mengubah data.
Saya tidak berpikir ada perbaikan ajaib umum untuk membuat masalah hilang. Tetapi dalam banyak kasus praktis mungkin ada peluang untuk mengubah pendekatan berbasis "pemungutan suara" menjadi pendekatan berbasis "interupsi", yang dapat membuat masalah hilang begitu saja.
sumber
Jika Anda akan getData () setiap kali Anda melakukan transformasi, Anda telah menghilangkan seluruh manfaat cache.
Untuk contoh Anda, sepertinya solusinya adalah ketika Anda menghasilkan data yang diubah, untuk juga menyimpan nama file dan waktu modifikasi terakhir dari file yang menghasilkan data (Anda sudah menyimpan ini dalam struktur data apa pun yang dikembalikan oleh getData ( ), jadi Anda cukup menyalin record itu ke dalam struktur data yang dikembalikan oleh transformData ()) lalu saat Anda memanggil transformData () lagi, periksa waktu terakhir file yang diubah.
sumber
IMHO, Pemrograman Reaktif Fungsional (FRP) dalam arti tertentu adalah cara umum untuk mengatasi pembatalan cache.
Inilah alasannya: data basi dalam terminologi FRP disebut glitch . Salah satu tujuan FRP adalah untuk menjamin tidak adanya gangguan.
FRP dijelaskan lebih rinci dalam pembicaraan 'Esensi FRP' ini dan dalam jawaban SO ini .
Dalam pembicaraan itu
Cell
s mewakili Objek / Entitas yang di-cache dan di-Cell
refresh jika salah satu dependensinya di-refresh.FRP menyembunyikan kode pipa yang terkait dengan grafik ketergantungan dan memastikan bahwa tidak ada yang basi
Cell
.Cara lain (berbeda dari FRP) yang dapat saya pikirkan adalah membungkus nilai yang dihitung (tipe
b
) menjadi semacam penulis Monad diWriter (Set (uuid)) b
manaSet (uuid)
(notasi Haskell) berisi semua pengidentifikasi dari nilai-nilai yang dapat berubah di mana nilai yang dihitungb
bergantung. Jadi,uuid
adalah semacam pengenal unik yang mengidentifikasi nilai / variabel yang bisa berubah (katakanlah baris dalam database) tempatb
bergantung yang dihitung .Gabungkan ide ini dengan kombinator yang beroperasi pada jenis penulis Monad ini dan itu mungkin mengarah pada semacam solusi pembatalan cache umum jika Anda hanya menggunakan kombinator ini untuk menghitung yang baru
b
. Kombinator seperti itu (katakanlah versi khusus darifilter
) mengambil monad dan(uuid, a)
-s Writer sebagai input, di manaa
data / variabel yang bisa berubah, diidentifikasi olehuuid
.Jadi, setiap kali Anda mengubah data "asli"
(uuid, a)
(misalnya data yang dinormalisasi dalam database yangb
dihitung) yang menjadi dasar penghitungan nilai jenis,b
Anda dapat membatalkan cache yang berisib
jika Anda mengubah nilai apa puna
yang menjadi tempatb
bergantung nilai yang dihitung. , karena berdasarkan padaSet (uuid)
Penulis Monad Anda dapat mengetahui kapan hal ini terjadi.Jadi setiap kali Anda memutasi sesuatu dengan yang diberikan
uuid
, Anda menyiarkan mutasi ini ke semua cache dan mereka membatalkan nilaib
yang bergantung pada nilai yang bisa berubah yang diidentifikasi dengan katauuid
karena monad Writer di mana yangb
dibungkus dapat mengetahui apakah itub
tergantung pada katauuid
atau tidak.Tentu saja, ini hanya bermanfaat jika Anda membaca lebih sering daripada menulis.
Pendekatan ketiga, praktis, adalah dengan menggunakan tampilan terwujud dalam database dan menggunakannya sebagai cache. AFAIK mereka juga bertujuan untuk menyelesaikan masalah pembatalan. Ini tentu saja membatasi operasi yang menghubungkan data yang bisa berubah ke data yang diturunkan.
sumber
Saya sedang mengerjakan pendekatan sekarang berdasarkan fungsi PostSharp dan memoizing . Saya telah menjalankannya melewati mentor saya, dan dia setuju bahwa ini adalah implementasi cache yang baik dengan cara konten-agnostik.
Setiap fungsi dapat ditandai dengan atribut yang menentukan periode kedaluwarsanya. Setiap fungsi yang ditandai dengan cara ini akan dimo dan hasilnya disimpan ke dalam cache, dengan hash dari pemanggilan fungsi dan parameter yang digunakan sebagai kuncinya. Saya menggunakan Velocity untuk backend, yang menangani distribusi data cache.
sumber
Tidak, karena semua data berbeda. Beberapa data mungkin "basi" setelah satu menit, beberapa setelah satu jam, dan beberapa mungkin baik-baik saja selama berhari-hari atau berbulan-bulan.
Mengenai contoh spesifik Anda, solusi paling sederhana adalah memiliki fungsi 'pemeriksaan cache' untuk file, yang Anda panggil dari keduanya
getData
dantransformData
.sumber
Tidak ada solusi umum tetapi:
Anda cache dapat bertindak sebagai proxy (tarik). Asumsikan cache Anda mengetahui stempel waktu perubahan asal terakhir, ketika seseorang memanggil
getData()
, cache menanyakan asal stempel waktu perubahan terakhirnya, jika sama, ia mengembalikan cache, jika tidak ia memperbarui isinya dengan yang sumber dan mengembalikan isinya. (Variasi adalah klien untuk langsung mengirim stempel waktu pada permintaan, sumber hanya akan mengembalikan konten jika stempel waktunya berbeda.)Anda masih dapat menggunakan proses pemberitahuan (push), cache mengamati sumber, jika sumber berubah, ia mengirimkan pemberitahuan ke cache yang kemudian ditandai sebagai "kotor". Jika seseorang memanggil
getData()
cache pertama-tama akan diperbarui ke sumbernya, hapus tanda "kotor"; lalu kembalikan isinya.Pilihan secara umum tergantung pada:
getData()
akan memilih push, jadi untuk menghindari sumber dibanjiri oleh fungsi getTimestampCatatan: Karena menggunakan stempel waktu adalah cara tradisional proxy http bekerja, pendekatan lain adalah membagikan hash konten yang disimpan. Satu-satunya cara yang saya tahu untuk 2 entitas untuk mendapatkan pembaruan bersama adalah saya menelepon Anda (tarik) atau Anda menelepon saya… (dorong) itu saja.
sumber
cache sulit karena Anda perlu mempertimbangkan: 1) cache adalah beberapa node, perlu konsensus untuk mereka 2) waktu pembatalan 3) kondisi balapan ketika multple get / set terjadi
ini bacaan yang bagus: https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/
sumber
Mungkin algoritme yang tidak menyadari cache akan menjadi yang paling umum (Atau setidaknya, lebih sedikit bergantung pada konfigurasi perangkat keras), karena mereka akan menggunakan cache tercepat terlebih dahulu dan melanjutkan dari sana. Berikut adalah kuliah MIT tentang itu: Cache Oblivious Algorithms
sumber