Pengembangan Plugin
NocoBase ClusterEnterprise Edition+Latar Belakang
Dalam environment single-node, plugin biasanya dapat memenuhi kebutuhan melalui state, event, atau task dalam proses; sedangkan dalam cluster mode, plugin yang sama mungkin berjalan di multiple instance bersamaan, menghadapi masalah tipikal berikut:
- Konsistensi state: Jika data konfigurasi atau runtime hanya disimpan di memory, sulit untuk disinkronkan antar instance, mudah terjadi dirty read atau eksekusi duplikat.
- Scheduling task: Task yang memakan waktu lama tanpa mekanisme antrian dan konfirmasi yang jelas, akan menyebabkan multiple instance mengeksekusi task yang sama secara bersamaan.
- Race condition: Saat melibatkan perubahan schema atau alokasi resource, perlu serialisasi operasi untuk menghindari conflict akibat concurrent write.
Inti NocoBase telah menyediakan berbagai interface middleware di lapisan aplikasi, membantu plugin menggunakan kemampuan terpadu dalam environment cluster. Berikut akan memperkenalkan penggunaan dan best practice cache, sync message, message queue, dan distributed lock dengan kode sumber.
Solusi
Komponen Cache
Untuk data yang akan disimpan di memory, disarankan menggunakan komponen cache built-in sistem untuk pengelolaan.
- Dapatkan instance cache default melalui
app.cache. Cachemenyediakan operasi dasar sepertiset/get/del/reset, juga mendukungwrapdanwrapWithConditionuntuk membungkus logika cache, serta metode batchmset/mget/mdel.- Pada deployment cluster, disarankan meletakkan shared data di storage yang memiliki kemampuan persistensi (seperti Redis), dan mengatur
ttldengan bijak untuk menghindari kehilangan cache akibat restart instance.
Contoh: Inisialisasi dan penggunaan cache di plugin-auth
SyncMessageManager
Jika state di memory tidak dapat menggunakan distributed cache (misalnya tidak dapat di-serialize), maka saat state berubah karena operasi user, perubahan tersebut perlu diberi tahu ke instance lain melalui sync signal untuk menjaga konsistensi state.
- Plugin base class telah mengimplementasikan
sendSyncMessage, secara internal memanggilapp.syncMessageManager.publishdan secara otomatis menambahkan prefix tingkat aplikasi pada channel, untuk menghindari channel conflict. publishdapat menentukantransaction, message akan dikirim setelah database transaction di-commit, untuk menjamin sinkronisasi state dan message.- Tangani message dari instance lain melalui
handleSyncMessage, dapat di-subscribe pada tahapbeforeLoad, sangat cocok untuk skenario seperti perubahan konfigurasi, sinkronisasi Schema, dll.
Contoh: plugin-data-source-main menjaga konsistensi schema multi-node melalui sync message
PubSubManager
Message broadcast adalah komponen dasar dari sync signal, dan juga mendukung penggunaan langsung. Saat perlu broadcast message antar instance, dapat diimplementasikan melalui komponen ini.
app.pubSubManager.subscribe(channel, handler, { debounce })dapat melakukan subscribe channel antar instance; opsidebouncedigunakan untuk debouncing, untuk menghindari callback berulang akibat broadcast duplikat.publishmendukungskipSelf(default true) danonlySelf, untuk mengontrol apakah message dikirim balik ke instance ini.- Adapter perlu dikonfigurasi sebelum aplikasi dimulai (seperti Redis, RabbitMQ, dll), jika tidak default tidak akan terhubung ke sistem messaging eksternal.
Contoh: plugin-async-task-manager menggunakan PubSub untuk broadcast event task cancellation
Komponen Message Queue (EventQueue)
Message queue digunakan untuk menjadwalkan async task, cocok untuk menangani operasi yang memakan waktu lama atau yang dapat di-retry.
- Deklarasikan consumer melalui
app.eventQueue.subscribe(channel, { idle, process, concurrency }),processmengembalikanPromise, dapat menggunakanAbortSignal.timeoutuntuk mengontrol timeout. publishakan secara otomatis melengkapi prefix nama aplikasi, dan mendukung opsi sepertitimeout,maxRetries. Default mengadaptasi memory queue, dapat dialihkan ke extension adapter seperti RabbitMQ sesuai kebutuhan.- Dalam cluster, pastikan semua node menggunakan adapter yang sama, untuk menghindari pemisahan task antar node.
Contoh: plugin-async-task-manager menggunakan EventQueue untuk scheduling task
LockManager
Saat perlu menghindari operasi race, dapat menggunakan distributed lock untuk men-serialize akses ke resource.
- Default menyediakan adapter
localberbasis proses, dapat mendaftarkan implementasi distributed seperti Redis; kontrol concurrency melaluiapp.lockManager.runExclusive(key, fn, ttl)atauacquire/tryAcquire. ttldigunakan untuk fallback release lock, mencegah lock dipegang selamanya pada situasi anomali.- Skenario umum meliputi: perubahan Schema, mencegah task duplikat, rate limiting, dll.
Contoh: plugin-data-source-main menggunakan distributed lock untuk melindungi proses delete field
Saran Pengembangan
- Konsistensi state memory: Hindari menggunakan state memory dalam pengembangan sebanyak mungkin, gunakan cache atau sync message untuk menjaga konsistensi state.
- Prioritaskan reuse interface built-in: Gunakan kemampuan seperti
app.cache,app.syncMessageManagersecara terpadu, hindari mengimplementasikan logika komunikasi cross-node berulang dalam plugin. - Perhatikan transaction boundary: Operasi dengan transaction harus menggunakan
transaction.afterCommit(syncMessageManager.publishsudah built-in) untuk menjamin konsistensi data dan message. - Tetapkan strategi backoff: Untuk task queue dan broadcast, atur
timeout,maxRetries,debouncedengan bijak, untuk mencegah lonjakan traffic baru pada situasi anomali. - Monitoring dan logging pendukung: Manfaatkan log aplikasi dengan baik untuk mencatat informasi seperti nama channel, payload message, lock key, untuk memudahkan investigasi masalah occasional di cluster.
Melalui kemampuan di atas, plugin dapat berbagi state secara aman antar instance, sinkronisasi konfigurasi, scheduling task, memenuhi requirement stabilitas dan konsistensi pada skenario deployment cluster.

