Cache
Modul Cache NocoBase berbasis pada node-cache-manager, menyediakan fungsionalitas cache untuk pengembangan plugin. Tersedia dua jenis cache bawaan:
- memory — Cache memori berbasis lru-cache, disediakan secara default oleh node-cache-manager
- redis — Cache Redis berbasis node-cache-manager-redis-yet
Lebih banyak jenis cache dapat didaftarkan melalui ekstensi API.
Penggunaan Dasar
app.cache
app.cache adalah instance cache default level aplikasi, dapat langsung digunakan.
// Set cache
await app.cache.set('key', 'value', { ttl: 3600 }); // Satuan TTL: detik
// Get cache
const value = await app.cache.get('key');
// Delete cache
await this.app.cache.del('key');
ctx.cache
Pada middleware atau operasi resource, dapat mengakses cache melalui ctx.cache.
async (ctx, next) => {
let data = await ctx.cache.get('custom:data');
if (!data) {
// Cache miss, ambil dari database
data = await this.getDataFromDatabase();
// Simpan ke cache, masa berlaku 1 jam
await ctx.cache.set('custom:data', data, { ttl: 3600 });
}
await next();
}
Membuat Cache Kustom
Jika perlu membuat instance cache independen (misalnya namespace atau konfigurasi yang berbeda), dapat menggunakan method app.cacheManager.createCache().
import { Plugin } from '@nocobase/server';
export default class PluginCacheDemo extends Plugin {
async load() {
// Membuat instance cache dengan prefix
const myCache = await this.app.cacheManager.createCache({
name: 'myPlugin',
prefix: 'plugin:cache:', // Semua key akan otomatis ditambahkan prefix ini
store: 'memory', // Menggunakan cache memori, opsional, default menggunakan defaultStore
max: 1000, // Jumlah maksimum item cache
});
await myCache.set('user:1', { name: 'John' });
const user = await myCache.get('user:1');
}
}
Penjelasan Parameter createCache
Mendapatkan Cache yang Sudah Dibuat
const myCache = this.app.cacheManager.getCache('myPlugin');
Method Dasar Cache
Instance Cache menyediakan method operasi cache umum, sebagian besar diwarisi dari node-cache-manager.
get / set
// Set cache, dengan waktu kadaluarsa (satuan: detik)
await cache.set('key', 'value', { ttl: 3600 });
// Get cache
const value = await cache.get('key');
del / reset
// Delete satu key
await cache.del('key');
// Bersihkan semua cache
await cache.reset();
wrap
wrap() akan terlebih dahulu mencoba mengambil data dari cache, jika cache miss, akan mengeksekusi callback function dan menyimpan hasilnya ke cache.
const data = await cache.wrap('user:1', async () => {
// Function ini hanya dieksekusi saat cache miss
return await this.fetchUserFromDatabase(1);
}, { ttl: 3600 });
Operasi Batch
// Batch set
await cache.mset([
['key1', 'value1'],
['key2', 'value2'],
['key3', 'value3'],
], { ttl: 3600 });
// Batch get
const values = await cache.mget(['key1', 'key2', 'key3']);
// Batch delete
await cache.mdel(['key1', 'key2', 'key3']);
keys / ttl
// Mendapatkan semua key (perhatian: sebagian store mungkin tidak mendukung)
const allKeys = await cache.keys();
// Mendapatkan sisa waktu kadaluarsa key (satuan: detik)
const remainingTTL = await cache.ttl('key');
Penggunaan Lanjutan
wrapWithCondition
wrapWithCondition() mirip dengan wrap(), tetapi dapat menentukan apakah menggunakan cache melalui kondisi.
const data = await cache.wrapWithCondition(
'user:1',
async () => {
return await this.fetchUserFromDatabase(1);
},
{
// Parameter eksternal mengontrol apakah menggunakan hasil cache
useCache: true, // Saat diset false, function akan dieksekusi ulang meskipun ada cache
// Tentukan apakah cache berdasarkan hasil data
isCacheable: (value) => {
// Misalnya: hanya hasil yang berhasil yang di-cache
return value && !value.error;
},
ttl: 3600,
},
);
Operasi Cache Objek
Ketika konten yang di-cache adalah objek, dapat menggunakan method berikut untuk langsung mengoperasikan property objek, tanpa perlu mengambil seluruh objek.
// Set property objek
await cache.setValueInObject('user:1', 'name', 'John');
await cache.setValueInObject('user:1', 'age', 30);
// Get property objek
const name = await cache.getValueInObject('user:1', 'name');
// Delete property objek
await cache.delValueInObject('user:1', 'age');
Mendaftarkan Store Kustom
Jika perlu menggunakan jenis cache lain (seperti Memcached, MongoDB, dll.), dapat mendaftarkan melalui app.cacheManager.registerStore().
import { Plugin } from '@nocobase/server';
import { redisStore, RedisStore } from 'cache-manager-redis-yet';
export default class PluginCacheDemo extends Plugin {
async load() {
// Mendaftarkan Redis store (jika belum terdaftar)
this.app.cacheManager.registerStore({
name: 'redis',
store: redisStore,
close: async (redis: RedisStore) => {
await redis.client.quit();
},
// Konfigurasi koneksi Redis
url: 'redis://localhost:6379',
});
// Membuat cache menggunakan store yang baru terdaftar
const redisCache = await this.app.createCache({
name: 'redisCache',
store: 'redis',
prefix: 'app:',
});
}
}
Perhatian
- Batasan Cache Memori: Saat menggunakan memory store, perhatikan untuk mengatur parameter
max yang wajar, untuk menghindari memory overflow.
- Strategi Pengosongan Cache: Saat memperbarui data ingatlah untuk membersihkan cache terkait, untuk menghindari data kotor.
- Konvensi Penamaan Key: Disarankan menggunakan namespace dan prefix yang bermakna, seperti
module:resource:id.
- Pengaturan TTL: Atur TTL secara wajar berdasarkan frekuensi update data, untuk menyeimbangkan performa dan konsistensi.
- Koneksi Redis: Saat menggunakan Redis, pastikan parameter koneksi dan password dikonfigurasi dengan benar di environment production.
Tautan Terkait
- Context — Mengakses cache melalui
ctx.cache di middleware dan Action
- Plugin — Membuat dan mengelola instance cache kustom dalam plugin
- Ikhtisar Pengembangan Server — Arsitektur server menyeluruh dan posisi modul cache
- Middleware — Menggabungkan cache dengan middleware untuk menangani logika request
- Database Operasi Database — Cache sering digunakan bersama dengan query database untuk meningkatkan performa