tutorial

78 Caching Query dan Result Resolver

78 Caching Query dan Result Resolver: Efisiensi, Best Practice, dan Implementasi Nyata

Oleh: [Nama Anda], Senior Software Engineer


Caching merupakan salah satu strategi paling efektif untuk meningkatkan performa sistem, terutama di dunia yang serba real-time dan high traffic seperti sekarang. Dalam artikel ini, saya akan membahas secara detail tentang query caching dan result resolver caching, membedakan keduanya, kapan dan bagaimana mengimplementasikan, serta membagikan best practice berdasarkan pengalaman di beberapa proyek scale-up. Saya juga akan menyertakan contoh kode, simulasi nyata, serta visualisasi proses menggunakan diagram alur mermaid.


Memahami Dasar: Query Caching vs Result Resolver

Sebelum masuk ke kode dan simulasi, penting sekali untuk memahami perbedaannya:

1. Query Caching

Query caching menyimpan hasil eksekusi query tertentu terhadap data source (biasanya database). Jadi, jika query yang identik dijalankan kembali, maka hasil cache dikembalikan tanpa perlu akses ke database.

2. Result Resolver Caching

Result resolver caching biasanya digunakan pada layer business logic atau data fetching framework (seperti GraphQL atau DataLoader). Resolver caching mengoptimalkan pemanggilan data antar komponen atau field sehingga menghindari pemanggilan sumber data yang berulang untuk entitas yang sama.


Manfaat Kedua Caching Ini

Query CachingResult Resolver Caching
KeuntunganMengurangi beban DB, response time cepatMengurangi duplikasi fetch pada data yang sama
SkalaSering dipakai di level DB/serverFramework/Business Logic Layer
KelemahanRentan data stale jika table berubahPerlu manajemen dependencies antar field

Ilustrasi Kasus: Sistem Article Recommendation

Bayangkan sistem article recommendations dengan query seperti:

SELECT * FROM articles WHERE published = true ORDER BY recommended_score DESC LIMIT 10;

Atau pada GraphQL:

{
  articles(recommended: true) {
    id
    title
    author {
      id
      name
    }
  }
}

Di sini:

  • Query caching akan menyimpan hasil seluruh list artikel.
  • Result resolver caching akan berperan saat field author dipanggil berulang.

Simulasi Tanpa Caching: Bottleneck si Author

Misal, kita fetch 10 artikel rekomendasi. Setiap artikel akan fetch data author. Tanpa cache resolver, akan ada 10x query ke table authors.

Pseudo-code ilustrasi (tanpa cache):

const articles = await db.query('SELECT * FROM articles WHERE ...');
const results = [];
for (const article of articles) {
  const author = await db.query('SELECT * FROM authors WHERE id = ?', [article.author_id]);
  results.push({ ...article, author });
}
// Total = 1 (artikel) + 10 (author) = 11 query

Simulasi Dengan Result Resolver Caching

Dengan result resolver caching (misal pakai DataLoader), sistem akan mengumpulkan semua ID unik, lalu fetch author cukup sekali.

Pseudo-code dengan cache resolver:

const articles = await db.query('SELECT * FROM articles WHERE ...');
const authorIds = articles.map(a => a.author_id);
const authors = await loadAuthorsByIds(authorIds); // batch
const results = articles.map(article => ({
  ...article,
  author: authors[article.author_id],
}));
// Total query: 1 (artikel) + 1 (semua author unik)

Simulasi Query Caching di Database

Bagaimana jika query artikel rekomendasi yang berat ini sering dipanggil (misal setiap user visit homepage)? Aktifkan query caching.

Diagram alur Query Caching (Mermaid):

flowchart LR
  A[Client Request] --> B[Check Cache Layer]
  B -->|Cache Hit| C[Return Cached Result]
  B -->|Cache Miss| D[Process to DB]
  D --> E[Store in Cache]
  E --> C

Implementasi: Query Caching (Node.js + Redis)

Misal, stack yang digunakan adalah Express, PostgreSQL, dan Redis sebagai cache.

Middleware caching sederhana:

const redis = require('redis').createClient();

async function cacheMiddleware(req, res, next) {
  const cacheKey = "homepage:articles";
  const cachedResult = await redis.get(cacheKey);
  if (cachedResult) {
    return res.json(JSON.parse(cachedResult));
  }
  // Simpan ke res.locals supaya handler bawahnya bisa pakai
  res.locals.cacheKey = cacheKey;
  next();
}

// Handler utama
app.get('/api/homepage', cacheMiddleware, async (req, res) => {
  const articles = await db.query("SELECT ..."); // Query berat
  await redis.set(res.locals.cacheKey, JSON.stringify(articles), 'EX', 60); // TTL 1 menit
  return res.json(articles);
});

Kelebihan:

  • Hanya satu query berat setiap 1 menit/cache TTL.
  • Sangat mengurangi load ke database.

Implementasi: Result Resolver Caching (Node.js + DataLoader)

DataLoader sangat populer untuk GraphQL dan REST mediation.

const DataLoader = require('dataloader');

const authorLoader = new DataLoader(async (authorIds) => {
  const rows = await db.query('SELECT * FROM authors WHERE id = ANY($1)', [authorIds]);
  // Buat map id => author
  const map = new Map(rows.map(row => [row.id, row]));
  return authorIds.map(id => map.get(id));
});

// Dalam resolver/handler
const articleIds = [...]; // hasil query artikel
const authors = await authorLoader.loadMany(articleIds);

Best Scenario:

  1. Request masuk, fetch semua article.
  2. DataLoader kumpulkan semua unique author_id.
  3. Query hanya 1X ke authors.
  4. Hasil di-cache (per request - jika ingin global bisa pakai redis layer lagi).

Tabel: Perbandingan Performa Query

SkemaJumlah QueryLatency (ms, simulasi)Keterangan
Tanpa Caching11500Fetch author N times
Query Caching Saja150Semua cached
Resolver Caching Saja21201x artikel, 1x author
Keduanya150Cache total jalur

Studi Kasus: Cache Invalidation

Bagian tersulit dari caching adalah invalidation. Ketika ada artikel baru, harus memastikan cache query dan resolver tidak mengandung data stale.

Cara sederhana:

  • Untuk query cache, pakai TTL pendek (misal 1 menit).
  • Untuk result resolver, invalidate/clear cache ketika data subject (misal author) berubah.

Tips dari lapangan:

  • Untuk cache resolver yang pendek (per-request) tidak perlu invalidasi manual.
  • Untuk query cache yang lebih besar, selalu seimbangkan antara performance vs data freshness.

Kesimpulan

Caching query dan result resolver adalah senjata rahasia dalam development aplikasi data-driven modern. Dengan penerapan dua lapis cache ini, Anda bisa menekan latency dari ratusan milidetik ke puluhan milidetik — bahkan under 10ms untuk repeated request — tanpa membebani database.

Checklist best practice:

  • Aktifkan query cache untuk query berat dan sering diulang.
  • Implementasikan resolver caching untuk field atau entitas yang sering berulang pada batch.
  • Selalu siapkan cache invalidation atau TTL yang masuk akal.
  • Monitor cache hit-rate dan latency secara real-time.

Struktur caching yang cermat akan membawa sistem Anda mendekati 99% reliability dan 10x lebih hemat resource dibanding brute-force querying.


Mau belajar lebih?
Lihat kode sumber dan diskusi lebih lanjut di Github atau DM saya di Twitter untuk konsultasi arsitektur caching di sistem Anda.


Terima kasih sudah membaca!


Referensi


Happy caching, engineers! 🚀

comments powered by Disqus