Databricks Certified Associate Developer for Apache Spark Study Guide¶
Independent AI-assisted resource — SOURCES + OBJECTIVES CHECKED; HUMAN REVIEW PENDING. Objective coverage, citations, volatility labels, links, and exam-integrity compliance were checked on October 28, 2026. This is not a guarantee that the guide is error-free or current after that date. See the sources-and-objectives record. The official certification page and its linked exam guide are authoritative.
Study guide at a glance
Start here
Prerequisites: No formal prerequisite; the official guide recommends related training and six months of hands-on Apache Spark experience. Practice Python/SQL, driver/executor and job/stage/task execution, schemas and I/O, DataFrame operations, joins/aggregates/UDFs, partitions/shuffles/AQE/UI/log diagnosis, Structured Streaming recovery/state, Spark Connect and Pandas API on Spark.
Official objective domains
- See the objective map in this guide
The active-reading range uses 90–130 words per minute for technical material. Labs, troubleshooting, note-taking, spaced review, and prerequisite gaps add time; it is not a total preparation promise.
Library identifier: DATABRICKS-ASSOCIATE-DEVELOPER-APACHE-SPARK; Databricks does not publish a short exam code on the official page checked.
Current baseline: Detailed official guide for the live version as of October 30, 2025; live seven-domain weighted page checked September 28, 2026.
Upcoming blueprint change: None announced as of September 28, 2026. The current exam is not the retired “Apache Spark 3.0 — Python/Scala” exam. Recheck the official page two weeks before testing and reject material that still teaches the former 60-question, 120-minute, documentation-aided, language-specific format.
Lifecycle status: Active; valid for two years, with the currently live exam required for recertification. The earlier review recorded Databricks staff confirmation that the previous Apache Spark 3.0 credential retired in 2025; this guide targets the replacement certification, not a renewal or conversion claim. That community page blocked automated access on September 28; the current official page and linked guide establish the active exam baseline.
Assessment: 45 scored multiple-choice questions, 90 minutes, English, online or test-center delivery, no test aids—including API documentation—according to the detailed guide.
Prerequisite: None required. The official guide highly recommends related training and six months of hands-on Apache Spark experience. Basic Python, SQL, schemas/files, distributed-data concepts, and command-line/application troubleshooting are practical prerequisites.
Current guide watch — October 28, 2026¶
- Databricks' Academy catalog lists a September 7 course update to Apache Spark 4 with notebook-only lectures and Python examples. The public exam page still lists seven weighted domains, including Spark Connect and Pandas API on Spark; the October 2025 exam guide remains the exam contract. Use the new course as product practice, then verify any version-specific API against the official exam guide and its task wording. The exam language is Python. See the current Academy course listing and official certification page.
How to use this guide¶
Write and diagnose code without relying on API documentation during recall practice. For every operation, predict schema, partitioning, shuffle, laziness/action boundary, null/duplicate behavior, output mode, state, and likely Spark UI/log evidence before running it. Then execute the code on small edge-case fixtures and a larger skewed dataset.
source + explicit schema -> logical DataFrame/SQL transformations
-> analyzed/optimized physical plan -> jobs -> stages at shuffle boundaries
-> tasks per partition on executors -> action/output
-> Spark UI + driver/executor logs + metrics -> correctness/performance decision
The DataFrame/Dataset API is 30%, and architecture plus Spark SQL add 40%. Learn API syntax and the distributed execution it creates together; memorized methods without partition, shuffle, schema, and failure reasoning are fragile.
About related items: A
Related item:callout adds prerequisite, architectural, migration, security, operational, or adjacent context that makes an objective easier to understand. It is useful supporting knowledge, not a claim that the item appears verbatim in Databricks' published exam objectives.
Objective map¶
| Published domain | Weight | Evidence you should be able to produce |
|---|---|---|
| Apache Spark Architecture and Components | 20% | Driver/executor/cluster/resources, session and structured APIs, job-stage-task hierarchy, lazy transformations/actions, partitions/shuffles, caching/GC, fault tolerance, and modules. |
| Using Spark SQL | 20% | Schema-aware reads/writes, file/JDBC sources, direct file SQL, modes/partitioned output, tables and temporary views. |
| Developing Apache Spark DataFrame/Dataset API Applications | 30% | Column/row/schema transforms, nulls/dedup/validation, aggregates/dates, joins/unions, I/O, collection, UDF/state, shared variables and broadcast joins. |
| Troubleshooting and Tuning Apache Spark DataFrame API Applications | 10% | Repartition/coalesce/skew/shuffle choices, AQE, Spark UI and driver/executor log diagnosis. |
| Structured Streaming | 10% | Incremental/micro-batch model, fault tolerance, sources/sinks/output modes, windows/aggregates and watermark-aware deduplication. |
| Using Spark Connect to deploy applications | 5% | Spark Connect client/server boundary and local/client/cluster deployment-mode distinctions. |
| Using Pandas API on Apache Spark | 5% | When pandas-like distributed APIs help and how vectorized Pandas UDFs differ from ordinary Python UDFs. |
1. Apache Spark Architecture and Components (20%)¶
Understand where application code and work execute¶
The driver owns the SparkSession/SparkContext, constructs logical work, requests resources, schedules jobs/stages/tasks, and collects metadata/results. Executors run tasks on partitions, cache data, perform shuffle reads/writes, and return results/status. Worker nodes host executor processes under a cluster manager. CPU cores determine concurrent task slots; executor and overhead memory bound deserialization, execution, caching, shuffle, Python workers, and native/off-heap needs.
Spark helps process data larger than one machine through partitioned parallel work, fault recovery, a unified structured API, and an optimizer. Challenges include serialization/network/shuffle cost, skew/stragglers, many small files/tasks, memory pressure, distributed debugging, startup overhead, and algorithms that do not parallelize well. Distribution is not a speed guarantee for small data.
Deployment mode describes where the driver runs relative to the submitter/cluster. In client mode, the driver is in the submitting client process; losing that process can lose the application. In cluster mode, the cluster manager launches the driver inside the cluster. In local mode, driver and execution threads run in one process/machine for development/testing—there are not distributed executors on a separate cluster.
Follow the execution hierarchy and laziness¶
A Spark application contains jobs. An action such as count, collect, a write, or many display operations triggers a job for the lineage required. A shuffle boundary commonly separates stages. Each stage has tasks, generally one per input/shuffle partition for that stage. Multiple actions can recompute shared lineage unless cached/persisted and materialized.
Transformations return a new logical DataFrame and are lazy; they do not normally scan all data immediately. Narrow transformations can compute each output partition from a small number of input partitions. Wide operations—grouping, distinct, ordering, many joins, repartition—redistribute records and create shuffle boundaries. The optimizer may rewrite or eliminate operations, so use explain and the Spark UI rather than assuming one method equals one physical stage.
Fault tolerance comes primarily from immutable lineage/recomputation and persisted/checkpointed state where applicable. A failed task can retry; executor loss can invalidate cached/shuffle data. Driver failure is a different boundary and needs cluster/application restart/recovery configuration. “Spark is fault tolerant” does not mean external side effects or non-idempotent writes are automatically safe.
Reason about partitions, caching, storage, and memory¶
Partitions are the units of distributed data and task scheduling. Too few underuse cores and create large tasks; too many add scheduling/shuffle/file overhead. Input layout, source splitability, explicit repartitioning, shuffle configuration, AQE, and output partitioning all affect counts.
cache() is a convenience for a default persistence level; persist(level) selects memory/disk/serialization behavior. Caching is lazy until an action and benefits reused expensive lineage. It consumes executor storage memory, can evict other blocks, and can make a one-use pipeline slower. unpersist() when reuse ends. Garbage collection reclaims JVM objects, but repeated high GC time often signals oversized heaps/objects, excessive allocation, poor serialization, cache pressure, or too few/large partitions—not simply “add more memory.”
DataFrames represent distributed tabular data with named columns and schema. On the JVM a DataFrame is a Dataset[Row]; typed Datasets are a Scala/Java construct, while PySpark uses DataFrames. Structured APIs enable Catalyst analysis/optimization and efficient execution; RDDs expose lower-level objects/functions with less relational optimization.
Know the major modules: Core and scheduling; Spark SQL/DataFrames; Structured Streaming; Pandas API on Spark; MLlib; and current related libraries. The exam is Python/DataFrame focused, not an invitation to study every MLlib algorithm.
Related item: A SparkSession is the structured entry point; SparkContext is the lower-level connection to execution. Creating uncontrolled multiple contexts is not normal application isolation.
2. Using Spark SQL (20%)¶
Read with known schemas and explicit source options¶
Use spark.read.format(...).options(...).schema(...).load(...) or format conveniences. An explicit schema avoids inference cost and ambiguity, stabilizes downstream contracts, and defines types/nullability intent. Still validate corrupt/unexpected records, column presence, date/timestamp parsing, delimiters/headers/quotes, and source evolution.
File sources include Parquet, ORC, JSON, CSV, text and Delta when its connector/runtime is available. Columnar Parquet/ORC/Delta support column pruning and predicate pushdown better than row-oriented text formats. JSON/CSV are flexible interchange formats but require parsing and disciplined schemas. Text yields text records rather than magically parsing business fields.
JDBC reads need URL/table or query, driver/authentication, and optionally partition column/bounds/count or predicates for parallelism. Too many partitions can overwhelm the database; one unpartitioned read can bottleneck. Push filters/projections where supported and protect credentials. JDBC writes also require controlled partitions/batches/isolation and idempotent business behavior.
The JDBC reference makes a subtle distinction: lowerBound and upperBound set partition stride; they do not filter the source rows. If a migration must read only IDs 100–199, express that condition in an appropriate source query/subquery or filter and verify the plan/result. Bounds alone can still return IDs outside that range. The query option cannot be combined with partitionColumn; use an aliased subquery in dbtable where appropriate. numPartitions also bounds concurrent JDBC connections.
Spark SQL can query supported file paths directly using the provider syntax, but persistent tables/views provide reusable names, metadata and governance. Do not confuse file-level query ability with an automatically governed/optimized table lifecycle.
Write with deliberate mode, layout, and table semantics¶
Common DataFrame/SQL save modes are append, overwrite, error/error-if-exists, and ignore. Understand the entire destination behavior before using overwrite; partition overwrite semantics and transactional capabilities depend on source/table/provider/configuration. Append can duplicate data when a job retries without an idempotent key/process.
partitionBy creates directory/table partition layout by column on write; it is not the same as repartition, which changes in-memory execution partitions. Select low-to-moderate cardinality partition columns that support common pruning; overpartitioning creates tiny directories/files. Sorting within partitions can help locality/compression or downstream access, but global ordering normally shuffles and does not by itself create a guaranteed table read order.
Temporary views expose a DataFrame to SQL within the current session; global temporary views have broader application-session scope through their special database, not permanent catalog lifetime. A persistent table stores catalog metadata and optionally manages data lifecycle depending on table/provider/location. Track whether dropping the table removes data.
Use createOrReplaceTempView, spark.sql, DataFrame readers/writers and saveAsTable/SQL DDL/DML in practice. Validate final schema, row counts, partition/file layout and restart/session behavior—not only whether the cell ran.
Related item: SQL and the DataFrame API generally compile to the same structured engine. Choose the clearest interface and inspect the plan; neither is inherently faster for an equivalent optimized plan.
3. Developing DataFrame/Dataset API Applications (30%)¶
Manipulate columns and rows without losing semantics¶
Use column expressions, not Python scalar logic over distributed rows. select projects/reorders/aliases expressions; withColumn adds/replaces one named column; withColumnRenamed changes a name; drop removes columns; filter/where keeps rows matching three-valued SQL conditions. Remember null comparisons: use isNull/isNotNull or null-safe equality where required, not == None as ordinary business comparison.
Split strings with split, access array elements carefully, and explode arrays/maps into multiple rows. Check empty/null collection behavior and row multiplication. Prefer built-in functions because Spark can analyze/optimize/generate code around them; a Python UDF creates serialization and optimization boundaries.
For missing data, na.drop() with the default “any” removes a row containing a null in any considered column; “all,” thresholds and subsets change behavior. na.fill must respect column types. Validate required ranges, formats, reference values, and cross-field rules explicitly; null removal is not complete data validation.
Deduplicate by the right business key. distinct considers all columns; dropDuplicates(keys) retains one row per key without expressing which conflicting record wins. Use a window ordered by event/update time and a deterministic tiebreaker when “latest” matters.
Aggregate and handle dates deliberately¶
groupBy(...).agg(...) can calculate count, mean/avg, sums, min/max, exact distinct and approximate distinct. Exact distinct often needs substantial shuffle/state; approx_count_distinct trades controlled error for lower resource use. Know whether count includes nulls: count(*) counts rows while count(column) omits null values.
Use summary/describe for exploration, not as a substitute for business validation. Convert epoch seconds/milliseconds with the correct function/unit; parse strings with an explicit pattern where possible; extract year/month/day; and account for session time zone, daylight saving, invalid values and timestamp/date truncation.
VERIFY CURRENT — casts and defaults: Spark SQL migration notes document ANSI mode as the default since Spark 4.0. Malformed casts and overflow may now fail where older examples expected nulls or wraparound. Record spark.version, spark.sql.ansi.enabled and session time zone; use try_cast plus an explicit quarantine/count rule when tolerant parsing is intended. Turning ANSI off for the entire session is a behavior change, not a substitute for validating inputs. The exam guide does not pin every runtime default.
Combine DataFrames with correct key and row semantics¶
An inner join returns matching keys; a left outer join preserves left rows with null right fields where unmatched; cross joins form a Cartesian product and can explode. Multi-key joins require all intended predicates and disambiguated duplicate column names. Null keys do not normally match under ordinary equality.
A broadcast join sends a sufficiently small side to executors, avoiding a large two-sided shuffle. Use an explicit broadcast hint only when measured size and executor memory make it safe; broadcasting a misestimated large table can cause executor OOM. AQE may choose/change strategies from runtime statistics.
union/unionAll in DataFrame APIs preserve duplicates and normally align columns by position. unionByName aligns names and can optionally handle missing columns. Neither automatically deduplicates; apply distinct only when business semantics require it and accept its shuffle.
Execute an edge-case DataFrame fixture¶
Run this original fixture in an existing spark session on disposable local compute. Collections below are bounded to five input records. Predict the null and duplicate outcomes before running. The null reference explains why a filter retains only true conditions, while ordinary null comparisons are unknown.
from pyspark.sql import Window, functions as F
events = spark.createDataFrame([
(1, 1, "b", 10), (1, 2, "a", 20), (1, 2, "z", 30),
(2, 1, "a", None), (None, 1, "a", 4),
], "id INT, version INT, event_id STRING, qty INT")
totals = events.agg(F.count("*").alias("rows"), F.count("qty").alias("known")).first()
assert (totals.rows, totals.known) == (5, 4)
assert events.filter(F.col("qty") != 30).count() == 3
# Contract: version plus event_id uniquely orders conflicting records per ID.
order = Window.partitionBy("id").orderBy(F.desc("version"), F.desc("event_id"))
latest = events.withColumn("rn", F.row_number().over(order)).filter("rn = 1").drop("rn")
assert latest.filter("id = 1").first().qty == 30
dimension = spark.createDataFrame([(1, "A"), (2, "B")], "id INT, segment STRING")
assert dimension.groupBy("id").count().filter("count > 1").count() == 0
enriched = latest.join(F.broadcast(dimension), ["id"], "left").fillna({"segment": "UNKNOWN"})
assert enriched.count() == 3
assert enriched.filter("segment = 'UNKNOWN'").first().qty == 4
left = spark.createDataFrame([("east", "red")], ["region", "color"])
right = spark.createDataFrame([("blue", "west")], ["color", "region"])
assert {(r.region, r.color) for r in left.union(right).collect()} == {("east", "red"), ("blue", "west")}
assert {(r.region, r.color) for r in left.unionByName(right).collect()} == {("east", "red"), ("west", "blue")}
assert left.union(left).count() == 2
The union fixture exposes a silent error because both columns have compatible string types. unionByName fixes alignment, but still preserves duplicate rows. The latest-row contract also needs a defined null-key policy: all null IDs form one window group here. Quarantine missing business keys before this step when unrelated null-key events must remain separate. Do not use this fixture as permission to collapse them. If ordering keys tie with different payloads, add a business tiebreaker or reject the conflict.
Read/write, inspect, and collect safely¶
Always predict schema before/after casts, expressions, joins and unions. printSchema() prints the structure and returns no DataFrame. show() displays a limited representation. collect() brings every row to driver memory; use only for demonstrably small results. take, head, limit().collect(), iterators, or writing distributed output may reduce—but do not erase—driver/resource risks.
Sort with orderBy/sort and explicit ascending/descending/null ordering. A global sort creates an ordered result through shuffle but output file/partition observation still needs care. sortWithinPartitions does not establish global order.
Use UDFs and shared variables only when built-ins cannot express the work¶
Ordinary UDFs transform inputs per row/batch without cross-record state. Stateful streaming operators and their StateStore maintain key-specific state across batches/events and need timeout/watermark/checkpoint/recovery thinking; do not call an ordinary UDF “stateful” because its Python object has a mutable variable.
Broadcast variables distribute a read-only value efficiently to tasks and are distinct from broadcast joins, a physical join strategy. Accumulators support associative task-side additions visible to the driver, commonly for diagnostics; task retry/speculation can complicate exactly-once business counting, so do not use them as a transactional counter.
Prefer built-in Spark SQL functions, then higher-order functions, then Pandas UDFs/vectorized patterns, and use scalar Python UDFs only when needed. Test nulls, exceptions, type conversion and deterministic behavior.
Related item: DataFrames are immutable logical plans. Reassigning a Python variable to a transformed DataFrame does not mutate the earlier DataFrame or execute it.
4. Troubleshooting and Tuning DataFrame Applications (10%)¶
Tune the partition/shuffle problem you actually observe¶
repartition(n, columns...) reshuffles to create/rebalance partitions and can improve parallelism or key distribution before expensive/repeated work or output. coalesce(n) commonly reduces partitions with a narrower operation and is useful after filtering, but can create uneven/oversized partitions. Avoid coalesce(1) for substantial output.
Identify skew through task duration/input/shuffle distributions: a few tasks much larger/slower than peers, spills, GC or OOM. Mitigations include filtering earlier, better partition keys/counts, pre-aggregation, broadcast of a truly small side, AQE skew handling, salting heavy keys, and separate handling for known hot values. Each changes cost or semantics; measure it.
Reduce shuffle by projecting/filtering early, using correct join/aggregation strategy, avoiding needless global distinct/sort/repartition, and reusing materialized expensive lineage appropriately. Do not optimize by deleting a correctness-required shuffle.
Adaptive Query Execution uses runtime statistics to coalesce post-shuffle partitions, handle skew and change join strategies where enabled/supported. It improves plans within constraints; it cannot repair bad business keys, an unbounded collection, or corrupt source data.
Use plan, UI, logs, and metrics in order¶
Start with the exception and application/job/stage/task timeline. Use explain to inspect logical/physical plans, then Spark UI SQL/DAG/stages/tasks/executors/storage/environment tabs. Compare input/output, records, shuffle read/write, spill, task time distribution, locality, executor loss, memory/GC and failed retries.
Driver logs show scheduling/application/collection/driver OOM and application exceptions. Executor logs show task/UDF/native/Python worker failures and executor OOM; obtain them through the Spark UI or cluster manager/log delivery, not by guessing a local path on the driver. Preserve event logs/metrics for completed application investigation.
Driver OOM often follows collect, large result metadata, too many tasks/files, or driver-side objects. Executor OOM may follow oversized/skewed partitions, unsafe broadcast, cache pressure, Python/native overhead, or too much task concurrency. Cluster underutilization can reflect too few partitions, serial driver work, slow source, skew, blocked I/O, or resource allocation—not only small cluster size.
Related item: A faster job that silently drops late/duplicate records or changes join/output semantics is a regression. Tune against correctness assertions and repeatable workload evidence.
5. Structured Streaming (10%)¶
Think of a stream as an incrementally maintained table¶
Structured Streaming applies DataFrame/Dataset operations to an unbounded input and incrementally updates a result, commonly in micro-batches. A trigger controls processing cadence; it is not the event-time window. The engine tracks source progress and state through checkpoints/offset logs/commits. End-to-end exactly-once claims depend on replayable sources, deterministic processing, checkpoint integrity, and an idempotent or transactional sink; arbitrary external side effects can violate them.
Create a streaming DataFrame with readStream, define selection/filter/projection/window/aggregation/dedup transformations, and start a query with writeStream using format/sink, output mode, checkpoint location, trigger and query name/options. Treat checkpoint identity and query logic/source/sink compatibility carefully across changes.
Output modes:
- append emits final/new rows that will not be updated under the query semantics;
- update emits rows changed since the last trigger where supported;
- complete rewrites the entire result table for an aggregation and can be costly.
Support depends on transformation and sink. Choose from result semantics, not preference.
Bound state with event time and watermarks¶
Windowed aggregations group events by event-time windows; processing time is when the engine handles them. Late records arrive after their event time. A watermark expresses how far behind the maximum observed event time the engine may generally wait before evicting old state; it is not a guarantee that every later record is discarded at exactly one boundary.
Choose the deduplication operator and keys explicitly. A watermark declaration alone does not guarantee bounded key state for every dropDuplicates form. In the streaming reference, classic deduplication with event time in its key can expire that state, but two copies with different timestamps become different keys. For ID-based duplicates with timestamp variation, dropDuplicatesWithinWatermark (Spark 3.5+) requires a watermark and bounds deduplication by its delay. Choose a delay longer than the maximum timestamp separation of copies; this is not permanent business-key uniqueness.
# incoming is a streaming DataFrame with event_id and timestamp event_time.
within_delay = (
incoming.withWatermark("event_time", "10 minutes")
.dropDuplicatesWithinWatermark(["event_id"])
)
Test on-time copies with changed timestamps, a too-late event, advancing watermarks, retained state, and restart with the same checkpoint. Inspect actual progress; idle input does not make event-time watermarks advance with the clock. Keep durable business deduplication separate when required beyond the retention window.
The January 10, 2025 streaming-deduplication walkthrough by Databricks employee Murali Talluri is useful for comparing ID-only, ID-plus-time and within-watermark state. Reconcile its step-by-step timing with the current API and observed micro-batch progress; do not treat illustration timestamps as universal batch scheduling guarantees.
Related item: Stateful UDF/operator behavior, StateStore, watermark, checkpoint and sink idempotency form one recovery contract. Studying only the transformation method misses correctness.
VERIFY CURRENT — streaming AQE: upstream migration guidance adds AQE for stateless streaming workloads in Spark 4.1, with spark.sql.adaptive.streaming.stateless.enabled as its control. Do not generalize batch AQE or this stateless support to a windowed aggregation/deduplication query. For that stateful path, examine state partitioning, hot keys, input rates and the exact engine's supported optimizations. Compare results before adopting a runtime upgrade.
6. Spark Connect and Deployment Modes (5%)¶
Spark Connect separates a client from the remote Spark driver through a protocol based on unresolved logical plans and results. It enables thin clients, language/tool integration, remote session isolation and decoupled client/server upgrades within compatibility limits. Code relying on driver JVM internals, SparkContext/RDD access, local files or unsupported APIs may not work the same; check the current Spark Connect overview.
Do not conflate Connect with spark-submit deployment mode. Connect describes client-to-Spark-server interaction. Client, cluster and local describe where the application driver/execution lives. A Connect client can run outside the cluster while the Connect server/driver executes remotely; application resource/configuration and authentication still belong to the remote environment.
Test session configuration, artifacts/dependencies, version compatibility, authentication/network, plan serialization, result collection and disconnect/retry behavior. Keep transformations in supported DataFrame/SQL APIs for portability.
Related item: Moving computation to the cluster does not move local Python files, environment variables or credentials automatically. Make every dependency and data location explicit.
7. Pandas API on Spark (5%)¶
Pandas API on Spark offers a pandas-like API backed by distributed Spark execution. It helps pandas users scale familiar transformations without collecting all data to one process, while retaining Spark plans/partitions/shuffles. It is not identical pandas: default indexes, ordering, type behavior, supported APIs and expensive global operations matter. Inspect execution and avoid conversions to pandas unless the result fits driver memory.
A Pandas UDF exchanges vectorized batches through Arrow and applies pandas operations to Series/DataFrame batches or grouped/iterator forms. Define Python type hints/return schema correctly and handle nulls/batch boundaries. Vectorization can outperform row-wise Python UDFs, but built-in Spark functions usually provide the best optimizer visibility and least serialization.
Pandas API on Spark is a high-level DataFrame API; a Pandas UDF is an extension point inside a Spark plan. Know which problem each solves. Benchmark with realistic rows/width and include serialization/memory/spill—not a tiny local example.
Related item: “Looks like pandas” describes developer ergonomics, not single-machine execution semantics. Any operation requiring one global order/index can cause substantial distributed work.
Integrated decision scenarios¶
Scenario A — daily customer-file normalization¶
Read CSV with an explicit schema and corrupt-record policy, normalize/validate fields with built-ins, parse event dates under a known timezone, quarantine failures, deterministically deduplicate by business key/latest update, broadcast a measured-small reference table, aggregate, and overwrite a controlled date partition. Inspect plan/shuffle/file sizes and validate counts/nulls/duplicates before publishing a persistent table and temp validation view.
Scenario B — skewed clickstream session metrics¶
Read events as a streaming DataFrame, extract/project early, apply event-time windows and watermark-aware deduplication, aggregate by product/session class, and write with checkpoint plus a compatible output mode/sink. Load-test one hot product key; inspect state, task skew, shuffle, batch duration and input/processing rates; assess preaggregation or carefully designed hot-key handling with correctness and restart/replay tests. Do not prescribe batch/stateless AQE as a fix for this stateful query; changing checkpoint-bound partition settings requires a supported migration plan.
Scenario C — remote Spark Connect application¶
A Python client connects to a governed remote Spark service, builds only supported DataFrame/SQL plans, reads a partitioned Parquet/Delta source, joins and writes results without collecting. Package dependencies/config explicitly, authenticate through the remote service, distinguish client location from the remote driver/cluster deployment, and diagnose a slow write through plan, UI and executor logs rather than client-only timing.
Worked decisions and answered checks¶
These original cases are learning exercises, not vendor exam questions.
| Evidence | Decision |
|---|---|
| JDBC bounds are 100 and 200, but rows outside that interval arrive | Bounds partition the read; add the required business predicate and validate returned keys. |
| A dimension unexpectedly contains two rows for one key | Detect/quarantine or resolve the dimension before joining; broadcast affects execution, not multiplication of matches. |
| ID copies have timestamps 10:00 and 10:03 within a 10-minute tolerance | ID-plus-time classic dedup treats them as different keys; evaluate within-watermark ID dedup for this contract. |
| A stateful streaming job has a hot key and long batches | Inspect state and task/input metrics; batch or stateless-only AQE support does not establish a remedy for this operator. |
- What does
count(qty)exclude? Null quantities;count(*)counts all rows. - Does
qty != 30keep null quantities? No. The comparison is unknown; addisNullexplicitly if required. - Does
dropDuplicates(['id'])select the latest event? No. Use a fully specified deterministic order and reject unresolved ties. - What happens to null IDs in the window fixture? They share a partition; define a quarantine or other business policy before deduplication.
- Why can a join inflate a total? A fact matches each qualifying dimension row. Check uniqueness at the intended join grain.
- Does broadcasting change join correctness? No. It changes data movement and memory use, not key multiplicity.
- Why can
unionsilently swap meaning? It aligns by position when types are compatible.unionByNamealigns names but still retains duplicates. - Are JDBC partition bounds row filters? No. They define stride; an actual predicate defines the desired data range.
- Why can an old malformed-cast example now fail? ANSI defaults changed in Spark 4.0. Make parsing and invalid-row handling explicit.
- Does adding
withWatermarkalways bound ID-only classic dedup state? No. Verify the operator/key relationship or use supported within-watermark deduplication for a bounded-time contract. - Does within-watermark dedup prove permanent uniqueness? No. Its guarantee is time-bounded; retained business identity may require durable state elsewhere.
- Can the current stateless-streaming AQE setting fix every stream? No. Stateful queries need their own supported tuning and recovery plan.
Hands-on lab sequence¶
Review execution boundary (September 28, 2026): the two Python blocks were syntax-checked, and ten SQLite assertions checked portable relational expectations. WSL setup/startup failures prevented executing Spark itself. All Spark runtime and streaming exercises below remain unexecuted in this review; SQLite results do not establish Spark, Arrow, checkpoint or watermark behavior.
- Execution anatomy: Run narrow and wide transformations followed by two actions; map application/job/stage/task, partitions, shuffle and recomputation, then cache/materialize/unpersist and compare.
- SQL and I/O: Read CSV/JSON/Parquet with explicit schemas; query files and temp views; write append/overwrite/partitioned outputs and a table; verify schemas, modes, files and session persistence.
- DataFrame correctness: Implement column, null, explode, validation, deterministic dedup, aggregate/date, join and union cases against adversarial fixtures; predict every schema/row result first.
- UDF/shared variables: Implement the same rule with a built-in, scalar UDF and Pandas UDF; compare plan/runtime/null behavior. Demonstrate a broadcast variable/accumulator and explain retry limitations.
- Tune a skewed join: Generate hot keys, measure tasks/shuffle/spill/GC, then test partitioning, broadcast, preaggregation, AQE and salting with identical result assertions.
- Streaming recovery: Build a windowed aggregate and dedup query with event time, watermark, output mode and checkpoint; inject duplicates/late data, stop/restart and inspect state/progress.
- Spark Connect: Run a supported remote DataFrame/SQL application, test version/dependency/session behavior and one unsupported/local-driver assumption, then diagnose through remote evidence.
- Pandas API on Spark: Port a pandas cleaning task, inspect its Spark plan/partitions, compare built-in/Pandas API/Pandas UDF approaches, and prove no unsafe full-data conversion occurs.
Readiness checks¶
Architecture and SQL¶
- I can map driver, worker, executor, CPU/task slots, memory and cluster manager responsibilities.
- I can compare local, client and cluster modes without confusing them with Spark Connect.
- I can explain application→job→stage→task and identify shuffle stage boundaries.
- I can distinguish lazy transformations/actions and predict recomputation across actions.
- I can distinguish narrow/wide work and relate partitions to task concurrency.
- I can choose cache/persist/storage level, materialize it and unpersist it with measured reuse.
- I can explain lineage task recovery versus executor/driver/external-side-effect failure boundaries.
- I can distinguish DataFrame, JVM Dataset and RDD optimization/typing boundaries.
- I can recognize Core, SQL/DataFrames, Structured Streaming, Pandas API on Spark and MLlib roles.
- I can read CSV/JSON/Parquet/ORC/text/Delta/JDBC with explicit schema and source-specific validation.
- I can configure parallel JDBC reads without overwhelming the source.
- I can query supported files through SQL and distinguish files, temp/global-temp views and persistent tables.
- I can choose append/overwrite/error/ignore and explain retry/partition-overwrite risk.
- I can distinguish write
partitionBy, executionrepartition,coalesceand sort semantics.
DataFrame API and tuning¶
- I can use select/alias/withColumn/rename/drop/filter/split/explode and predict null/row/schema effects.
- I can apply
na.drop/fill and business validation without confusing “any” and “all.” - I can distinguish distinct/dropDuplicates from deterministic latest-record selection.
- I can calculate aggregates and compare exact versus approximate distinct tradeoffs.
- I can parse/convert/extract dates and timestamps with units, formats and timezone awareness.
- I can implement inner/left/cross/multi-key joins and reason about null/duplicate columns.
- I can choose a broadcast join from size/memory evidence and recognize broadcast OOM risk.
- I can distinguish union/unionAll/unionByName and preserve/deduplicate rows intentionally.
- I can sort, print schema, show/take/collect and avoid driver-memory mistakes.
- I can choose built-in, ordinary UDF, Pandas UDF or stateful operator and explain optimization/state.
- I can distinguish broadcast variables from broadcast joins and explain accumulator retry limits.
- I can use explain/Spark UI/stage-task metrics/driver-executor logs as one diagnostic chain.
- I can distinguish driver OOM, executor OOM, skew, spill, GC and cluster-underutilization signatures.
- I can choose repartition/coalesce and reduce shuffle without changing required semantics.
- I can explain AQE coalescing, skew and join adaptation—and its limits.
Streaming, Connect, and pandas¶
- I can explain incremental/micro-batch processing, triggers, checkpoints and qualified exactly-once claims.
- I can create readStream/writeStream queries and choose compatible append/update/complete modes and sinks.
- I can distinguish event time, processing time, window and trigger.
- I can explain watermark-aware state eviction and deduplication for late/replayed data.
- I can test streaming restart, checkpoint compatibility, source replay and sink idempotency.
- I can explain Spark Connect client/server logical-plan/result behavior and unsupported/local assumptions.
- I can package/configure/authenticate a remote Connect application and diagnose it on the remote engine.
- I can choose Pandas API on Spark for pandas ergonomics without assuming single-node behavior.
- I can create a typed/schema-correct Pandas UDF and compare its Arrow boundary to built-ins/scalar UDFs.
- I can complete all common API recall exercises without exam-time documentation.
Places to learn¶
This is not a complete list, and it is not meant to be consumed in full. Pick the resources that fit your gaps; spend most time writing, predicting, testing, and diagnosing Spark code. Public metadata was checked September 28, 2026: Pluralsight still lists 11 courses, 5 labs and 9 hours. Academy lessons require sign-in. Both O'Reilly book pages and Udemy blocked automated access, so previously observed editions, dates and durations below were not reverified. Other times are planning estimates.
| Resource | Access | Estimated time |
|---|---|---|
| Official certification page and October 30, 2025 guide | Free | 2–3 hours to map objectives and inspect vendor sample format; do not redistribute questions |
| Databricks Academy — Introduction to Apache Spark, Developing Applications, Stream Processing and Analysis, and Monitoring and Optimizing Spark Workloads | Free account/customer or partner entitlement varies | 20–35 hours with labs; catalog totals and availability require sign-in |
| Apache Spark documentation and PySpark API | Free | 12–20 hours selected architecture, SQL/DataFrame, streaming, Connect and pandas/API work |
| Databricks Free Edition/local Spark plus this guide's eight labs | Free/organizational | 25–45 hours including skew, recovery, logs/UI and remote/Connect experiments |
| Streaming-deduplication walkthrough — Murali Talluri, January 10, 2025 | Free | 30–45 minutes reading plus 1–2 hours comparing duplicate keys, event times and state; verify current APIs |
| Pluralsight: Apache Spark for Data Scientists | Paid/trial; 11 courses and 5 labs | 9 hours listed plus 6–12 hours applied practice; map out-of-scope ML/Graph work and close Connect/deployment gaps |
| O'Reilly: Learning Spark, 2nd Edition | Paid/trial; 397-page 2020 book | 9 hours 49 minutes listed plus labs; strong Chapters 2–8, but Spark 3-era and requires current Connect/Pandas/API checks |
| O'Reilly: High Performance Spark, 2nd Edition | Paid/trial; June 2026 Spark 4.x book | 10 hours 46 minutes listed; select architecture/skew/tuning/Connect sections—advanced and intentionally broader than the exam |
| Udemy: Apache Spark 4 hands-on guide — Ansh Lamba | Paid; July 2026 update previously observed; not reverified September 28 | 20–35 hours planning estimate with exercises; verify exact runtime and October 2025 blueprint mapping |
The current exam has no API documentation aid. The linked official guide supplies sample-format questions; the previously cited community statement about practice-exam availability could not be reverified on this date. No exact current MeasureUp, Whizlabs, or exam-aligned O'Reilly/LinkedIn Learning product was independently verified. Reject resources that advertise recalled/real questions or still teach the retired Spark 3.0 exam format as current.