DOCUMENT / LOG-20260703-MODERN-DATA-
The Modern Data Stack for Systems Engineers
A practical map of warehouses, data lakes, lakehouses, Parquet, Iceberg, Spark, Trino, Airflow, security, and monitoring for engineers who already understand infrastructure.
CONTENTS32
If you already understand servers, storage, jobs, schedulers, permissions, and failure recovery, most of the modern data stack is familiar machinery with unfamiliar names.
The confusing part is not usually the individual tools.
It is understanding which responsibility belongs to which layer.
A typical architecture diagram may contain:
Postgres
Kafka
S3
Parquet
Iceberg
a catalog
Spark
Trino
Airflow
Ranger
Prometheus
These are often drawn beside one another as if they are interchangeable products in one category.
They are not.
Some store data. Some describe how data is encoded. Some manage files as tables. Some execute computation. Some schedule work. Some control access. Some monitor the platform.
This guide builds a practical map of those responsibilities.
The goal is not to teach every tool. It is to make terms such as warehouse, data lake, lakehouse, Parquet, Iceberg, catalog, Spark, Trino, orchestration, and policy enforcement fit into one understandable system.
The most important principle is:
Use the smallest system that can complete the workload reliably, within the required time, and at acceptable cost.
The map in five minutes
Imagine an application where users submit computational jobs.
Postgres stores the operational state:
users
projects
analyses
applications
instance_types
billing_records
The application uses this database to:
- create jobs;
- update job status;
- record start and end times;
- calculate cost;
- check permissions;
- show users the current state.
Now management asks for a dashboard:
- How many jobs ran each day?
- What percentage failed?
- Which applications are slowest?
- Which instance types cost the most?
- How many users were active?
- Was the daily report ready by 06:00?
At first, this sounds like a SQL problem.
It quickly becomes an architecture problem.
Large historical queries may compete with application traffic. The data may need to be stored in a cheaper analytical format. Multiple jobs may need to transform it. Analysts may need stable table names instead of raw object paths. Different teams may require different access. Operators must know whether the report is late, incomplete, or wrong.
A simple analytical path might look like this:
Postgres
|
| export or CDC
v
Parquet on S3
|
| transform
v
curated analytical tables
|
| query
v
dashboard
Several other responsibilities span that path:
Airflow or another orchestrator
-> coordinates when work runs
Ranger, IAM, or native permissions
-> controls who may access what
monitoring and observability
-> show whether services and pipelines are healthy
data-quality checks
-> verify that the result is usable
catalog and lineage
-> explain what tables exist and where they came from
The complete mental model is:
sources
-> record operational events
ingestion
-> moves data
storage
-> keeps durable bytes
file formats
-> represent values inside files
table formats
-> organize files as reliable tables
catalogs
-> name and locate those tables
engines
-> transform and query data
resource managers
-> allocate machines and containers
orchestrators
-> coordinate work
policy systems
-> decide who may perform which actions
enforcement points
-> apply those decisions
monitoring
-> detects health, progress, and delay
quality checks
-> validate the result
owners
-> remain accountable
Everything else in this guide expands that map.
Warehouse, data lake, and lakehouse
These terms describe different ways to organize analytical data and computation.
They are not three versions of the same product.
Start with OLTP and OLAP
Before choosing an architecture, separate operational work from analytical work.
An operational database handles current application activity:
create a job
update its status
record a payment
change a permission
look up one analysis
This is usually called online transaction processing, or OLTP.
An OLTP system commonly needs:
- small reads and writes;
- low latency;
- indexes;
- frequent updates;
- strong transactional correctness;
- high application concurrency.
A typical query looks like:
SELECT status
FROM analyses
WHERE analysis_id = 'analysis-123';
Analytical systems handle broader historical questions:
failure rate by application version
monthly cost by project
average runtime by instance type
active users by week
This is usually called online analytical processing, or OLAP.
An OLAP system commonly needs:
- large scans;
- aggregations;
- joins;
- historical data;
- column-oriented storage;
- high throughput;
- analytical concurrency.
A typical query looks like:
SELECT
instance_type,
count(*) AS jobs,
avg(duration_seconds) AS average_duration
FROM analysis_history
WHERE started_at >= DATE '2026-01-01'
GROUP BY instance_type;
Both systems may support SQL.
That does not mean they are designed for the same workload.
SQL is a language, not an architecture.
What is a data warehouse?
A data warehouse is a managed analytical system where data is presented as queryable tables.
A warehouse commonly provides:
- managed storage;
- SQL execution;
- table management;
- query optimization;
- concurrency;
- access control;
- backup and operational tooling.
A warehouse is attractive when the main priorities are:
- business reporting;
- managed analytical SQL;
- predictable operations;
- fewer components to integrate;
- limited platform-engineering effort.
The main benefit is integration.
Storage, compute, table metadata, permissions, and query operations are usually delivered as one managed platform.
That can reduce architectural flexibility, but it also removes a large amount of operational work.
What is a data lake?
A data lake is a repository of files on scalable storage.
It may contain:
- Parquet;
- CSV;
- JSON;
- logs;
- images;
- model artifacts;
- BAM and VCF files;
- application exports.
The lake separates durable storage from one particular compute engine.
Different tools may read the same underlying objects.
This flexibility is useful when:
- data must be retained cheaply;
- several formats are involved;
- multiple engines need access;
- raw history should be preserved;
- storage must scale independently from compute.
A data lake does not automatically provide:
- reliable tables;
- a current schema;
- safe concurrent updates;
- quality checks;
- ownership;
- access policies;
- a query engine.
A bucket full of unexplained files is not a healthy data platform.
The problem is not the lake.
The problem is unmanaged data.
What is a lakehouse?
A lakehouse is an architectural pattern that adds warehouse-like table management to data stored in a lake.
A simplified lakehouse might use:
S3
-> object storage
Parquet
-> analytical data files
Iceberg
-> table metadata and snapshots
catalog
-> stable table names and metadata locations
Spark
-> large transformations
Trino
-> interactive SQL
No single line is the lakehouse.
The architecture is the combination.
A lakehouse can be useful when an organization wants:
- open storage;
- independent compute engines;
- large shared analytical tables;
- stronger update and snapshot semantics;
- fewer dependencies on one warehouse vendor.
It is not automatically simpler than a warehouse.
Each independent layer must be integrated, upgraded, secured, monitored, and owned.
How S3, Parquet, Iceberg, and a catalog fit together
This is one of the most important distinctions in the modern data stack.
S3 stores objects
S3 and similar object stores keep durable objects addressed by keys:
s3://company-data/analysis-events/day=2026-07-23/part-0001.parquet
Object storage knows that an object exists.
It does not understand that several objects form one analytical table.
Parquet describes data inside a file
Parquet is a column-oriented file format.
It supports:
- typed values;
- compression;
- column pruning;
- row-group statistics;
- efficient analytical scans.
Suppose a table has 100 columns but a query needs only four.
A Parquet reader may avoid reading the other 96 columns.
Parquet is not a database.
It is a way to encode tabular data inside files.
The boundary is:
S3
-> stores the object
Parquet
-> defines the structure inside the object
Iceberg manages many files as one table
A Parquet file does not know:
- which other files belong to the same table;
- which schema is current;
- which files were removed;
- whether a multi-file write completed;
- which version readers should see.
A table format such as Apache Iceberg manages a collection of files as one logical table.
It may track:
- schemas;
- partitions;
- snapshots;
- data files;
- delete files;
- committed metadata versions.
Conceptually:
Iceberg metadata
|
+---- Parquet file 1
+---- Parquet file 2
+---- Parquet file 3
Iceberg does not replace Parquet.
It organizes Parquet files into a more reliable table.
A catalog helps engines find the table
A catalog maps a stable table name to the current table metadata.
Instead of querying:
s3://company-data/warehouse/analysis-events/
a user may query:
SELECT *
FROM analytics.analysis_events;
The catalog resolves:
analytics.analysis_events
|
v
current Iceberg metadata
|
v
Parquet data files
A catalog may manage:
- table names;
- namespaces;
- schemas;
- metadata locations;
- properties;
- ownership fields.
A broader data catalog may also provide:
- search;
- descriptions;
- tags;
- lineage;
- quality indicators;
- business terminology.
But a catalog is not automatically a policy engine.
A catalog may label a column as sensitive.
An enforcement point must still block, mask, or filter access.
The compact distinction
warehouse
-> integrated managed analytical system
data lake
-> files on scalable storage
lakehouse
-> lake storage plus stronger table management
S3
-> stores objects
Parquet
-> represents analytical values inside files
Iceberg
-> manages files as a table
catalog
-> names and locates the table
That is the core storage map.
How data moves and gets computed
Once the storage layers are clear, the next question is how data enters the platform and how work is executed.
Ingestion, transformation, and serving
Most data pipelines contain three broad activities.
Ingestion
Ingestion moves data from a source into the analytical platform.
Examples:
- export a database table;
- capture database changes;
- receive events;
- upload files;
- call an API;
- copy logs.
For the running example:
Postgres analyses table
|
v
daily Parquet export
or:
Postgres changes
|
v
CDC connector
|
v
object storage
Transformation
Transformation converts source data into a more useful form.
Examples:
- cast types;
- normalize timestamps;
- remove duplicates;
- join reference data;
- apply business rules;
- calculate cost;
- aggregate jobs by day.
For example:
SELECT
date_trunc('day', started_at) AS day,
application_id,
count(*) AS jobs,
count(*) FILTER (WHERE status = 'failed') AS failed_jobs,
avg(duration_seconds) AS average_duration,
sum(cost) AS total_cost
FROM analysis_events
GROUP BY 1, 2;
Serving
Serving exposes the result to a consumer.
Examples:
- dashboard table;
- SQL view;
- API database;
- Parquet export;
- search index.
The ingestion tool, transformation engine, and serving system do not need to be the same product.
Batch, streaming, and CDC
Batch processing handles a bounded input:
read yesterday's records
transform them
write the result
stop
Streaming handles a continuing input:
read events
update state
emit results
continue
Change data capture, or CDC, records changes from a source database:
insert
update
delete
CDC is a movement mechanism.
It does not automatically mean that the entire downstream platform must be a real-time streaming system.
For example:
Postgres changes
|
v
CDC connector
|
v
object storage
|
v
hourly batch merge
The source mechanism and the processing model are separate decisions.
ETL and ELT
ETL means:
Extract -> Transform -> Load
The data is transformed before reaching the analytical destination.
ELT means:
Extract -> Load -> Transform
Raw or lightly processed data is loaded first, then transformed inside the analytical platform.
ETL can make sense when:
- sensitive fields must be removed before loading;
- strict validation is required at the boundary;
- the destination cannot read the source format;
- network transfer should be reduced.
ELT can make sense when:
- raw history should be preserved;
- transformations change frequently;
- several downstream models share the same source;
- the analytical platform has strong compute.
Real systems often use both.
The acronym should follow the requirement, not the other way around.
Choose the execution model by workload shape
The right engine depends on what the work must do.
One machine: DuckDB or Polars
Start with one machine when the workload is a single analytical operation such as:
read Parquet
filter records
join a moderate reference table
group by project
write a summary
DuckDB and Polars can process substantial analytical workloads on one machine.
They may support:
- column pruning;
- filter pushdown;
- streaming execution;
- disk spill;
- parallel processing.
A dataset larger than memory does not automatically require a cluster.
The important question is whether the workload can complete on one machine within the required time and cost.
Many independent tasks: use a batch scheduler
Suppose the workload looks like:
sample_00001.bam -> collect metrics -> sample_00001.json
sample_00002.bam -> collect metrics -> sample_00002.json
sample_00003.bam -> collect metrics -> sample_00003.json
Each task owns its input and output.
One task does not need data from another task.
Use:
- a process pool;
- Slurm arrays;
- cloud batch;
- Kubernetes Jobs;
- a workflow engine.
Do not use Spark merely to launch many unrelated command-line tools.
Spark is a distributed data engine.
It is not a universal batch scheduler.
Coordinated distributed transformation: Spark
Spark becomes useful when one logical operation requires coordinated work across machines.
Examples:
- distributed joins;
- group-by across partitions;
- large shuffles;
- coordinated writes;
- task-level retries.
The important signal is not:
many files
It is:
one operation requires data to move and combine across workers
That is the workload shape Spark was designed to handle.
Interactive distributed SQL: Trino
Trino is useful when many users need interactive SQL over distributed data sources.
It may query:
- object-storage tables;
- relational databases;
- metadata catalogs;
- other analytical systems.
A practical division is:
Spark
-> build and transform large datasets
Trino
-> query those datasets interactively
Postgres
-> run transactional application workloads
These systems can coexist because they own different responsibilities.
Scale the machine before the architecture
Before introducing distributed processing, ask:
- Are we using a columnar format?
- Are we reading only required columns?
- Are filters pushed down?
- Can the engine stream or spill?
- Is the query plan reasonable?
- Is local storage fast enough?
- Are there millions of tiny files?
- Would a larger machine solve the problem more cheaply?
A distributed system adds:
- network communication;
- serialization;
- worker startup;
- remote scheduling;
- partial failures;
- retries;
- distributed logs;
- more operational dependencies.
Sometimes that cost is necessary.
Often it is not.
Coordination, security, and monitoring
A working data path still needs coordination, access control, and operational visibility.
These responsibilities should not be hidden inside one vague word such as governance.
Orchestration is not execution
An orchestrator answers:
- What runs first?
- What depends on what?
- When should it run?
- What happens after failure?
- How are retries and backfills handled?
Examples include Airflow, Dagster, Prefect, and Argo Workflows.
For the running example:
export_postgres
|
v
validate_raw
|
v
build_daily_metrics
|
v
run_quality_checks
|
v
publish_dashboard
Airflow coordinates those steps.
It does not replace the engine that performs the transformation.
One Airflow task may launch DuckDB, Spark, dbt, or another program.
Similarly:
Airflow
-> coordinates work
Spark
-> executes distributed transformations
Kubernetes, YARN, or Slurm
-> allocates resources
S3
-> stores durable data
Keeping those boundaries clear prevents category mistakes.
Security is a request flow
Suppose Alice runs:
SELECT *
FROM finance.employee_salary;
Several responsibilities are involved.
Authentication
Authentication answers:
Who is the requester?
Examples include:
- OIDC;
- SAML;
- Kerberos;
- LDAP-backed login;
- cloud identity;
- service credentials.
Authentication establishes that the requester is Alice.
It does not automatically decide what Alice may access.
Authorization
Authorization answers:
What may Alice do?
For example:
Alice may:
SELECT job status
SELECT application name
SELECT aggregated cost
Alice may not:
SELECT user email
SELECT individual billing records
UPDATE analytical tables
Authorization may apply to:
- catalogs;
- schemas;
- tables;
- columns;
- rows;
- topics;
- object paths;
- administrative actions.
Enforcement
A policy matters only when a component enforces it.
The enforcement point may be:
- the database;
- the query engine;
- the object store;
- the message broker;
- the API gateway;
- the application.
It may:
- allow the request;
- deny the request;
- mask a column;
- filter rows;
- restrict an operation.
Audit
Audit records answer:
- Who attempted the action?
- Which resource was involved?
- Was access allowed or denied?
- Which policy produced the decision?
- When did it happen?
An audit record is not the same as a normal application error log.
Where Apache Ranger fits
Apache Ranger is one example of a centralized policy and auditing layer for supported data services.
Conceptually:
identity
|
v
request to Trino, Hive, Kafka, or another service
|
v
Ranger-integrated enforcement point
|
+---- allow
+---- deny
+---- row filter
+---- column mask
|
v
security audit event
A useful mental model is:
Ranger Admin
-> policy control plane
Ranger plugin or integration
-> enforcement point
Ranger audit
-> security decision record
Ranger is not:
- the identity provider;
- object storage;
- a query engine;
- a table format;
- a general metadata catalog;
- a monitoring platform.
Many platforms use native warehouse permissions, cloud IAM, managed governance services, or engine-specific controls instead.
Ranger is an example of the layer, not a universal requirement.
Governance is not enforcement
A catalog may label a field as:
classification: personal_data
A policy document may state:
Only support managers may access personal data.
Neither statement alone stops a query.
Governance becomes operational when:
- identity is established;
- policy is evaluated;
- the request is enforced;
- the decision is audited;
- someone owns the policy.
The useful systems question is:
Where is this rule actually enforced?
Monitoring is a cross-cutting plane
Monitoring does not sit at the end of the pipeline.
Every component must expose signals.
source
-> ingestion
-> storage
-> processing
-> serving
-> consumer
^
|
metrics, logs, events, traces, checks, and alerts
from every layer
A practical distinction is:
Monitoring
-> Are known things behaving as expected?
Observability
-> Do we have enough evidence to investigate
unexpected behaviour?
Monitoring may detect that a daily report is late.
Observability helps explain whether the delay came from ingestion lag, a slow transformation, a catalog outage, or an output commit failure.
Monitor more than infrastructure
Traditional infrastructure monitoring focuses on:
- CPU;
- memory;
- disk;
- network;
- process availability.
A data platform needs those signals, but it also needs:
service health
pipeline progress
data freshness
data validity
consumer outcome
For the running example:
Infrastructure health
-> Are the workers and storage systems healthy?
Service health
-> Are Airflow, Spark, Trino, and the catalog functioning?
Pipeline health
-> Did the workflow start, progress, and complete?
Data health
-> Is the output fresh, complete, and valid?
Consumer outcome
-> Was the dashboard ready by 06:00?
These layers are not interchangeable.
A pipeline can be technically successful and still produce unusable data:
Airflow task: succeeded
Spark job: succeeded
output table: created
row count: 0
dashboard: wrong
That is why process monitoring does not replace data-quality checks.
Progress is a first-class signal
A process may be alive but stuck.
For example:
process exists yes
CPU usage 2%
memory usage stable
errors none
records processed unchanged for 45 minutes
latest output partition six hours old
Useful progress indicators include:
- records processed;
- bytes processed;
- latest source offset;
- latest event timestamp;
- current stage;
- completed tasks versus total tasks;
- latest committed partition;
- last successful checkpoint.
For data pipelines, progress and freshness are often more important than raw host utilization.
Alert on outcomes that require action
A useful rule is:
Alert when someone needs to act.
Good alerts include:
- The 06:00 report will miss its deadline.
- The ingestion backlog exceeds the recovery window.
- The latest published partition is too old.
- Automated retries are exhausted.
- The output row count is far below normal.
- Storage will fill before the team can respond.
CPU spikes, individual task retries, and temporary latency increases may still be useful diagnostic metrics.
They are not always paging conditions.
The strongest operational path is:
consumer outcome
-> pipeline
-> task
-> service
-> infrastructure
Do not force operators to inspect CPU graphs to discover that a business report is missing.
When the stack should grow
A modern data platform should grow by encountering real limits, not by copying a reference architecture.
Stage 1: query the operational database
The smallest system may be:
Postgres
|
v
report
This is acceptable when:
- data volume is small;
- analytical queries are infrequent;
- application performance is unaffected;
- the query is easy to maintain.
Do not create a lakehouse merely because the report is analytical.
Stage 2: separate historical analysis
A limitation appears:
Large scans compete with application traffic.
The design becomes:
Postgres
|
| nightly export
v
Parquet on S3
|
v
DuckDB
|
v
report
Now the analytical workload has independent storage and compute.
Stage 3: add stronger table management
Another limitation appears:
Several jobs update the same dataset.
Consumers need one consistent current version.
Schema and partition changes are difficult to manage.
The design becomes:
S3
|
Parquet files
|
Iceberg table
|
catalog
This is where a table format and catalog become useful.
Not because the data is fashionable.
Because directories of files no longer provide a sufficient table and commit model.
Stage 4: add distributed computation
Another limitation appears:
A representative transformation cannot meet its deadline
on one machine, even after improving the data layout and query plan.
Now Spark may be justified.
The important evidence is:
- one logical operation needs coordinated work;
- data must move across workers;
- one machine cannot meet the requirement;
- the additional operational cost is acceptable.
Stage 5: add interactive distributed SQL
Another requirement appears:
Many analysts need interactive SQL across shared tables and sources.
Now Trino or a warehouse may be justified.
This is a serving and query-concurrency requirement, not merely a transformation requirement.
Stage 6: add centralized policy controls
Another requirement appears:
Different teams need different table, column, and row access.
Policies must be applied consistently and audited.
Native engine permissions may be enough at first.
A centralized policy system such as Ranger becomes useful when:
- policies span several integrated services;
- masking or row filtering is required;
- audit needs are stronger;
- centralized policy ownership reduces operational inconsistency.
Stage 7: add outcome-oriented monitoring
Another failure appears:
The workflow is running, but nobody notices that the report is six hours late.
Now the platform needs:
- freshness metrics;
- progress metrics;
- outcome dashboards;
- actionable alerts;
- named owners;
- runbooks.
This is not merely “install Prometheus.”
The design must first decide what success means.
For example:
SLI
-> percentage of daily reports available by 06:00
SLO
-> 99% per month
alert
-> projected completion is later than 06:00
owner
-> data-platform on-call
The architecture should become more complex only when each new layer solves a real operating problem.
The final responsibility map
The modern data stack becomes easier to understand when every term is reduced to the responsibility it owns.
operational database
-> runs the current application state
warehouse
-> provides an integrated analytical system
data lake
-> stores open files on scalable storage
lakehouse
-> adds stronger table management to lake storage
object storage
-> stores durable objects
Parquet
-> represents analytical data inside files
Iceberg, Delta Lake, or Hudi
-> manage files as reliable tables
catalog
-> names and locates tables
ingestion
-> moves source data into the platform
DuckDB or Polars
-> execute analytical work on one machine
batch scheduler
-> runs many independent jobs
Spark
-> executes coordinated distributed transformations
Trino
-> provides interactive distributed SQL
Airflow or another orchestrator
-> coordinates schedules and dependencies
Kubernetes, YARN, Slurm, or cloud batch
-> allocate resources
identity provider
-> establishes who the requester is
policy system
-> decides what the requester may do
enforcement point
-> applies the decision
audit system
-> records access decisions
monitoring
-> detects whether known expectations are being met
observability
-> provides evidence for investigating unexpected behaviour
data-quality checks
-> determine whether the result is valid
catalog and lineage tools
-> explain what data exists and where it came from
owners
-> remain accountable for policies, pipelines, and datasets
The most common mistakes come from confusing those boundaries:
Parquet is not a database.
Iceberg is not a replacement for Parquet.
A catalog is not automatically a policy engine.
Ranger is not an identity provider.
Airflow is not a processing engine.
Kubernetes is not a data engine.
Monitoring a process is not the same as validating its output.
A data lake is not automatically a lakehouse.
A lakehouse is not one product.
The practical decision is not:
Which modern stack should we install?
It is:
What problem exists now?
Which layer owns that problem?
Can the current system solve it more simply?
What operational cost will the new layer introduce?
How will we secure, monitor, recover, and own it?
Use the smallest system that satisfies the real requirement.
Scale the machine before scaling the architecture.
Add a platform layer only when the workload or operating model—not fashion—requires it.