Spark modernization without a migration: Huohua’s “engine swap” playbook
How Huohua swapped Spark for Singdata with zero data move—10× faster jobs, hourly freshness, 60%+ lower compute cost.
If you’ve run Spark long enough in a large organization, you know the pattern: your most important jobs drift from “fine” to fragile. Runtime creeps up. Freshness stalls at T+1 because nobody wants to run the pipeline more often. And the team spends too many cycles tuning memory, shuffles, and retries just to keep dashboards alive.
Huohua (Spark Education)—one of China’s top online education companies in logic-thinking—took a different path than a traditional platform migration.
They didn’t move data. They didn’t switch schedulers. Business users didn’t have to change their habits.
They swapped the engine.
Spark modernization without a migration: the engine-swap pattern
Most Spark “modernization” projects fail for a simple reason: they’re treated like migrations.
A migration changes everything at once: storage layout, compute engine, orchestration, permissions, table definitions, and user workflows. Even if the target platform is better, the blast radius is huge.
An “engine swap” is the opposite. It’s a constrained change with a narrow goal:
Keep your storage (object storage, formats, paths)
Keep your scheduler and operational muscle memory
Keep your access model and user-facing interfaces where possible
Change only the compute engine that reads/writes the data
That’s exactly what Huohua did.
The baseline: why Spark pain shows up in production
On paper, Spark is flexible. In production, large ETL jobs tend to accumulate risk:
Memory pressure from wide transformations and large shuffles
Skew that silently concentrates work into a few partitions
Tuning tax (executor sizing, shuffle partitions, spill behavior) that grows with data volume
These are well-known failure modes in distributed compute systems, and they’re a common root cause of OOMs, long tail latency, and timeouts.
Huohua’s Phase 1: replace the compute engine, keep everything else
Huohua’s Phase 1 goal was not “new architecture.” It was minimum change, maximum return.
Here’s what stayed the same:
Data stayed on Tencent Cloud COS (no data moved)
Their in-house Athena scheduler stayed (no platform switch)
Existing ETL logic, permissions, and user habits were preserved
Here’s what changed:
They plugged in Singdata Lakehouse using external-table mode
A vectorized engine read and wrote COS directly
If you’re unfamiliar with the pattern: external tables (in many SQL/lakehouse systems) are metadata pointers to data that lives outside the engine-managed storage layer. The table definition tells the engine where the files live; it doesn’t require a copy step. Spark itself documents external table behavior in its SQL migration notes (see the Apache Spark docs on Hive external table semantics).
Key Takeaway: The “engine swap” isn’t magic—it's disciplined scope control. You change compute, not the rest of the system.
Production results after the June 2025 cutover (two weeks of data)
These are Huohua’s production metrics, two weeks after the June 2025 cutover:
Top job runtime: 60 min → 6 min (10×)
Data freshness: T+1 → hourly
Compute cost: down 60%+
Stability: frequent OOM/timeouts → rock-stable
The important part isn’t just the magnitude of the gains. It’s the operational reality:
No multi-quarter rewrite
No data relocation project
No retraining business users
Why this works: object storage + external tables + modern execution
Reading and writing directly against object storage is not new. The difference is how well your engine does it.
In object-storage analytics, performance tends to come down to fundamentals:
Columnar formats (Parquet/Iceberg/Delta-type layouts)
Partitioning discipline to avoid full scans and small-file explosions
Vectorized execution to reduce per-row overhead and improve scan/aggregate throughput
Smart caching for repeated reads
Engines like Trino explicitly document object-storage connector behavior (see Trino’s S3 file system support docs). ClickHouse’s S3 table engine docs also highlight patterns like object-store access and caching (see ClickHouse’s S3 table engine documentation).
Huohua’s implementation detail that matters: Singdata’s engine could operate directly on COS while preserving the surrounding system.
If you want to see how Singdata positions this pattern, their Spark modernization solution describes a non-invasive path and calls out “native vectorized engine” plus “incremental compute” as core mechanics.
A practical playbook you can copy
If you’re considering a similar approach—whether you’re staying on Spark longer, evaluating a lakehouse engine, or just trying to shrink operational risk—use this checklist.
Prerequisites
Storage reality check
Do you have durable, well-organized object storage paths?
Are file formats columnar (or can you get there without a rebuild)?
Metastore and permissions clarity
Can you keep your current permission model or map it cleanly?
Do you have a clear inventory of the “critical tables” that power dashboards and exec reporting?
A minimal success metric set Pick 3–5 numbers you’ll accept as “proof” in production:
runtime for top jobs
freshness/latency
compute cost
failure rate (OOM/timeouts/retries)
Steps with validation gates
Step 1: Start with external-table mode
Point the new engine at existing COS data locations and expose them as external tables.
Done when: you can run the critical read paths (top queries/jobs) without rewriting data.
Step 2: Shadow-run the top workloads
Run the new engine in parallel for the highest-value jobs.
Validate: row counts, aggregates, and key business metrics match within an agreed tolerance.
⚠️ Warning: Don’t cut over based on staging performance alone. Shadow-run long enough to see the failure modes.
Step 3: Cut over with rollback in mind
Switch execution for one slice of workloads at a time.
Done when: you can revert to the prior engine/scheduler path within a predictable window.
Step 4: Increase freshness deliberately
Hourly freshness isn’t just “run it more.” It changes your operational posture.
Use incremental processing where possible and add observability: freshness lag, failed refreshes, anomaly checks.
A helpful neutral reference for near-real-time lakehouse patterns is Microsoft’s architecture example for real-time lakehouse data processing.
Common failure modes (and how to avoid them)
Failure mode 1: treating the swap as a stealth migration
If the engine swap turns into “while we’re here, let’s change the storage layout,” you lose the main benefit: low disruption.
Fix: lock the Phase 1 scope: compute-only change.
Failure mode 2: ignoring governance until after cutover
Enterprise readers know this one: you can’t bolt on governance after the fact.
Fix: keep permissions and auditing continuity as explicit acceptance criteria.
Failure mode 3: freshness without guardrails
Moving from T+1 to hourly can create alert fatigue and hard-to-debug data discrepancies if you don’t design validation.
Fix: instrument freshness and quality checks from day one.
What Phase 2 and Phase 3 unlock
Huohua’s roadmap framing is directionally aligned with what many enterprise teams want:
Phase 2: incremental compute toward a Kappa-style posture (more continuous processing, fewer full recomputes)
Phase 3: Data + AI operating on the same lakehouse substrate
If you want to explore the incremental compute direction in Singdata’s ecosystem, their docs on incremental computing for streaming pipelines and creating dynamic tables provide a starting point.
Next steps
If you’re wrestling with Spark instability, cost, or freshness—and you can’t afford a multi-quarter migration—start by writing down what must not change.
Then evaluate whether an engine-swap approach can meet your acceptance criteria with minimal disruption.
If you want a concrete entry point, start with Singdata’s Spark modernization solution and then drill into the documentation for Singdata lakehouse key concepts.