Seiring tumbuhnya TokoKita, data tidak lagi tinggal di satu tempat: pesanan berjalan di PostgreSQL, data historis menumpuk sebagai Parquet di object storage, dan sistem lama masih menyimpan master produk di MySQL. Analis ingin satu pertanyaan terjawab dengan satu SQL lintas ketiganya. Menyalin semuanya ke satu warehouse setiap kali butuh jawaban itu mahal; alternatifnya adalah query federation — meninggalkan data di tempatnya lalu mengirim query ke sana. Trino adalah engine distributed yang dibangun untuk pekerjaan ini, berawal dari proyek Presto di Facebook sebelum dikembangkan komunitas Trino.

Arsitektur: Coordinator dan Worker #

Trino berjalan sebagai klaster. Sebuah coordinator menerima SQL, mengurainya, menyusun rencana eksekusi, lalu membagi tahapan pekerjaan ke banyak worker; hasil antara dijahit kembali sebelum dikirim ke klien. Pola ini disebut MPP (massively parallel processing) dan dibedah lebih dalam di chapter Distributed Query Processing. Menariknya, meski pekerjaan tersebar, semua tahap tetap berjalan dalam memori worker — Trino tidak menumpahkan hasil antara ke disk, sehingga query yang hasil antaranya sangat besar bisa gagal kehabisan memori kecuali datanya difilter lebih dulu.

Worker tidak menyimpan data sendiri; setiap worker membaca dari sumber melalui connector sesuai rencana yang diberikan coordinator. Ketika satu worker gagal, coordinator menjadwalkan ulang tahap pekerjaannya ke worker lain. Dari sisi klien, seluruh klaster tampak seperti satu database SQL tunggal — klien Trino CLI, JDBC, atau ODBC cukup mengarah ke alamat coordinator di port 8080.

Katalog: Jendela ke Tiap Sumber #

Setiap sumber data didaftarkan sebagai catalog lewat berkas properties di direktori etc/catalog, sesuai panduan dokumentasi Trino . Satu catalog bernama tokokita yang menunjuk ke PostgreSQL misalnya ditulis demikian (format key=value standar Java properties):

text
# etc/catalog/tokokita.properties
connector.name=postgresql
connection-url=jdbc:postgresql://postgres:5432/tokokita
connection-user=analitik
connection-password=YOUR_DATABASE_PASSWORD

Nama berkas menjadi nama catalog, dan setiap connector membawa konfigurasi koneksinya sendiri. Daftar catalog yang aktif bisa dilihat dengan query berikut:

sql
-- Berjalan di Trino
SHOW CATALOGS;

Untuk menghidupkan Trino lokal, cara paling ringkas adalah container berikut (bagian ini divalidasi strukturnya dengan yaml.safe_load, bukan dijalankan — Trino tidak terpasang di lingkungan penulisan):

yaml
services:
  trino:
    image: trinodb/trino:latest
    ports:
      - "8080:8080"
    volumes:
      - ./etc/catalog:/usr/lib/trino/etc/catalog

Validasi struktur berkas di atas, plus parsing berkas katalog, dijalankan nyata di Python:

python
import yaml

docker_compose = """
services:
  trino:
    image: trinodb/trino:latest
    ports:
      - "8080:8080"
    volumes:
      - ./etc/catalog:/usr/lib/trino/etc/catalog
"""

hasil = yaml.safe_load(docker_compose)
print("services   :", sorted(hasil["services"]))
print("image trino:", hasil["services"]["trino"]["image"])
print("port trino :", hasil["services"]["trino"]["ports"][0])
print("volume     :", hasil["services"]["trino"]["volumes"][0])

katalog = """
connector.name=postgresql
connection-url=jdbc:postgresql://postgres:5432/tokokita
connection-user=analitik
connection-password=YOUR_DATABASE_PASSWORD
"""

kunci = {}
for baris in katalog.strip().splitlines():
    nama, nilai = baris.split("=", 1)
    kunci[nama.strip()] = nilai.strip()

print()
print("kunci terbaca :", list(kunci))
print("konektor      :", kunci["connector.name"])
print("kredensial    :", kunci["connection-password"] == "YOUR_DATABASE_PASSWORD")

Output yang Dihasilkan:

text
services   : ['trino']
image trino: trinodb/trino:latest
port trino : 8080:8080
volume     : ./etc/catalog:/usr/lib/trino/etc/catalog

kunci terbaca : ['connector.name', 'connection-url', 'connection-user', 'connection-password']
konektor      : postgresql
kredensial    : True

SQL Lintas Katalog #

Kekuatan utama Trino ada pada penamaan tiga tingkat catalog.schema.table. Satu query boleh merujuk tabel di katalog mana pun, dan coordinator mengurus pembacaan dari sumber yang berbeda:

sql
-- Berjalan di Trino: join PostgreSQL x MySQL dalam satu query
SELECT c.kota,
       date_trunc('month', p.waktu) AS bulan,
       count(*)                     AS jumlah_pesanan,
       sum(p.nilai)                 AS total_nilai
FROM postgres.public.pesanan p
JOIN mysql.tokokita.pelanggan c ON c.id = p.pelanggan_id
WHERE p.waktu >= DATE '2024-07-01'
GROUP BY c.kota, date_trunc('month', p.waktu)
ORDER BY c.kota, bulan;

Query di atas tidak dijalankan di lingkungan penulisan karena Trino tidak terpasang — makanya tidak ada blok output di sini. Perilakunya: connector membaca tabel dari kedua sumber, coordinator memilih urutan join, dan agregasi GROUP BY dipecah menjadi agregasi parsial di tiap worker lalu digabung di tahap akhir. Filter WHERE p.waktu >= ... didorong selengkap mungkin ke sumber data (predicate pushdown) agar data yang tidak relevan tidak pernah menyeberangi jaringan. Fungsi date_trunc('month', ...) membulatkan waktu ke awal bulan sehingga pesanan bisa dikelompokkan per bulan — padanan pola substr(tanggal, 1, 7) yang kita pakai di course Dasar SQL.

Trino juga bukan rumah untuk event berorientasi baris yang mengalir cepat dan harus terbaca dalam hitungan milidetik. Kebutuhan dashboard real-time semacam itu punya spesialis sendiri — kandidat berikutnya: ClickHouse.