-
Notifications
You must be signed in to change notification settings - Fork 514
Backend fallback (cloud run) #1306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
f457fc1
Update dependencies and enhance Cloud Run support
mantrakp04 5ca107c
Move Dockerfile to docker/backend
N2D4 490cab6
Add .gcloudignore file to exclude specific files from Google Cloud de…
mantrakp04 23e760d
Remove .gcloudignore file and simplify Dockerfile installation comman…
mantrakp04 42747da
Refactor imports to use background-tasks utility
mantrakp04 ee150a9
Implement OpenTelemetry shutdown handling and enhance database connec…
mantrakp04 eb0627a
Add E2E Fallback Tests Workflow and Update Environment Configurations
mantrakp04 6e18155
Refactor fallback URL handling and enhance backend API resilience
mantrakp04 8cbaae6
Merge branch 'dev' into gcp-stuff
mantrakp04 cd6e7e4
Merge branch 'dev' into gcp-stuff
mantrakp04 0c1cfa8
Refactor OpenTelemetry integration and cleanup
mantrakp04 a5efcad
Merge branch 'dev' into gcp-stuff
mantrakp04 573f69e
Enhance SDK fallback logic and update test configurations
mantrakp04 01a6652
Update e2e fallback tests command for improved execution
mantrakp04 a10b0f1
Update e2e fallback tests command to exclude additional test files
mantrakp04 a5788ff
Add Cloud Build configuration for Docker image build and deployment
mantrakp04 41cdf3b
Refine SDK fallback tests command to streamline exclusions
mantrakp04 f9efe6b
Remove cloudbuild.yaml configuration file for Docker image build and …
mantrakp04 f45e081
Refactor StackClientInterface for improved URL fallback handling
mantrakp04 3546663
Refactor prisma-client and background-tasks for improved SIGTERM hand…
mantrakp04 0640a72
Merge branch 'dev' into gcp-stuff
mantrakp04 50fd34a
Remove unnecessary ESLint disable comment in background-tasks.tsx for…
mantrakp04 d47d395
Merge branch 'dev' into gcp-stuff
mantrakp04 c6e5b92
Merge branch 'dev' into gcp-stuff
mantrakp04 3228ca2
Merge branch 'dev' into gcp-stuff
mantrakp04 25be3f2
Merge branch 'dev' into gcp-stuff
mantrakp04 e23330a
Merge branch 'dev' into gcp-stuff
mantrakp04 fe1fc15
Merge branch 'dev' into gcp-stuff
mantrakp04 5d95240
Refactor Cloud Run IP handling in getBrowserEndUserInfo and enhance S…
mantrakp04 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add E2E Fallback Tests Workflow and Update Environment Configurations
- Introduced a new GitHub Actions workflow for end-to-end fallback tests, ensuring the SDK properly exercises fallback logic when the primary backend is down. - Updated environment configuration files across multiple applications to include fallback API URLs. - Enhanced the backend's package.json to support fallback logic in development mode. - Added client-side components and pages for testing fallback scenarios in the demo application. - Improved the StackClientInterface to handle fallback URLs and implement sticky fallback behavior. These changes enhance the testing framework and improve the SDK's resilience in handling backend failures.
- Loading branch information
commit eb0627adca3729f73ccee77eebad6444f5919bb5
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| # TODO: keep in sync with e2e-tests.yaml — this is a near-copy with the backend | ||
| # started on the fallback port (8110) only, so the SDK exercises fallback logic. | ||
| name: Runs E2E Fallback Tests | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| - dev | ||
| pull_request: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }} | ||
|
|
||
| jobs: | ||
| build: | ||
| name: E2E Fallback Tests (Node ${{ matrix.node-version }}) | ||
| runs-on: ubicloud-standard-8 | ||
| env: | ||
| NODE_ENV: test | ||
| STACK_ENABLE_HARDCODED_PASSKEY_CHALLENGE_FOR_TESTING: yes | ||
| STACK_DATABASE_CONNECTION_STRING: "postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:8128/stackframe" | ||
| STACK_EXTERNAL_DB_SYNC_MAX_DURATION_MS: "20000" | ||
| STACK_EXTERNAL_DB_SYNC_DIRECT: "false" | ||
|
|
||
| strategy: | ||
| matrix: | ||
| node-version: [22.x] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
|
|
||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
|
|
||
| - name: Start Docker Compose in background | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: docker compose -f docker/dependencies/docker.compose.yaml up --pull always -d & | ||
| wait-on: /dev/null | ||
| tail: true | ||
| wait-for: 3s | ||
| log-output-if: true | ||
|
|
||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Create .env.test.local files | ||
| run: | | ||
| cp apps/backend/.env.development apps/backend/.env.test.local | ||
| cp apps/dashboard/.env.development apps/dashboard/.env.test.local | ||
| cp apps/e2e/.env.development apps/e2e/.env.test.local | ||
| cp docs/.env.development docs/.env.test.local | ||
| cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local | ||
| cp examples/demo/.env.development examples/demo/.env.test.local | ||
| cp examples/docs-examples/.env.development examples/docs-examples/.env.test.local | ||
| cp examples/e-commerce/.env.development examples/e-commerce/.env.test.local | ||
| cp examples/middleware/.env.development examples/middleware/.env.test.local | ||
| cp examples/supabase/.env.development examples/supabase/.env.test.local | ||
| cp examples/convex/.env.development examples/convex/.env.test.local | ||
|
|
||
| - name: Configure fallback backend URL | ||
| run: | | ||
| echo "NEXT_PUBLIC_STACK_FALLBACK_API_URL=http://localhost:8110" >> apps/backend/.env.test.local | ||
| echo "NEXT_PUBLIC_STACK_FALLBACK_API_URL=http://localhost:8110" >> apps/dashboard/.env.test.local | ||
| echo "NEXT_PUBLIC_STACK_FALLBACK_API_URL=http://localhost:8110" >> apps/e2e/.env.test.local | ||
| echo "NEXT_PUBLIC_STACK_FALLBACK_API_URL=http://localhost:8110" >> examples/demo/.env.test.local | ||
| echo "STACK_BACKEND_BASE_URL=http://localhost:8110" >> apps/e2e/.env.test.local | ||
|
|
||
| - name: Build | ||
| run: pnpm build | ||
|
|
||
| - name: Wait on Postgres | ||
| run: pnpm run wait-until-postgres-is-ready:pg_isready | ||
|
|
||
| - name: Wait on Inbucket | ||
| run: pnpx wait-on tcp:localhost:8129 | ||
|
|
||
| - name: Wait on Svix | ||
| run: pnpx wait-on tcp:localhost:8113 | ||
|
|
||
| - name: Wait on QStash | ||
| run: pnpx wait-on tcp:localhost:8125 | ||
|
|
||
| - name: Wait on ClickHouse | ||
| run: pnpx wait-on http://localhost:8136/ping | ||
|
|
||
| - name: Initialize database | ||
| run: pnpm run db:init | ||
|
|
||
| # Start backend ONLY on fallback port 8110 — primary port 8102 is intentionally left down | ||
| # so the SDK exercises its fallback logic for every request. | ||
| - name: Start stack-backend on fallback port (8110) | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: pnpm -C apps/backend run with-env:test next start --port 8110 --log-order=stream & | ||
| wait-on: | | ||
| http://localhost:8110 | ||
| tail: true | ||
| wait-for: 30s | ||
| log-output-if: true | ||
|
|
||
| - name: Start stack-dashboard in background | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: pnpm run start:dashboard --log-order=stream & | ||
| wait-on: | | ||
| http://localhost:8101 | ||
| tail: true | ||
| wait-for: 30s | ||
| log-output-if: true | ||
|
|
||
| - name: Start mock-oauth-server in background | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: pnpm run start:mock-oauth-server --log-order=stream & | ||
| wait-on: | | ||
| http://localhost:8110 | ||
| tail: true | ||
| wait-for: 30s | ||
| log-output-if: true | ||
|
|
||
| - name: Start run-email-queue in background | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: pnpm -C apps/backend run run-email-queue --log-order=stream & | ||
| wait-on: | | ||
| http://localhost:8110 | ||
| tail: true | ||
| wait-for: 30s | ||
| log-output-if: true | ||
|
|
||
| - name: Start run-cron-jobs in background | ||
| uses: JarvusInnovations/background-action@v1.0.7 | ||
| with: | ||
| run: pnpm -C apps/backend run run-cron-jobs:test --log-order=stream & | ||
| wait-on: | | ||
| http://localhost:8110 | ||
| tail: true | ||
| wait-for: 30s | ||
| log-output-if: true | ||
|
|
||
| - name: Wait 10 seconds | ||
| run: sleep 10 | ||
|
|
||
| - name: Verify primary port 8102 is NOT running | ||
| run: | | ||
| if curl -s -o /dev/null -w "%{http_code}" http://localhost:8102/health 2>/dev/null | grep -q "200"; then | ||
| echo "ERROR: Primary backend on port 8102 should NOT be running for fallback tests" | ||
| exit 1 | ||
| fi | ||
| echo "Confirmed: primary port 8102 is down, fallback tests will exercise SDK fallback logic" | ||
|
|
||
| - name: Run tests | ||
| run: pnpm test run | ||
|
|
||
| - name: Verify data integrity | ||
| run: pnpm run verify-data-integrity --no-bail | ||
|
|
||
| - name: Print Docker Compose logs | ||
| if: always() | ||
| run: docker compose -f docker/dependencies/docker.compose.yaml logs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| "use client"; | ||
|
|
||
| import { useStackApp, useUser } from "@stackframe/stack"; | ||
| import Link from "next/link"; | ||
| import { usePathname } from "next/navigation"; | ||
| import { useCallback, useEffect, useRef, useState } from "react"; | ||
|
|
||
| type LogEntry = { | ||
| time: number; | ||
| msg: string; | ||
| ok: boolean; | ||
| }; | ||
|
|
||
| export function FallbackTestClient() { | ||
| const app = useStackApp(); | ||
| const user = useUser(); | ||
| const pathname = usePathname(); | ||
| const [log, setLog] = useState<LogEntry[]>([]); | ||
| const [running, setRunning] = useState(false); | ||
| const renderCount = useRef(0); | ||
| renderCount.current++; | ||
|
|
||
| const addLog = useCallback((msg: string, ok: boolean) => { | ||
| setLog(prev => [...prev, { time: Date.now(), msg, ok }]); | ||
| }, []); | ||
|
|
||
| const runTests = useCallback(async () => { | ||
| setLog([]); | ||
| setRunning(true); | ||
|
|
||
| // Test 1: getProject | ||
| { | ||
| const start = Date.now(); | ||
| try { | ||
| const project = await app.getProject(); | ||
| addLog(`getProject: ${project.id} (${Date.now() - start}ms)`, true); | ||
| } catch (e: any) { | ||
| addLog(`getProject FAILED: ${e.message?.slice(0, 80)} (${Date.now() - start}ms)`, false); | ||
| } | ||
| } | ||
|
|
||
| // Test 2: useUser | ||
| addLog(`useUser: ${user ? user.primaryEmail ?? user.id : "(not signed in)"}`, true); | ||
|
|
||
| // Test 3: 5x getProject to show sticky latency | ||
| { | ||
| const times: number[] = []; | ||
| for (let i = 0; i < 5; i++) { | ||
| const start = Date.now(); | ||
| try { | ||
| await app.getProject(); | ||
| times.push(Date.now() - start); | ||
| } catch { | ||
| times.push(-1); | ||
| } | ||
| } | ||
| const avg = times.filter(t => t >= 0).reduce((a, b) => a + b, 0) / times.filter(t => t >= 0).length; | ||
| addLog(`getProject x5: [${times.map(t => t >= 0 ? `${t}ms` : "FAIL").join(", ")}] avg=${Math.round(avg)}ms`, times.every(t => t >= 0)); | ||
| } | ||
|
|
||
| setRunning(false); | ||
| }, [app, user, addLog]); | ||
|
|
||
| useEffect(() => { | ||
| void runTests(); | ||
| }, []); // eslint-disable-line react-hooks/exhaustive-deps | ||
|
|
||
| return ( | ||
| <div> | ||
| <section style={{ marginBottom: 24, padding: 16, background: "#f0f8ff", borderRadius: 8 }}> | ||
| <h2 style={{ marginTop: 0 }}>Client-side</h2> | ||
|
|
||
| <div style={{ marginBottom: 12, fontSize: 12, color: "#666" }}> | ||
| <strong>Debug:</strong> renders={renderCount.current} | pathname={pathname} | ||
| </div> | ||
|
|
||
| <div style={{ marginBottom: 16, fontFamily: "monospace", fontSize: 13, lineHeight: 1.8 }}> | ||
| {log.map((entry, i) => ( | ||
| <div key={i} style={{ color: entry.ok ? "#2a7" : "#c33" }}> | ||
| {entry.ok ? "OK" : "ERR"} {entry.msg} | ||
| </div> | ||
| ))} | ||
| {log.length === 0 && <div style={{ color: "#999" }}>Running...</div>} | ||
| </div> | ||
|
|
||
| <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}> | ||
| <button onClick={() => void runTests()} disabled={running} style={{ padding: "6px 14px", cursor: running ? "wait" : "pointer" }}> | ||
| {running ? "Running..." : "Re-run"} | ||
| </button> | ||
| </div> | ||
| </section> | ||
|
|
||
| <section style={{ padding: 16, background: "#fff8f0", borderRadius: 8 }}> | ||
| <h2 style={{ marginTop: 0 }}>SPA Navigation Test</h2> | ||
| <p style={{ fontSize: 13, color: "#666" }}> | ||
| Click these links (client-side navigation) and come back. | ||
| If sticky fallback persists, requests after navigating back should still be fast. | ||
| </p> | ||
| <div style={{ display: "flex", gap: 12 }}> | ||
| <Link href="/" style={{ color: "#06c" }}>Home</Link> | ||
| <Link href="/fallback-test" style={{ color: "#06c" }}>This page (reload)</Link> | ||
| <Link href="/settings" style={{ color: "#06c" }}>Settings</Link> | ||
| </div> | ||
| </section> | ||
| </div> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { stackServerApp } from "src/stack"; | ||
| import { FallbackTestClient } from "./client"; | ||
|
|
||
| export default async function FallbackTestPage() { | ||
| const serverStart = Date.now(); | ||
| const user = await stackServerApp.getUser(); | ||
| const project = await stackServerApp.getProject(); | ||
| const serverDuration = Date.now() - serverStart; | ||
|
|
||
| return ( | ||
| <div style={{ fontFamily: "monospace", padding: 32, maxWidth: 900 }}> | ||
| <h1>SDK Fallback Test</h1> | ||
|
|
||
| <section style={{ marginBottom: 24, padding: 16, background: "#f5f5f5", borderRadius: 8 }}> | ||
| <h2 style={{ marginTop: 0 }}>Server-side (RSC)</h2> | ||
| <pre>{JSON.stringify({ projectId: project.id, projectName: project.displayName, user: user?.primaryEmail ?? user?.id ?? null, duration: `${serverDuration}ms` }, null, 2)}</pre> | ||
| </section> | ||
|
|
||
| <FallbackTestClient /> | ||
| </div> | ||
| ); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.