Skip to content
··4 menit baca

Dari 3 Menit ke 500ms: Bug Signup yang Tidak Masuk Akal

Saya memburu delay signup 3 menit yang ternyata adalah user Schrödinger — ada dan tidak ada secara bersamaan karena lag replikasi database antara write dan read.

Kamu tahu perasaan ketika pengguna melaporkan bug yang sama sekali tidak masuk akal? "Aplikasinya butuh 3 menit untuk loading setelah saya signup." Tiga menit? Itu bukan waktu loading, itu istirahat ngopi. Ini cerita tentang bagaimana saya melacak salah satu bug paling aneh dalam sejarah DIALØGUE.

Misteri Dimulai

Semuanya dimulai dengan polos. Pengguna baru mendaftar menggunakan Google SSO, bersemangat mencoba DIALØGUE. Lalu... tidak ada. Yah, tidak sepenuhnya tidak ada — mereka mendapat loading skeleton yang kami desain dengan cantik. Selama tiga menit penuh.

Bagian anehnya? Ini hanya terjadi pada pengguna baru. Pengguna yang sudah ada bisa login seketika. Dan tidak konsisten — kadang 1 menit, kadang 3, sesekali langsung bekerja.

Pikiran pertama saya: "Pasti masalah cold start." (Narator: Bukan masalah cold start.)

Investigasi

Ronde 1: Salahkan Frontend

```typescript
// Tersangka pertama: Hook loading profil
useEffect(() => {
  if (user) {
    fetchUserProfile(); // Ini yang lama banget
  }
}, [user]);
```

Ditambahkan timer di mana-mana. API call memang butuh 3 menit. Tapi kenapa? Backend seharusnya mengembalikan data atau error, bukan hanya... menunggu.

Ronde 2: Salahkan Backend

Menyelam ke Supabase Edge Functions kami:

```typescript
// Edge Function untuk mendapatkan profil user
const { data: profile } = await supabase
  .from('users')
  .select('*')
  .eq('id', userId)
  .single();

if (!profile) {
  // User baru - buat profil
  await createUserProfile(userId);
}
```

Ini terlihat benar. Harusnya cepat, kan? Waktunya tambah logging lagi.

Ronde 3: Plot Semakin Tebal

Setelah menambah log di mana-mana (dan saya maksud di mana-mana), saya menemukan sesuatu yang aneh:

[00:00] User sign in dengan Google
[00:01] Auth trigger aktif - buat user record
[00:01] Frontend minta profil
[00:01] Edge Function query user... TIDAK ADA HASIL
[00:02] Edge Function coba buat user...
[00:02] Database constraint error: User sudah ada
[00:03] Function retry...
[03:00] Function akhirnya timeout

Tunggu, apa? User tidak ada, tapi juga sudah ada? User Schrödinger? T.T

Pengungkapan

Setelah menatap log database sampai mata perih, akhirnya saya melihatnya. Database kami punya proses yang saling berkompetisi:

  1. Supabase Auth Trigger: Membuat user record saat signup
  2. Edge Function: Mencoba membuat user kalau tidak ditemukan
  3. Database Replication Lag: INSERT dari trigger belum tereplikasi ke read replica

Ini yang terjadi:

-- Auth trigger (di primary database)
INSERT INTO users (id, email) VALUES ($1, $2);

-- Edge Function (baca dari replica)
SELECT * FROM users WHERE id = $1; -- Tidak menemukan apa-apa!

-- Edge Function (mencoba membantu)
INSERT INTO users (id, email) VALUES ($1, $2); -- KONFLIK!

Function akan retry dengan exponential backoff, setiap percobaan kena race condition yang sama sampai:

  • Replikasi akhirnya terkejar (1-3 menit)
  • Function timeout (3 menit)

Update: Penyebab Sebenarnya dan Solusi Akhir yang Robust

Setelah postingan awal, kami terus debugging, dan meskipun perubahan backend kami adalah perbaikan, akar misterinya tetap sulit ditemukan. Terobosan datang ketika kami mengalihkan fokus dari backend ke flow hidrasi dan autentikasi sisi klien.

Penyebab Sebenarnya: Race Condition Sisi Klien

Masalahnya bukan database trigger yang lambat atau Edge Function yang cold start. Masalah sebenarnya adalah race condition klasik sisi klien:

  1. OAuth Redirect: Pengguna baru sign in dengan Google dan diredirect kembali ke aplikasi kami.
  2. Async Session: Library klien Supabase (supabase-js) mulai memproses token dari URL untuk membuat session. Ini proses asinkron.
  3. Premature Render: Tapi aplikasi React kami langsung render. Dia meminta session pengguna sebelum proses asinkron dari langkah 2 selesai.
  4. Kegagalan: Aplikasi mendapat session null, menyimpulkan pengguna tidak login, dan merender state kosong atau error. Beberapa saat kemudian, session tersedia, tapi sudah terlambat — UI sudah membuat keputusannya.

Workaround awal kami, seperti menambah retry sisi klien, hanyalah gejala dari melawan race condition fundamental ini.

Solusinya: Satu Sumber Kebenaran untuk Autentikasi

Solusi yang benar dan final adalah merancang ulang manajemen state autentikasi frontend kami agar benar-benar event-driven dan robust.

1. Logic Terpusat di SupabaseProvider: Kami merefaktor SupabaseProvider kami menjadi satu-satunya sumber kebenaran yang otoritatif untuk autentikasi. Kami menghapus semua listener dan check lain dari hook lain.

2. Menggunakan onAuthStateChange dengan Benar: Inti dari perbaikan ini adalah mengandalkan secara eksklusif listener onAuthStateChange dari Supabase.

// Logic yang disederhanakan di SupabaseProvider.tsx

export function SupabaseProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true); // Mulai dalam state loading

  useEffect(() => {
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {
        setUser(session?.user ?? null);
        // Kita baru menganggap autentikasi "selesai" setelah listener ini aktif.
        setLoading(false);
      }
    );

    return () => subscription.unsubscribe();
  }, []);

  // ...
}

Pattern ini memastikan seluruh aplikasi tetap dalam state loading sampai Supabase mengonfirmasi session pengguna valid atau null. Tidak ada lagi race condition.

Happy Ending

DIALØGUE sekarang onboarding pengguna baru dalam waktu kurang dari satu detik. Tidak ada lagi istirahat ngopi saat signup. Tidak ada lagi pengguna bingung yang bertanya-tanya apakah mereka merusak sesuatu.

Perbaikan ini sudah di production selama 3 minggu. Nol masalah timeout. Nol race condition. Hanya signup yang mulus dan cepat seperti seharusnya sejak awal.

Apakah worth menghabiskan seminggu debugging ini? Ketika saya melihat pengguna baru dengan lancar membuat podcast pertama mereka dalam hitungan menit setelah mendaftar — tentu saja. :D

Pernahkah kamu melacak bug di mana sesuatu secara bersamaan ada dan tidak ada? Saya merasa setiap developer punya setidaknya satu cerita bug Schrödinger. Saya ingin dengar ceritamu!

Salam,

Chandler

Ingin mencoba signup yang sekarang cepat? Buat podcast AI-mu di DIALØGUE. Saya janji tidak akan butuh 3 menit lagi :)

Lanjutkan Membaca

Perjalanan Saya
Terhubung
Bahasa
Preferensi