Working with Large-Scale Datasets Questions
Analyzing data that does not fit comfortably in memory or a single query. Covers distributed processing concepts, partitioning and sampling strategies, query and pipeline performance, and trade-offs when scaling an analysis. Focuses on getting correct answers efficiently at scale.
Describe a reproducible workflow you would implement to deliver a weekly automated report that pulls data from a production database, transforms metrics, and publishes a dashboard. Mention scheduling, testing, error monitoring, and tools you would choose (e.g., Airflow, dbt, Git).
Sample Answer
Requirements & constraints:
- Weekly automated report (ETL + metrics + dashboard) from production DB; reproducible, testable, observable; low-impact on prod; deliver to stakeholders (Tableau/Power BI).
High-level architecture:
Production DB → Airflow (scheduler + orchestration) → dbt (TRANSFORM + tests) → analytics DB / materialized tables → BI tool (dashboard) → Stakeholders
Core components & responsibilities:
- Airflow DAG
- Schedule: weekly (cron) with sensible start_date, catchup=False.
- Tasks: extract (light-weight CDC or read-only queries), run dbt models, run data-quality checks, deploy artifacts, refresh dashboard extracts.
- Retries, exponential backoff, SLA, task-level timeouts, idempotent tasks.
- dbt
- Implements modular SQL models for metrics, documented with schema.yml.
- dbt tests: schema tests (unique, not_null), custom data tests for business rules, snapshots for slowly-changing dims.
- Materializations: incremental for large tables, ephemeral for short-lived transforms.
- CI/CD & reproducibility
- Git repo for DAGs, dbt project, docs; PR + review process.
- GitHub Actions (or CI) runs dbt run + dbt test on PR; prevents broken code merging.
- Versioned releases and tags for reproducibility.
- Testing & validation
- Unit tests: dbt tests, small-sample integration tests against a staging copy of production data.
- Data quality: Great Expectations or dbt-expectations for profiling and alerting on metric drift.
- Backfill and dry-run modes in Airflow for validation.
- Monitoring & error handling
- Centralized logs (ELK) and Airflow metrics exported to Prometheus/Datadog.
- Alerts: Slack/email + runbook links for failures; PagerDuty for critical SLA breaches.
- Automated retries for transient failures; human escalation for data-quality breaks.
- Data lineage: dbt docs + Airflow task metadata to trace upstream sources.
- Deployment & operations
- Staging environment mirrors schema; run scheduled smoke tests before production DAG runs.
- Use service accounts with least privilege for DB access; read replicas for heavy reads.
- Dashboard: incremental refresh; include freshness indicator and table of key validation metrics.
Trade-offs:
- Airflow + dbt provides strong reproducibility and testing but requires maintenance. For smaller teams, managed Airflow (MWAA) or dbt Cloud reduces operational burden.
This workflow ensures weekly, auditable, tested, and observable delivery of business metrics to stakeholders with clear rollback and monitoring paths.
A monitoring dashboard demands sub-minute latency for critical alerts, while analysts need daily aggregates. Describe an architecture to support both: ingestion (batch vs streaming), storage layers (hot/warm/cold), pre-aggregation strategy, and cost trade-offs. Suggest technologies for each layer and how to display freshness metadata on dashboards.
Sample Answer
Requirements and constraints:
- Functional: sub-minute (<=30s) critical alerts; daily aggregates and ad-hoc analyst queries.
- Non-functional: cost-conscious, durable historical storage, simple SQL access for analysts, clear freshness metadata on dashboards.
High-level architecture:
Event sources → Ingest layer (stream + micro-batch) → Streaming processor (real-time pipeline) → Hot/Warm store (for low-latency reads & alerts) → Batch/OLAP store (for daily aggregates & historical) → BI tools (Tableau/PowerBI) + Alerting/Monitoring.
Ingestion:
- Streaming (Kafka / AWS Kinesis): all events published; guarantees low latency for alerts.
- Batch/micro-batch (Airflow or Spark jobs): bulk loads from upstream sources or to backfill.
Streaming processing & alerting:
- Use stream processor (Flink / Kafka Streams / Spark Structured Streaming) to compute rolling metrics and detect alert conditions within seconds. Emit alerts to Alerting system (PagerDuty, Opsgenie) and write materialized real-time aggregates to Hot store.
Pre-aggregation strategy:
- Real-time materialized views: sliding-window aggregates (1s–1m windows) for critical metrics stored in Hot store (Redis, Druid, ClickHouse).
- Nearline/warm aggregates: 5–60 minute rollups in Warm store (ClickHouse, BigQuery with partitioned tables) for exploratory queries.
- Daily batch aggregates: scheduled jobs compute day-level summaries into Cold/OLAP (BigQuery, Snowflake, Redshift) with partitioning by date and precomputed rollups for common dimensions.
Storage layers & tech:
- Hot (<1 minute reads, ephemeral): Redis or ClickHouse for single-row/aggregate reads and alert thresholds.
- Warm (minutes-hours): ClickHouse or Cassandra for time-series rollups enabling fast ad-hoc queries.
- Cold (long-term, cost-efficient): BigQuery / Snowflake / S3 + Parquet + Athena for full-history analytics.
Cost trade-offs:
- Keep event retention in Kafka short (days) to reduce cost; persist only aggregates to long-term stores.
- Hot stores (Redis/ClickHouse) are expensive per GB—limit retention and only store derived aggregates/indices needed for alerts.
- Warm store sized for query latency vs storage cost; use compression and partitioning to lower cost.
- Cold storage on object store (S3 + Parquet) is cheapest; accepts higher query latency for deep historical analytics.
- Use compute-on-demand (serverless query engines) where interactive cost is acceptable; use scheduled materialized tables for heavy, repeated queries.
Data pipeline specifics:
- Exactly-once semantics for correctness (Kafka + idempotent sinks or Flink checkpointing).
- Schema evolution via schema registry (Avro/Protobuf).
- Backfills: batch jobs read cold store and repopulate warm/hot aggregates.
Freshness metadata on dashboards:
- Each widget shows: last_updated_timestamp and latency (event_time → processed_time). Compute and materialize these in aggregation tables; expose via SQL view.
- Color-code freshness (green <30s, yellow 30s–5min, red >5min). For daily aggregates show processing window and dataset_partition_date.
- Include lineage link to ingestion pipeline status and recent ingestion lag metrics (from Kafka consumer lag / streaming job metrics) in dashboard header.
Why this works:
- Streaming handles sub-minute urgency; pre-aggregations and tiered storage give analysts fast interactive queries at reasonable cost; explicit freshness metadata builds trust and surfaces pipeline issues quickly.
Propose a system to record data lineage so that every dashboard cell can be traced back to source rows and the SQL or pipeline that produced it. Describe metadata to store (dataset versions, query text, hashes), snapshotting strategy, and how to balance storage cost vs recomputation for reproducibility.
Sample Answer
Requirements:
- Trace each dashboard cell to source rows, SQL/pipeline code, and dataset versions.
- Reproducible: rerun pipeline to get same results or point to stored snapshot.
- Scalable and cost-aware (hot vs cold storage).
- Usable by analysts (searchable lineage UI, explainability).
High-level design:
- Lineage Service (metadata store + API) + Snapshot Store (object storage) + Index/Search + Orchestrator hooks (ETL/SQL runners) + Dashboard integration.
Metadata to store (per dataset / job / cell):
- DatasetVersion: id, source URI(s), ingestion timestamp, schema (column hashes), row-count, partition keys.
- QueryRecord: id, author, full SQL/pipeline script, parameters, execution timestamp, upstream DatasetVersion ids, DAG edges.
- RowFingerprint index: for sampled rows store content hash (e.g., SHA256) and primary key -> links to DatasetVersion.
- CellProvenance: dashboard_id, cell_id, QueryRecord id, output hash, aggregation definition, timestamp, pointers to contributing DatasetVersion ids and row-fingerprint ranges.
- Artifact hashes: commit hash for code, container image, config checksum.
- Metrics: latency, cost, data freshness.
Snapshotting strategy:
- Full snapshots for critical authoritative tables (daily) stored in object storage (columnar Parquet + manifest with DatasetVersion metadata).
- Incremental snapshots (change data capture) for high-volume tables with base snapshot + deltas.
- Sampling: keep deterministic sample snapshots (e.g., stratified hash-based sample) for quick row-level verification.
- Retention tiers: hot (recent full snapshots, fast access), warm (monthly), cold (archive) with lifecycle rules.
Balancing storage vs recomputation:
- Classify datasets by SLA/importance (gold/silver/bronze). Gold: store full snapshots; Silver: store incremental + metadata to recompute; Bronze: store only metadata + code to recompute on demand.
- Use content hashes + cached materialized views to reuse results when inputs unchanged.
- Store compact fingerprints for rows to prove origin without full payload; fetch full snapshot only when requested.
- Provide on-demand recompute: if snapshot expired, orchestrator re-runs QueryRecord on that exact DatasetVersion (using stored code, container, and parameterized inputs). Record recompute results and link as new provenance event.
Operational concerns:
- Ensure immutability: DatasetVersions and QueryRecords are append-only.
- Security/Audit: RBAC, immutable audit logs.
- Performance: index by DatasetVersion id, row hash, and cell id; allow sampling-based lineage to reduce cost for large joins.
- UX: UI to show lineage graph, "show source rows" which either retrieves stored rows, reconstructs from snapshot, or triggers recompute (with estimated cost and ETA).
Example flow:
- Analyst creates dashboard cell with SQL -> runner stores QueryRecord, links to latest DatasetVersions, computes output and output-hash, stores CellProvenance. If downstream user clicks "trace to rows", system uses row-fingerprint map to return matching source rows or triggers recompute using stored SQL and DatasetVersion pointers.
This design gives auditors cryptographic proof (hashes + immutable metadata) while tuning storage/recompute per dataset importance to control cost.
You need to compute daily unique users from event logs. Describe two approaches: (1) a SQL-based approach using window functions/aggregation and (2) a MapReduce/batch approach for very large data. For each approach, explain time and space complexity, where bottlenecks occur, and the communication points you would use to justify the chosen approach to engineering and product stakeholders.
Sample Answer
Approach 1 — SQL (single-cluster / analytical DB)
- Method: Use GROUP BY or window functions on event_time truncated to date.
Example:
SELECT event_date, COUNT(DISTINCT user_id) AS dau
FROM events
WHERE event_time BETWEEN '2025-01-01' AND '2025-01-31'
GROUP BY event_date
ORDER BY event_date;
Or for sessions per day using windowing:
ROW_NUMBER() OVER (PARTITION BY user_id, event_date ORDER BY event_time) ...
- Complexity: Time O(N) scanning events; DISTINCT may add hashing cost O(N) extra. Space: O(U) in-memory hash per date (U = unique users per day).
- Bottlenecks: DISTINCT/group-by memory pressure, single-node query planner limits, heavy I/O if table not partitioned/indexed.
- Communication points: Fast to implement, low operational overhead, good for daily reports and ad-hoc slices. Recommend partitioning by date and indexes to speed queries. Share SLA: near-real-time (minutes) for small-to-medium volumes.
Approach 2 — MapReduce / Batch (very large scale)
- Method: Map by (date,user_id) emit 1; Reduce deduplicates per key and sums per date. Typical two-step: map -> dedupe per mapper (local combiner) -> shuffle -> reducer aggregates counts by date.
- Complexity: Time O(N) across cluster; network/shuffle cost O(M) where M ≈ distinct (date,user) pairs. Space: per-worker memory O(local uniques); overall disk/network proportional to M.
- Bottlenecks: Shuffle/network IO and skewed keys (popular dates/users); reducer hotspots if not partitioned well.
- Communication points: Scales to petabytes, predictable cost per batch (hourly/daily). Justify for high-volume pipelines where SQL cluster cannot hold state or when query would time out. Explain trade-offs: higher latency (batch hours), operational complexity, but lower per-query memory pressure and horizontal scalability. Recommend combiners, proper partitioning, and handling skew (salting).
Recommendation to stakeholders
- If daily event volume fits warehouse (tables partitioned, <~hundreds of millions rows/day): use SQL for speed, easier ownership, and interactive exploration.
- If volume is massive, or you need repeatable, auditable nightly pipelines at scale: use MapReduce/batch (or Spark), emphasizing cost, latency, and reliability trade-offs.
- Present metrics: expected rows/day, query latency targets, cost estimates, and maintenance effort to align engineering (ops complexity) and product (freshness and accuracy) expectations.
Design an end-to-end analytics pipeline to measure experiments in near real-time for a product that generates 100M events per day. Include event collection, identity stitching, bucketing consistency, streaming vs batch ETL, data warehouse design, and how you'd ensure experiment assignment and metric calculation are reproducible and auditable.
Sample Answer
Requirements & constraints:
- 100M events/day (~1.2k/s avg, peaks higher), near real-time experiment reporting (minutes), reproducibility, auditable assignments and metrics.
High-level architecture:
- Event producers -> front/back-end SDKs -> streaming layer (Kafka/PubSub) -> stream processing (Flink/Beam/KStream) -> materialized aggregates + event lake -> data warehouse (Snowflake/BigQuery) + serving tables -> BI/analyst queries & dashboards.
Event collection:
- Lightweight JSON/AVRO events from SDKs with schema version, timestamp (client/server), event_id, user_ids (user_id, anon_id), device, experiment_context (optional), and signature for integrity.
- Ingest to Kafka with partitioning by event time bucket or user_id hash to keep ordering.
Identity stitching:
- Maintain a deterministic identity service in stream processor: use deterministic resolver that prefers stable IDs (user_id > login_id > cookie > device). Emit a canonical_id with provenance fields (which ids used, timestamps). Persist identity map writes to a low-latency KV store (Cassandra/Redis) for lookups and to the event-lake for audit trail. All resolution decisions are logged.
Bucketing consistency & experiment assignment:
- Calculate bucketing deterministically in the streaming layer using experiment_id, user_id canonical_id, start_time, and a stable hash function + config (seed, allocation). Store assignment records to a assignments topic and write to assignments table in warehouse (append-only) containing assignment_id, canonical_id, experiment_id, variant, config_version, timestamp, and signature.
- For deterministic repeatability, store experiment config/version in a config service and snapshot configs alongside assignments.
Streaming vs batch ETL:
- Streaming (Flink/Beam) for: identity stitching, deterministic bucketing, real-time metric increments, and writing to materialized views (e.g., metrics topic, OLAP table via Kafka connector).
- Batch for: reprocessing historical events (schema migrations, bug fixes), heavy aggregations, and complex baseline analyses. Keep raw event lake (Parquet on S3/GCS) as single source of truth.
Data warehouse design:
- Raw_events (immutable): event_id, canonical_id, raw_payload, ingestion_ts, event_ts, schema_version
- Assignments: assignment_id, canonical_id, experiment_id, variant, config_version, assigned_ts
- Metrics_facts (near real-time): date_hour, experiment_id, variant, metric_name, count, sum, unique_users_estimated (HyperLogLog) and exact_user_list when needed (for audits)
- Dimension tables: experiments (versioned), users (canonical), events_meta
- Use partitioning by date and clustering by experiment_id/variant to optimize queries.
Reproducible & auditable assignment and metric calculation:
- Immutable event lake + append-only assignments table with config_version links allow exact replay.
- Log experiment configs and seeding rules in a config store; every change gets a versioned snapshot.
- Store deterministic bucketing code in a version-controlled repo and tag deployed versions; include code version in assignment records.
- Support full replay: re-run streaming logic in batch over raw_events with the same config_version/code to reproduce results; record checksum of outputs.
- Maintain lineage metadata for every aggregated metric (SQL or DAG) that traces to raw_events and assignment_id ranges.
Monitoring & validation:
- Deploy unit/integration tests for bucketing determinism, identity merges, and metric aggregations.
- Shadow buckets and canary runs for new experiment logic.
- Drift alerts: compare streaming aggregates vs periodic batch aggregates (daily) and alert on >X% divergence.
- Sampling + store golden sample events for manual audit.
Operational notes for a data analyst:
- Provide pre-built SQL views that join assignments -> raw_events -> metrics_facts to answer "What users in variant A did X in timeframe T".
- Surface experiment meta (config_version, assignment_time) in dashboards.
- Offer reproducible notebooks that accept config_version and date range to rerun metrics from raw_events for audit.
Trade-offs:
- Streaming gives minutes-latency; batch ensures correctness for reprocessing. Keeping raw immutable lets analysts re-run and validate.
- HLL for scalable uniques, but store exact lists for small experiments/audits.
This design balances near-real-time visibility with reproducibility and auditability needed for trustworthy experiment analysis.
Unlock Full Question Bank
Get access to all 14 Working with Large-Scale Datasets interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.