ข้ามไปยังเนื้อหา

Realtime: Broadcast and Presence

Broadcast กับ Presence คือ layer pub/sub และ tracking การ online ทั่วไปที่วางอยู่บน Realtime channel และต่างจาก Postgres Changes ตรงที่เปิดอยู่โดย default ไม่ต้องตั้งค่า table หรือ replication ใด ๆ เลย

Postgres Changes (ที่คุยไปในบทก่อน) จะยิงก็ต่อเมื่อ row ใน database เปลี่ยนแปลงจริง ๆ เท่านั้น แต่ฟีเจอร์ realtime หลายอย่างไม่ต้องการ — และไม่ควรต้องจ่ายต้นทุนของ — การ write ลง database เลยด้วยซ้ำ เช่น ตำแหน่ง cursor แบบสด, flag “user กำลังพิมพ์อยู่” หรือ chat message ที่แค่ต้องถึงคนที่อยู่ในห้องตอนนั้นพอ Broadcast ครอบคลุมเคสนี้ตรง ๆ client ที่ subscribe channel ไหนก็ส่ง JSON payload ได้ แล้ว subscriber คนอื่นบน channel เดียวกันจะได้รับตรง ๆ โดยไม่มี table ใน public, ไม่มี publication และไม่มี WAL เข้ามาเกี่ยวข้องเลย

ส่ง broadcast message จาก supabase-js

const channel = supabase.channel('room1');
channel.on('broadcast', { event: 'cursor-pos' }, (payload) => {
console.log('Cursor moved:', payload.payload);
});
channel.subscribe();
channel.send({
type: 'broadcast',
event: 'cursor-pos',
payload: { x: 120, y: 340 },
});

subscriber ทุกตัวที่ลงทะเบียน .on('broadcast', { event: 'cursor-pos' }, ...) ไว้บน room1 จะได้รับ payload นี้ ปกติภายในหลักสิบมิลลิวินาที ไม่มี round trip ไป database เลยบน hot path

Presence ต่อยอดจาก channel mechanism เดียวกันเพื่อแก้ปัญหาคนละแบบ คือรู้ว่า client ไหนกำลังต่ออยู่กับ channel ตอนนี้บ้าง และแต่ละคนอยากแชร์ state อะไร (username, ตำแหน่ง cursor, flag “online” หรือ “typing”) แทนที่คุณจะต้อง broadcast message “เข้าร่วมแล้ว” กับ “ออกแล้ว” เองแล้วมา reconcile เอง channel ที่เปิด Presence จะเก็บ ชุด client ที่ present อยู่แบบ sync กันตลอด และ push update ให้ทุกคนที่ subscribe อยู่ทุกครั้งที่มีคนเข้าร่วม, ออก หรืออัปเดต state ที่ track ไว้

client ประกาศ presence ของตัวเองด้วย track และอ่านชุด client ที่ present อยู่ตอนนี้ทั้งหมดด้วย presenceState

const channel = supabase.channel('room1');
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
console.log('Currently online:', state);
});
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await channel.track({ user_id: 'user-42', online_at: new Date().toISOString() });
}
});

event sync จะยิงทุกครั้งที่ presence set เปลี่ยนไปสำหรับใครก็ตามบน channel และ presenceState() จะคืนภาพรวมปัจจุบันเต็ม ๆ เสมอ โดย key ด้วย presence key แต่ละตัวถือ state ที่ client นั้น track ไว้

นี่คือความต่างที่ควรจำไว้ต่อจากบทก่อน Postgres Changes ทำงานได้ก็ต่อเมื่อคุณรัน alter publication supabase_realtime add table ... ให้ table นั้นก่อน เพราะโดยพื้นฐานแล้วนี่คือการ stream ออกมาจาก replication log ของ Postgres เอง Broadcast กับ Presence ไม่เกี่ยวกับเรื่องนั้นเลย — ทั้งคู่เป็น layer messaging และ state-sync ที่อยู่ในตัว Realtime server ล้วน ๆ เปิดใช้งานได้ทันทีที่ต่อ channel โดยไม่มีอะไรต้องตั้งค่าในฝั่ง database เลย

flowchart LR
  subgraph channel["Realtime channel room1"]
    c1["Client A"]
    c2["Client B"]
  end
  c1 -->|"channel.send broadcast cursor-pos"| c2
  c1 -->|"channel.track presence state"| sync["Synced presence state"]
  c2 -->|"channel.track presence state"| sync
  sync -->|"presence sync event"| c1
  sync -->|"presence sync event"| c2
Broadcast ส่งตรงระหว่าง client; Presence sync state ที่แชร์กัน — ทั้งคู่ไม่แตะ Postgres
Broadcast กับ Presence ต้องเพิ่ม table เข้า publication supabase_realtime เหมือน Postgres Changes หรือไม่
Broadcast เหมาะกับอะไรที่สุด เทียบกับ Postgres Changes
Presence track อะไรจริง ๆ
method ตัวไหนใช้ประกาศ state ของ client ตัวเองให้ subscriber คนอื่นบน channel ที่ใช้ Presence เห็น