Skip to content
··閱讀時間2分鐘

由3分鐘到500ms:完全冇道理嘅Signup Bug

我追蹤咗一個3分鐘嘅signup delay,結果發現係薛定諤嘅user——因為database寫入同讀取之間嘅replication lag,佢同時存在又唔存在。

你知唔知用戶report一個完全冇道理嘅bug嗰種感覺?「個app sign up之後要3分鐘先load到。」三分鐘?嗰個唔係loading time,嗰個係去沖杯咖啡。呢個係我追蹤DIALØGUE歷史上最奇怪嘅bug之一嘅故事。

謎題開始

佢開始得好innocent。一個新用戶用Google SSO sign up,好excited想試DIALØGUE。然後...咩都冇。好吧,唔係完全咩都冇——佢哋見到我哋設計得好靚嘅loading skeleton。成三分鐘。

奇怪嘅地方?佢只發生喺_新_用戶身上。Existing用戶可以即刻login。而且唔一致——有時1分鐘,有時3分鐘,偶爾即刻就work。

我第一個想法:「一定係cold start問題。」(旁白:唔係cold start問題。

調查

第1輪:Blame Frontend

```typescript
// 第一個嫌疑人:Profile loading hook
useEffect(() => {
  if (user) {
    fetchUserProfile(); // 呢個take forever
  }
}, [user]);
```

四圍加timer。API call的確take 3分鐘。但點解?Backend應該要麼return data,要麼error out,唔係就咁...等。

第2輪:Blame Backend

深入我哋嘅Supabase Edge Functions:

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

if (!profile) {
  // New user - create profile
  await createUserProfile(userId);
}
```

呢個睇落冇問題。應該好快,對唔對?係時候加更多logging。

第3輪:劇情加深

四圍加咗logs(我係話_四圍_)之後,我發現咗啲bizarre嘅嘢:

[00:00] User signs in with Google
[00:01] Auth trigger fires - creates user record
[00:01] Frontend requests profile
[00:01] Edge Function queries for user... NO RESULT
[00:02] Edge Function tries to create user...
[00:02] Database constraint error: User already exists
[00:03] Function retries...
[03:00] Function finally times out

等等,咩話?User唔存在,但同時又已經存在?薛定諤嘅user?T.T

真相

盯住database logs睇到眼攰之後,我終於見到喇。我哋嘅database有competing processes:

  1. Supabase Auth Trigger:喺signup時create user record
  2. Edge Function:如果搵唔到就試create user
  3. Database Replication Lag:Trigger嘅INSERT仲未replicate到read replica

以下係發生緊嘅嘢:

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

-- Edge Function (reading from replica)
SELECT * FROM users WHERE id = $1; -- Returns nothing!

-- Edge Function (trying to help)
INSERT INTO users (id, email) VALUES ($1, $2); -- CONFLICT!

Function會用exponential backoff retry,每次都撞到同一個race condition直到:

  • Replication終於catch up(1-3分鐘)
  • Function timeout(3分鐘)

更新:真正嘅元兇同最終Robust Solution

最初post之後,我哋繼續debug,雖然backend嘅改動係improvements,但mystery嘅root一直搵唔到。突破係當我哋將focus由backend轉到client-side嘅hydration同authentication flow。

真正嘅元兇:Client-Side Race Condition

問題唔係慢嘅database trigger或cold Edge Function。真正嘅問題係經典嘅client-side race condition:

  1. OAuth Redirect: 新用戶用Google sign in然後被redirect返我哋嘅app。
  2. Async Session: Supabase client library(supabase-js)開始處理URL入面嘅token嚟建立session。呢個係async process。
  3. Premature Render: 但我哋嘅React app即刻render。佢_喺_步驟2嘅async process完成之前就ask用戶嘅session。
  4. Failure: App得到null session,判定用戶未login,render blank或error state。幾秒之後session available喇,但太遲——UI已經做咗決定。

我哋最初嘅workarounds,好似加client-side retries,只係fight緊呢個fundamental race condition嘅symptoms。

Solution:Authentication嘅Single Source of Truth

正確嘅最終solution係re-architect我哋嘅frontend authentication state management,令佢truly event-driven同robust。

1. SupabaseProvider入面嘅Centralized Logic: 我哋refactor咗SupabaseProvider做authentication嘅single authoritative source of truth。移除咗其他hooks入面嘅所有其他listeners同checks。

2. 正確使用onAuthStateChange Fix嘅core係_exclusively_依賴Supabase嘅onAuthStateChange listener。

// Simplified logic in SupabaseProvider.tsx

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

  useEffect(() => {
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {
        setUser(session?.user ?? null);
        // 只有當呢個listener fire咗先consider authentication「完成」。
        setLoading(false);
      }
    );

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

  // ...
}

呢個pattern確保成個application保持loading state直到Supabase confirm用戶嘅session係valid定null。再冇race condition喇。

Happy Ending

DIALØGUE依家一秒內就可以onboard新用戶。唔使喺signup嘅時候去沖咖啡喇。唔使再有confused嘅用戶以為佢哋整壞咗啲嘢。

呢個fix已經喺production上面3個禮拜喇。零timeout issues。零race conditions。只有smooth、快嘅signups,好似一開始就應該係咁。

花一個禮拜debug呢個值唔值得?當我見到新用戶可以喺sign up幾分鐘內seamlessly create佢哋第一個podcast——absolutely。:D

你有冇追蹤過一個bug,入面嘅嘢同時存在又唔存在?我覺得每個developer都至少有一個薛定諤bug嘅故事。我好想聽吓你嘅!

祝好,

Chandler

想試吓依家快咗嘅signup?喺DIALØGUECreate你嘅AI podcast。我promise唔會再要3分鐘喇 :)

繼續閱讀

我嘅旅程
聯繫
語言
偏好設定