← Semua pembelajaran / Blockchain Nol → RWA
Fase 3 · Integrasi Backend Java & PHP

Event listening & indexing ke MySQL

Aplikasi produksi hampir tidak pernah membaca riwayat transaksi langsung dari node saat pengguna membuka halaman. Pola standarnya: proses latar belakang mendengarkan event, mencatatnya ke database relasional, dan halaman aplikasi cukup query SQL biasa.

Sumber asli docs.web3j.io Resmi Rangkuman ~8 menit baca

Intisari

  • Event log tidak tersimpan di storage kontrak (Fase 1) — mengambilnya lagi harus lewat eth_getLogs, yang lambat untuk query berulang dari banyak pengguna sekaligus.
  • Pola standarnya: proses indexer terpisah mendengarkan event baru, menyimpannya ke tabel MySQL lokal — aplikasi utama cukup SELECT dari tabel itu, bukan bertanya ke blockchain setiap request.
  • Idempotensi wajib: kunci baris unik pada (tx_hash, log_index), bukan cuma tx_hash — satu transaksi bisa memuat banyak event.
  • Simpan block terakhir yang berhasil diindeks supaya proses bisa dilanjutkan dari titik terakhir setelah restart, bukan dari awal.
  • Tunggu beberapa konfirmasi block sebelum menganggap event final — block yang sangat baru masih punya kemungkinan kecil di-reorg (digantikan rantai lain), meski kecil di Ethereum pasca-Merge.

Kenapa tidak query blockchain langsung tiap request

Menampilkan "riwayat transaksi" pengguna di halaman web dengan memanggil eth_getLogs setiap kali halaman dibuka akan lambat (satu panggilan RPC ekstra per request), rawan kena rate limit provider, dan tidak bisa di-JOIN dengan data lain di database-mu (nama pengguna, status KYC, dst). Solusi standarnya sama dengan pola caching pada umumnya: proses latar belakang menyalin data relevan ke database lokal, aplikasi utama membaca dari salinan itu.

Skema tabel indexing

CREATE TABLE gold_transactions (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    tx_hash         CHAR(66) NOT NULL,
    log_index       INT UNSIGNED NOT NULL,
    block_number    BIGINT UNSIGNED NOT NULL,
    event_name      VARCHAR(32) NOT NULL,
    dari_alamat     CHAR(42) NULL,
    ke_alamat       CHAR(42) NULL,
    jumlah_gram     DECIMAL(36, 0) NOT NULL,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    UNIQUE KEY uq_log (tx_hash, log_index)
);

CREATE TABLE indexer_state (
    kontrak         VARCHAR(42) PRIMARY KEY,
    last_block      BIGINT UNSIGNED NOT NULL
);

Kenapa UNIQUE KEY (tx_hash, log_index), bukan tx_hash saja. Satu transaksi yang memanggil mintGold() bisa memicu lebih dari satu event dalam satu eksekusi (mis. Transfer bawaan ERC-20 dan EmasDiterbitkan kustom). Menjadikan tx_hash saja sebagai unique key akan membuat baris kedua ditolak sebagai "duplikat" padahal keduanya event yang sah dan berbeda.

Indexer di web3j: Flowable event

import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;

EthFilter filter = new EthFilter(
    DefaultBlockParameter.valueOf(BigInteger.valueOf(lastBlockTersimpan)),
    DefaultBlockParameterName.LATEST,
    alamatKontrak
);

kontrak.emasDiterbitkanEventFlowable(filter).subscribe(event -> {
    // Idempoten: INSERT ... ON DUPLICATE KEY UPDATE, atau cek exists dulu
    simpanKeMySQL(
        event.log.getTransactionHash(),
        event.log.getLogIndex().intValue(),
        event.log.getBlockNumber(),
        "EmasDiterbitkan",
        null,
        event.ke,
        event.gram
    );
    updateLastBlock(event.log.getBlockNumber());
}, error -> log.error("Indexer error, akan retry dari last_block tersimpan", error));

Polling vs subscription — dan kenapa polling lebih aman untuk pemula

Polling (eth_getLogs berkala)Subscription (WebSocket)
KompleksitasRendah — cron job/scheduled task biasaLebih tinggi — perlu kelola koneksi persisten & reconnect
LatensiSesuai interval polling (mis. tiap 15 detik)Nyaris real-time
Tahan restart prosesYa — tinggal lanjut dari last_blockButuh penanganan tambahan agar tidak kehilangan event saat reconnect
Provider gratis mendukung?Hampir selaluSering dibatasi paket berbayar

Konfirmasi block: menunggu sebelum menganggap final

Block terbaru masih bisa "berubah pikiran". Dalam kondisi jaringan tertentu, block yang baru saja ditambang bisa digantikan block lain di cabang yang berbeda (reorg) — meski di Ethereum pasca-Merge kemungkinannya sangat kecil untuk block yang sudah finalized. Praktik aman: proses indexer menunggu event berada beberapa block di belakang latest (mis. 12 block, atau tunggu status finalized lewat eth_getBlockByNumber dengan tag finalized) sebelum menuliskannya sebagai baris permanen di database — supaya tabel gold_transactions tidak pernah perlu di-rollback.

Latihan: buat tabel gold_transactions dan indexer_state di MySQL lokal. Tulis satu skrip Java (atau PHP dengan polling eth_getLogs manual lewat web3.php) yang membaca event EmasDiterbitkan dari kontrak testnet-mu sejak block deploy, menuliskannya idempoten ke tabel, lalu jalankan skripnya dua kali berturut-turut — pastikan baris tidak terduplikasi pada jalankan kedua.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.