Skip to content

PE-9103: Parallelize upload preparation pipeline - #2161

Merged
vilenarios merged 5 commits into
devfrom
PE-9103-upload-prep-optimization
Jul 31, 2026
Merged

PE-9103: Parallelize upload preparation pipeline#2161
vilenarios merged 5 commits into
devfrom
PE-9103-upload-prep-optimization

Conversation

@vilenarios

@vilenarios vilenarios commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Optimizes the upload preparation phase by parallelizing sequential operations that were previously blocking on each other unnecessarily.

Changes:

  • Parallel conflict detection: All file conflict DB queries run concurrently via Future.wait instead of one sequential query per file. For 100 files: 100 sequential queries → 1 parallel batch.
  • Pre-computed file lengths: All file lengths read in parallel at the start of startUploadPreparation() and cached in _fileLengthCache. Eliminates 2-3x redundant sequential reads per file across warning check, plan creation, and total size calculation.
  • Parallel cost estimation: Turbo balance + AR bundle/file sizes fetched concurrently. AR and Turbo cost calculations run in parallel after sizes are computed.
  • Parallel upload plan creation: AR and Turbo upload plans created simultaneously via Future.wait instead of sequentially.
  • Remove redundant operations: Duplicate wallet mismatch check removed, ANT records not re-fetched if already populated, 100ms artificial UI delay reduced to Duration.zero.

Impact

For multi-file uploads (50+ files), preparation time reduced from sequential O(n) DB queries to parallel O(1). Network-bound cost estimation parallelized (saves ~1-2s).

Test plan

  • Upload single file — verify preparation completes normally
  • Upload 10+ files — verify faster preparation
  • Upload folder with nested files — verify conflict detection works
  • Upload with file name conflicts — verify conflict modal appears correctly
  • Upload to private drive — verify encryption/key derivation works
  • Turbo vs AR cost display — verify both costs shown correctly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance Improvements

    • Improved upload preparation by running planning, size, conflict checks, and cost estimation concurrently.
    • Added caching for file sizes so later steps reuse them without extra I/O.
    • Conflict detection now completes faster by processing per-file checks in parallel.
    • Upload payment evaluation now calculates AR and Turbo info in parallel and avoids extra network work when unnecessary.
    • Replaces a fixed delay with a UI-yield to keep the upload flow more responsive.
  • New Features

    • Free-upload eligibility is now reported with clearer free-status details and eligibility checks.

…ation, plans PE-9103

- parallelize conflict detection: check all files concurrently via
  Future.wait instead of sequential DB query per file
- pre-compute all file lengths in parallel at start of preparation,
  cache in _fileLengthCache to avoid redundant sequential reads
- parallelize cost estimation: turbo balance + AR sizes fetched in
  parallel, then AR and Turbo cost calculations run concurrently
- parallelize upload plan creation: AR and Turbo plans created
  simultaneously via Future.wait instead of sequentially
- remove duplicate wallet mismatch check (called twice in
  prepareUploadPlanAndCostEstimates)
- skip ANT records re-fetch if already populated from initial
  fire-and-forget fetch
- reduce 100ms artificial UI delay to Duration.zero (just yields
  to event loop for state update)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d7fa713-99b8-43d7-96fd-109cfeb6b2ec

📥 Commits

Reviewing files that changed from the base of the PR and between 4d28c18 and 831070f.

📒 Files selected for processing (2)
  • lib/blocs/upload/upload_cubit.dart
  • lib/core/upload/uploader.dart

📝 Walkthrough

Walkthrough

Upload preparation now caches file lengths, parallelizes conflict checks and upload setup, and runs upload plan mounting, balance retrieval, size calculation, and cost estimation concurrently.

Changes

Upload preparation concurrency

Layer / File(s) Summary
File length caching in UploadCubit
lib/blocs/upload/upload_cubit.dart
Caches file lengths computed concurrently during preparation and reuses them when calculating total upload size.
Parallel conflict detection and upload guards
lib/blocs/upload/upload_cubit.dart
Runs conflict lookups concurrently, yields with Duration.zero, moves the wallet-mismatch guard, and narrows ArNS ant-record loading.
Parallel upload plan mounting and cost estimation
lib/core/upload/uploader.dart
Mounts AR and Turbo plans concurrently, parallelizes balance and size retrieval, and computes AR and Turbo costs concurrently with fallbacks.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: parallelizing the upload preparation pipeline.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PE-9103-upload-prep-optimization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/core/upload/uploader.dart (1)

440-451: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reuse the Turbo estimate instead of recalculating it.

For non-free Turbo uploads, turboCostFuture calculates the Turbo cost, then _determineUploadMethod() calls _turboUploadCostCalculator.calculateCost() again. Reuse turboCostEstimate to avoid the duplicate network-bound cost estimate and inconsistent fallback behavior.

Proposed refactor
     uploadMethod = isFreeUploadPossibleUsingTurbo
         ? UploadMethod.turbo
-        : await _determineUploadMethod(
-            turboBalance.balance,
-            turboBundleSizes,
-            _appConfig.allowedDataItemSizeForTurbo,
-            _isTurboAvailableToUploadAllFiles,
-          );
+        : _isTurboAvailableToUploadAllFiles &&
+                turboBalance.balance >= turboCostEstimate.totalCost
+            ? UploadMethod.turbo
+            : UploadMethod.ar;

Also applies to: 473-480

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/core/upload/uploader.dart` around lines 440 - 451, Reuse the existing
Turbo cost result instead of calling the cost calculator twice: the
`turboCostFuture` in `uploader.dart` already produces `turboCostEstimate`, so
update `_determineUploadMethod()` (and the related non-free Turbo path in the
surrounding upload selection flow) to consume that cached estimate rather than
invoking `_turboUploadCostCalculator.calculateCost()` again. Keep the fallback
behavior centralized in the `catchError` handling for `turboCostFuture`, and
ensure the upload method decision uses the already awaited `turboCostEstimate`
value consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/blocs/upload/upload_cubit.dart`:
- Around line 105-107: The file length cache in UploadCubit is using a derived
string key from getIdentifier(), which can collide for different files and
overwrite cached lengths. Update _fileLengthCache and its read/write sites in
UploadCubit, especially around _getTotalSize() and the upload preparation logic,
to key by the file object or IOFile identity instead of a string identifier.
Make sure the cache lookup and population use the same identity-based key so
distinct files cannot share entries.
- Around line 1078-1080: The manifest reload guard in the upload cubit is
checking for a null `_ants` value even though `_ants` is initialized as an empty
list, so the reload path never runs. Update the condition in the upload flow
within the `UploadCubit` manifest handling logic to use the empty-list fallback
by checking `_ants.isEmpty` instead of `_ants == null`, keeping the existing
reload behavior intact when manifest entries are present.

---

Nitpick comments:
In `@lib/core/upload/uploader.dart`:
- Around line 440-451: Reuse the existing Turbo cost result instead of calling
the cost calculator twice: the `turboCostFuture` in `uploader.dart` already
produces `turboCostEstimate`, so update `_determineUploadMethod()` (and the
related non-free Turbo path in the surrounding upload selection flow) to consume
that cached estimate rather than invoking
`_turboUploadCostCalculator.calculateCost()` again. Keep the fallback behavior
centralized in the `catchError` handling for `turboCostFuture`, and ensure the
upload method decision uses the already awaited `turboCostEstimate` value
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 092002cb-3c6c-4110-ad14-4031aa496e51

📥 Commits

Reviewing files that changed from the base of the PR and between 14623fa and 5607cf0.

📒 Files selected for processing (2)
  • lib/blocs/upload/upload_cubit.dart
  • lib/core/upload/uploader.dart

Comment on lines +105 to +107
/// Pre-computed file lengths cache. Populated in parallel at the start of
/// upload preparation to avoid redundant sequential file.ioFile.length reads.
Map<String, int> _fileLengthCache = {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid key collisions in the file length cache.

getIdentifier() can collapse distinct files to the same key, especially for empty/blob paths where it returns only ioFile.name. That can overwrite one file’s cached length and make _getTotalSize() report the wrong total. Key the cache by the file object/IOFile identity instead of a derived string.

Proposed fix
-  Map<String, int> _fileLengthCache = {};
+  Map<IOFile, int> _fileLengthCache = {};
@@
-      size += _fileLengthCache[file.getIdentifier()] ??
+      size += _fileLengthCache[file.ioFile] ??
           await file.ioFile.length;
@@
-        _files[i].getIdentifier(): lengths[i],
+        _files[i].ioFile: lengths[i],

Also applies to: 557-558, 941-945

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/blocs/upload/upload_cubit.dart` around lines 105 - 107, The file length
cache in UploadCubit is using a derived string key from getIdentifier(), which
can collide for different files and overwrite cached lengths. Update
_fileLengthCache and its read/write sites in UploadCubit, especially around
_getTotalSize() and the upload preparation logic, to key by the file object or
IOFile identity instead of a string identifier. Make sure the cache lookup and
population use the same identity-based key so distinct files cannot share
entries.

Comment thread lib/blocs/upload/upload_cubit.dart Outdated
vilenarios and others added 3 commits July 5, 2026 15:12
IOFile.length returns FutureOr<int>, not Future<int>. Future.wait
requires Future<T>, so wrap in async closure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mocktail's thenThrow throws synchronously before returning a Future,
so .catchError never gets attached. Wrapping in async IIFE with
try/catch handles both sync and async throws correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 831070f):

https://ardrive-web--pr2161-pe-9103-upload-prep-xabdgr5j.web.app

(expires Thu, 06 Aug 2026 14:18:40 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: a224ebaee2f0939e7665e7630e7d3d6cd7d0f8b0

…timization

# Conflicts:
#	lib/core/upload/uploader.dart
@vilenarios

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Note for this pass: the latest commit merges origin/dev into this branch and hand-resolves conflicts in lib/core/upload/uploader.dart (getUploadPaymentInfoForUploadPlans), fusing this PR's parallelization with the free-tier allowance path now on dev. Please focus on that conflict resolution.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

@vilenarios I’ll review the latest conflict resolution, focusing on getUploadPaymentInfoForUploadPlans and preservation of the free-tier allowance behavior alongside the parallelized cost-estimation flow.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vilenarios
vilenarios merged commit 8e54477 into dev Jul 31, 2026
8 checks passed
@vilenarios
vilenarios deleted the PE-9103-upload-prep-optimization branch July 31, 2026 00:14
vilenarios added a commit that referenced this pull request Aug 11, 2026
Brings in everything that landed after the branch was cut:

  #2161  parallelize upload preparation
  #2174  read the Turbo free-item cap from /v1/info
  #2175  self-describing share links, authenticated downloads, recipient page
  #2176  vendor pdf.js and add pdfx
  #2177  CLAUDE.md
  #2178  gateway fallback for every fetch, vendored CDN scripts, dead code
  #2179  sync reads a single gateway, pooled instead of chunked

No conflicts. Verified on the merged tree: analyze clean, 1319 app tests,
43 crypto tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant