digidem / digidem/comapeo-core-react-native
perf(sentry): compute storage-size metric via StorageStatsManager instead of a recursive walk
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 1
- Forks
- 0
- Avg merge
- 8h 24m
- Merged PRs (30d)
- 9
Description
Background
The backend emits a coarse storage_size bucket metric once at boot (§11.2.a) so we can see, in aggregate, roughly how much data CoMapeo installs hold. Today this is computed by sampleStorageSize in backend/index.js, which recursively walks the private storage directory in the nodejs-mobile backend — readdir + fs.stat on every file — and sums the bytes before bucketing.
That walk is O(number of files): on an install with a large media/blob store it is real disk I/O and battery proportional to the dataset, run on every cold start. As an interim fix (#111) the walk is now skipped entirely when Sentry is disabled (metrics.isEnabled() gate), so opted-out users no longer pay for it. This issue tracks replacing the walk with an OS-provided figure so even diagnostics-on users don't pay for it.
Proposed change
Use the platform's built-in per-app storage accounting instead of walking the tree.
Android — StorageStatsManager (API 26+).
StorageStatsManager.queryStatsForUid(StorageManager.UUID_DEFAULT, Process.myUid()) returns a StorageStats with getDataBytes() / getCacheBytes() / getAppBytes(). The OS already tracks this per-UID, so it is effectively a lookup rather than a walk. Querying our own app's UID needs no permission (PACKAGE_USAGE_STATS is only required to query other apps).
The backend is JS (nodejs-mobile) and can't call Android APIs directly, but we already compute DeviceTags (deviceClass/osMajor) in Kotlin and pass them to the backend via argv (--deviceClass=…). Storage can ride the same channel: compute the bucket in Kotlin and pass --storageBucket=…, or emit the metric straight from the FGS Sentry bridge. Either way the JS walk disappears.
iOS has no equivalent per-sandbox OS counter (URLResourceValues is still per-file), so iOS keeps a gated walk or skips the metric.
Pros
- Removes an O(files) disk walk from every cold start on Android; the OS figure is ~constant-cost.
- No new permission required for our own UID.
- Fits the existing Kotlin→backend argv plumbing used for device tags.
- Arguably more accurate as a "user footprint" signal:
getDataBytes()is the whole app's on-disk data (filesDir + databases + caches + …), a superset of theprivateStorageDirsubtree we currently sum.
Cons / considerations
- minSdk is 24;
StorageStatsManageris API 26+. API 24–25 needs a fallback (walk, or skip the metric) — a small and shrinking device slice. - Metric semantics shift: whole-app data bytes vs. the
privateStorageDirsubtree. If we want continuity with already-collected data, account for the step change (e.g. note it in the dashboard, or restrict to the data subdir if the API allows — it does not, it's UID-level). queryStatsForUidcan itself be non-trivial on some devices/large apps (the OS may compute on demand), though still far cheaper than our walk and the sanctioned API.- Cross-platform split: Android gets the native path; iOS keeps a walk or drops the metric. Slightly more code paths than the current single JS implementation.
- Moves one more piece of telemetry from the backend into the native layer — fine given device tags already live there, but worth being deliberate about where the storage metric is owned.
Rejected alternatives
StatFs— only reports whole-filesystem free/total, not this app's usage.du -s <dir>shell-out — still an O(files) walk (in C), and awkward to spawn from nodejs-mobile; marginal win.
Acceptance
- Android computes storage bytes via
StorageStatsManager.queryStatsForUid(API 26+), with a 24–25 fallback decided (walk or skip). - Value reaches the metric without a JS recursive walk on Android.
- iOS path decided (gated walk or drop).
- Bucket-semantics change documented for whoever reads the dashboard.
Interim gating landed in #111.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with sampleStorageSize in backend/index.js and inspect the existing Kotlin DeviceTags-to-backend argv plumbing. Compare the Android API 26+ path with the required API 24–25 fallback and decide how iOS is handled. Done means Android avoids the JS recursive walk, the value reaches the metric, and the bucket-semantics change is documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, javascript, kotlin, node.js
- Domain
- mobile-dev, observability, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100