How NEMichelinCache Cuts Crashes and Boosts Performance in Cross‑Platform RN/H5 Apps
The article details the design and implementation of NEMichelinCache, a cross‑platform cache library for React Native and H5, explaining the problems of the legacy cache, the architectural choices made, the modules introduced, and the measurable improvements in stability and speed achieved after deployment.
Background
NetEase Cloud Music required a modern, cross‑platform cache solution for its H5 and React Native (RN) modules because the legacy cache library caused frequent crashes, main‑thread stalls, blank pages, and high error rates.
Cache Fundamentals
Cache trades space for time. Two classic policies are:
Write‑Through (Direct Write)
Pros: data recovery, cache‑memory consistency, immediate I/O access.
Cons: slower performance, higher write overhead.
Write‑Back (Deferred Write)
Pros: faster, fewer writes.
Cons: possible inconsistency, stale reads.
Design Considerations
Eliminate crashes, thread blocking, blank pages, and high error rates.
Balance cache consistency with read/write speed for stability.
Provide fast error localization via a custom logging domain.
Introduce a lightweight local‑recovery log to reduce server load.
Minimize migration cost between old and new cache libraries.
Offer extensibility for different data sources and cache types.
Lower integration effort for business teams.
Problem‑Driven Solutions
From the drawbacks of write‑back caches: Guarantee data consistency across memory, engine, and disk caches. Disallow direct I/O access to the cache from business code.
Crash and Main‑Thread Stall Mitigation
Thread‑pool design moves I/O and heavy operations off the main thread.
Download‑update module uses a responsibility‑chain pattern; each node is atomic and runs on a background thread.
Unified database handling via FMDB queue.
Degraded Errors, Load Failures, Blank Pages, Engine Errors
Synchronize the database only after a full successful update.
Database module supports transactions and fallback mechanisms.
Support multiple cache versions to avoid interference.
Reference‑count module prevents premature cache deletion.
Remove public APIs that allow arbitrary cache clearing or direct disk reads.
Priority‑based runner ensures critical pages load faster.
Database/file migration guarantees compatibility between versions.
CDN migration and enforced HTTPS prevent interception.
Fast Error Localization
Logging module defines custom domains for each stage.
Eliminate redundant logs.
Fine‑grained exception information tied to the loading flow creates a closed‑loop for debugging.
Architecture Overview
Business Interface Layer
Provides a controlled API for developers, preventing direct disk access.
Initialization via @interface NEMichelinCacheConfig : NSObject with app name and cache root path.
DataProvider protocol – business code implements a single method to fetch bundle cache resources.
Cache update API – update resources with priority and completion callbacks.
Custom cache/data protocol for special cases.
The layer also handles AB switching between old and new caches and reserves slots for future extensions.
Responsibility‑Chain Module
Nodes: pre‑check, download, MD5 verification, zip/gz/tar extraction, merge, final cache update.
Customizable chain – nodes can be added or removed.
Supports pause/resume, global context propagation, exception throwing, lifecycle callbacks, single‑responsibility nodes, and data‑centric logic.
Responsibility‑Chain Runner
Priority‑queue capability.
Multiple chains per key.
Cache‑aware execution allowing high‑priority tasks to jump ahead.
Decompression / Merge Module
Extracts and merges zip, tar, gz archives.
Configurable third‑party libraries can be swapped out.
Database Layer
Replaces raw SQLite with FMDB.
Uses transactions, data migration, validation, and rollback on errors.
Multi‑Version Coexistence
AppInfo describes each bundle version and its path.
Bundle files are the JS bundles read by RN.
Multiple versions coexist because the timing of AppInfo reads and engine bundle loads can differ, which would otherwise cause stale paths and unpredictable behavior. Each version’s database, memory, and files remain untouched until all associated bridges are released, preventing unlimited disk growth. Reference counting cleans up unused versions.
Reference Counting for Local Bundle Cache Cleanup
When a Bridge is created, it holds a reference to its local cache.
Cache is cleared only after all Bridges release their references.
Cleanup verifies that the cache is the latest version and not in use.
Results
After deploying NEMichelinCache in the RN module, key metrics improved compared with the legacy CCCandyWebCache:
MD5 verification success rate: 97% → 100% (+3%).
Sampled degraded errors: 1,600 → 100 (‑94%).
Xcode wake‑up crashes: peak 70% → 0 (‑100%).
ANR occurrences: 100 sampled → none detected (‑100%).
Stutter events: 46,000 → 3 (‑99%).
CPU anomalies, OOM, engine errors all dropped by ~90‑100%.
24‑hour upgrade rate increased by 2‑20% for major clients.
Conclusion
The redesign, driven by concrete failure analysis, introduced a modular responsibility‑chain architecture, robust domain‑based logging, and a reference‑counted multi‑version strategy. These changes dramatically reduced crash rates, improved performance, and simplified future maintenance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
