Memilih arsitektur search engine pada aplikasi Django berpusat pada trade-off antara kesederhanaan operasional single-database dan performa dedicated search engine. PostgreSQL Full-Text Search (FTS) memanfaatkan fungsionalitas bawaan database relasional melalui django.contrib.postgres.search. Meilisearch memisahkan beban komputasi pencarian ke dedicated external service melalui protokol REST. Keputusan ini menentukan alokasi sumber daya CPU/IO, konsistensi data, dan arsitektur deployment aplikasi.
Arsitektur Direct Query vs Event-Driven Sync
Postgres FTS bekerja langsung pada primary database atau read-replica menggunakan SQL query biasa. Tidak ada komponen middleware tambahan, tidak ada transport layer network baru, dan data bersifat strongly consistent (ACID). Begitu transaksi INSERT atau UPDATE selesai, data langsung terbaca oleh query search berikutnya.
Meilisearch menerapkan model event-driven synchronization. Aplikasi Django tidak bisa langsung mengandalkan query database. Pola standarnya menggunakan Django model signals yang memicu task Celery untuk mengirimkan dokumen JSON ke API Meilisearch secara asynchronous. Pendekatan ini memunculkan eventual consistency lag: terdapat jeda waktu antara commit data ke database dan tersedianya data tersebut pada hasil pencarian Meilisearch.
Beban I/O, CPU, dan Performa Latensi Query
1. PostgreSQL FTS (Native)
Postgres FTS memproses teks mentah menjadi token tsvector dan membandingkannya dengan tsquery menggunakan kamus bahasa tertentu (stemming dan stop words). Untuk mencegah pembacaan sekuensial (full table scan), index GIN (Generalized Inverted Index) wajib digunakan.
- Beban CPU: Operasi stemming dan parsing teks pada runtime membutuhkan siklus CPU signifikan jika dilakukan on-the-fly. Solusi standarnya adalah menyimpan pre-computed vector pada kolom
SearchVectorField. Perhitungan ranking (ts_rankatauts_rank_cd) tetap membebani CPU saat query dieksekusi. - Beban I/O: Index GIN berukuran relatif besar. Jika ukuran index melampaui alokasi
shared_buffers, Postgres akan membaca disk I/O secara intensif, yang dapat mendegradasi performa query transaksional (OLTP) lainnya. - Typo Tolerance: Postgres tidak mendukung typo tolerance bawaan di modul FTS standarnya. Untuk mengimplementasikannya, Anda harus menggabungkan ekstensi
pg_trgm(trigram matching). Pendekatan ini membuat indeks membengkak secara eksponensial dan memperlambat waktu respons query di atas dataset ratusan ribu baris.
2. Meilisearch (Dedicated Engine)
Meilisearch dirancang spesifik untuk responsivitas instan (search-as-you-type) menggunakan struktur data memory-mapped (LMDB).
- Isolasi Beban: Beban komputasi tokenisasi, ranking, dan disk traversal sepenuhnya berpindah ke instance Meilisearch. Primary database terbebas dari load spikes akibat query pencarian kompleks.
- Typo Tolerance & Latensi: Algoritma berbasis Damerau-Levenshtein distance bawaan mengeksekusi pencarian dengan toleransi salah ketik pada tingkat latensi sub-50 milidetik tanpa konfigurasi indeks manual yang rumit.
Implementasi Teknis pada Django
Pola 1: PostgreSQL FTS Teroptimasi
Pola optimal pada Postgres menghindari komputasi SearchVector saat query. Buat kolom terdedikasi yang diupdate via trigger database atau method save() model, lalu pasang GIN index.
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField, SearchQuery, SearchRank
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
search_vector = SearchVectorField(null=True, blank=True)
class Meta:
indexes = [
GinIndex(fields=["search_vector"]),
]
# Query implementation
@classmethod
def search(cls, term: str):
query = SearchQuery(term, search_type="websearch", config="indonesian")
return (
cls.objects.filter(search_vector=query)
.annotate(rank=SearchRank(models.F("search_vector"), query))
.order_by("-rank")
)
Pola 2: Pipeline Asynchronous Meilisearch
Pola integrasi Meilisearch membutuhkan isolasi proses melalui worker asynchronous untuk menjaga respons HTTP Django tetap cepat dan mencegah race condition sebelum transaksi database selesai di-commit.
# signals.py
from django.db import transaction
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from .models import Product
from .tasks import index_product_task, delete_product_task
@receiver(post_save, sender=Product)
def on_product_save(sender, instance, **kwargs):
# Gunakan transaction.on_commit agar worker tidak membaca uncommitted read
transaction.on_commit(
lambda: index_product_task.delay(instance.id)
)
@receiver(post_delete, sender=Product)
def on_product_delete(sender, instance, **kwargs):
transaction.on_commit(
lambda: delete_product_task.delay(instance.id)
)
# tasks.py
import meilisearch
from celery import shared_task
from django.conf import settings
from .models import Product
client = meilisearch.Client(settings.MEILISEARCH_URL, settings.MEILISEARCH_API_KEY)
index = client.index("products")
@shared_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=3)
def index_product_task(product_id: int):
product = Product.objects.get(id=product_id)
index.add_documents([{
"id": product.id,
"title": product.title,
"description": product.description,
}])
@shared_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=3)
def delete_product_task(product_id: int):
index.delete_document(str(product_id))
Sinkronisasi Data dan Failure Modes
Postgres FTS tidak memiliki failure mode sinkronisasi data karena data search tersimpan di tabel yang sama atau terikat langsung dengan foreign key. ACID menjamin konsistensi mutlak.
Arsitektur Meilisearch menghadapi masalah sinkronisasi klasik sistem terdistribusi:
- Celery Task Failure: Jaringan terputus atau Meilisearch crash saat indexing menyebabkan data di engine tertinggal. Task harus menerapkan idempotent retry dan exponential backoff.
- Order of Execution: Jika terjadi update berturut-turut pada ID yang sama, task Celery yang selesai tidak berurutan berpotensi menimpa data baru dengan data lama. Penanganannya membutuhkan tracking versi dokumen atau timestamp modifikasi pada payload.
- Data Drift: Kesalahan unhandled exception pada worker akan membuat database dan Meilisearch desync. Diperlukan cron job rekonsiliasi (batch reindexing periodik) untuk memvalidasi ulang seluruh data index secara berkala.
Total Cost of Ownership (TCO) dan Operasional
PostgreSQL FTS memiliki TCO operasional rendah. Tim engineering tidak perlu memantau node baru, tidak memerlukan pipeline backup terpisah, dan tidak ada dependensi network tambahan antar instance. Monitoring resource terpusat pada metric PostgreSQL yang sudah ada.
Meilisearch menambahkan kompleksitas infrastruktur:
- Membutuhkan dedicated compute instance dengan alokasi RAM yang cukup untuk memuat working set index ke memori.
- Membutuhkan komponen message broker (seperti Redis atau RabbitMQ) dan worker process (Celery) yang harus dipantau availability-nya.
- Membutuhkan strategi disaster recovery terpisah: backup snapshot Meilisearch secara rutin di luar WAL archive PostgreSQL.
Kriteria Pengambilan Keputusan
| Parameter | Gunakan Postgres FTS | Gunakan Meilisearch |
|---|---|---|
| Volume Data | < 500.000 records | > 500.000 records |
| Throughput Search | < 50 QPS | > 50 QPS (skala ratusan hingga ribuan QPS) |
| Typo Tolerance | Tidak krusial atau cukup matching standar | Wajib ada (fitur e-commerce, UX search-first) |
| Search-as-you-type | Tidak praktis (beban IO tinggi per keystroke) | Fitur bawaan, latensi sangat rendah (< 20ms) |
| Kapasitas Operasional | Tim kecil / DevOps terbatas | Tersedia kapabilitas memelihara cluster & worker |
| Konsistensi Data | Harus real-time dan strictly consistent | Dapat mentolerir eventual consistency (100ms - bbrp detik) |
Gunakan PostgreSQL FTS jika prioritas utama adalah kesederhanaan deployment, konsistensi data transaksional yang kaku, dan beban pencarian tidak mendominasi utilisasi database. Beralihlah ke Meilisearch ketika aplikasi membutuhkan fitur search-as-you-type yang instan, toleransi salah ketik yang granular, atau ketika profiling database menunjukkan degradasi metrik read/write IOPS akibat index GIN yang membengkak.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!