•CASE STUDY

Interactive SQL Query Notebook (Snowflake)

4 min read·759 words·Intermediate

Asked at

1 candidate report in Aug 2026

How to use this case study

SDE-2 / Mid

  • Explain submitting a query as an async job
  • Getting its status
  • Fetching results page by page

SDE-3 / Senior

  • Go deeper on polling vs push (WebSocket/SSE)
  • Large result sets (result storage and pagination)
  • Cancellation
  • Timeouts and reconnecting after a browser refresh

Staff / Principal

  • Discuss multi-tenant concurrency limits
  • Result caching
  • Cost controls
  • Scaling the query service

Problem RestatementProblem

Snowflake asked: design a notebook-like service where users type SQL queries in cells, run them against a data warehouse, and see results. Queries may take milliseconds or hours, and results can be a few rows or millions. The focus: how the client learns the job status and gets results, including long queries, big results, cancellation and a page refresh in the middle.

RequirementsRequirements

  • Submit a query from a cell. See status (queued, running, progress, done, failed).
  • View results in pages, sort and download.
  • Cancel a running query.
  • Refresh the browser or reopen the notebook later and still see the running or finished results.
  • Many users running queries concurrently, with fair limits.

ArchitectureArchitecture

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
    NB["Notebook UI"] -->|"submit"| API["Query API"]
    API --> JDB[("Query jobs DB")]
    API --> SCH["Scheduler - per-user/warehouse limits"]
    SCH --> ENG["Query engine / warehouse"]
    ENG -->|"progress"| JDB
    ENG -->|"results"| RS[("Result store - chunked files")]
    NB -->|"status: SSE / polling"| API
    NB -->|"fetch pages"| API
    API --> RS

FlowFlows

  1. Submit: POST /queries { sql, notebook_id, cell_id } → 202 { query_id }. The job is saved as queued.
  2. Short queries fast path: the API waits up to ~1–2 seconds. If the query finishes, it returns the first page of results directly (no second round trip for most interactive queries).
  3. Status: for longer queries, the UI subscribes via SSE (server-sent events) to /queries/{id}/events for status and progress, with polling as a fallback (every 1–2 s, with backoff as time passes).
  4. Results: the engine writes results in chunks (e.g., 10 MB compressed files) to the result store, along with the schema and row counts. GET /queries/{id}/results?page=N (or a cursor) reads the right chunk. The first page shows immediately, and the rest load when the user scrolls.
  5. Download: a pre-signed URL to the chunk files (or a single exported file).
  6. Cancel: POST /queries/{id}/cancel → the engine stops the query and the status becomes cancelled.

Deep Dive — A query that outlives the browser tabDeep dive

Cells run for milliseconds or for hours. Laptops sleep, tabs get closed, wifi drops. The query must not care.

Weak

Hold the query in the browser

The cell issues the query over a long-lived connection and renders results when they arrive.

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  CELL["Run cell"] --> CONN["Long-lived HTTP connection"]
  CONN --> WH["Warehouse - 40 minute query"]
  LAPTOP["Laptop sleeps / tab closed"] --> DROP["Connection drops"]
  DROP --> LOST["UI has no handle - cannot tell if it is still running"]
  DROP --> RERUN["User re-runs - a second 40-minute query starts"]

The only reference to the running query lives in a browser tab, which is the least durable component in the system. Losing it means the user cannot check on their query and, rationally, re-runs it — doubling the load on a warehouse that is already busy.

Good

Poll a status endpoint

Submit the query, get an id back, and have the UI poll for status and results.

The query is now server-side and survives a dropped connection. What is still fragile is the id: it lives in page state, so a refresh loses it. The user comes back to a cell that looks idle while the query is still running, and re-runs it anyway.

Best

Persist the query id on the cell

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  RUN["Run cell"] --> SUB["Submit - returns query_id"]
  SUB --> SAVE["query_id stored on the notebook cell, server-side"]
  SAVE --> POLL["UI polls status by query_id"]
  REFRESH["Refresh / new device / next day"] --> READ["Read the cell's last query_id"]
  READ --> POLL
  POLL --> DONE["Results from the result store - retained ~24 h"]
  RERUN["Re-run an identical query on unchanged data"] --> CACHE["Reuse cached results - no warehouse work"]
  • The cell owns the query id, not the page. Refreshing, switching device or opening the notebook the next morning all reconnect to the same running or finished query, because nothing important ever lived only in the browser.
  • Results are stored and retained for a window — a day is the common choice — so viewing them again is a fetch, not a re-execution.
  • A result cache keyed by query text and data version makes re-running an unchanged cell free, which is exactly what people do when reading a notebook someone shared.

Two protections that belong with it: cancel must be a real server-side operation on the query id, because a user who navigates away should be able to stop an expensive query; and results must be paginated or size-capped, since a SELECT * that returns fifty million rows will otherwise take the notebook service down rather than the warehouse.

Limits and Fairness

  • Per-user and per-warehouse concurrency limits: excess queries wait in queued with a visible position.
  • Timeouts (e.g., max 2 hours by default) and max result size (bigger results must be exported).
  • Cost controls: show estimated cost or scanned bytes, and warn on huge scans.

Wrap-UpWrap-up

Treat each cell execution as an async query job: submit returns a query ID (with a short synchronous wait so fast queries return inline), status flows over SSE with polling fallback, and results are written in chunks to a result store and fetched page by page or downloaded via pre-signed URLs. Store query IDs on cells so refreshes resume, support cancellation and timeouts, cache results, and enforce per-user concurrency limits with visible queuing.

More Case Studies

Frequently Asked Questions

What is the Interactive SQL Query Notebook (Snowflake) system design question?

Interactive SQL Query Notebook (Snowflake) is a system design interview question asked at FAANG companies. It covers api design, scheduling, storage, real-time and tests your ability to design scalable, production-ready systems. InterviewSkool's breakdown walks you through requirements, API design, architecture, and trade-offs.

Which companies ask the Interactive SQL Query Notebook (Snowflake) question?

Snowflake have reportedly asked variations of this question in system design interviews. The exact wording may differ, but the core design challenges remain the same.

How should I prepare for the Interactive SQL Query Notebook (Snowflake) interview question?

Start with the problem statement and scale estimates, then design the high-level architecture. Focus on the core components, data model, and API design. InterviewSkool's breakdown covers the full solution with mermaid diagrams and trade-off analysis to help you prep efficiently.

What level is the Interactive SQL Query Notebook (Snowflake) question?

This question is suitable for SDE-2, SDE-3, and Staff engineer interviews. The level guidance on this page provides specific tips for each level — SDE-2 candidates should focus on core architecture, while Staff engineers should discuss trade-offs, monitoring, and incremental rollouts.

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with InterviewSkool's AI interviewer.

Start System Design Interview →