Skip to content

Instant: stop stale background sync when embedded views are disposed (workaround + stress example) - #12

Draft
mutumbakato wants to merge 2 commits into
masterfrom
instant-stale-sync-workaround
Draft

Instant: stop stale background sync when embedded views are disposed (workaround + stress example)#12
mutumbakato wants to merge 2 commits into
masterfrom
instant-stale-sync-workaround

Conversation

@mutumbakato

@mutumbakato mutumbakato commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Update (10 Aug 2026): the underlying bug is now fixed upstream in Nutrient Android SDK 11.6.2. This PR no longer ships a workaround — it bumps the SDK pin and keeps the stress example as a regression harness. The original adapter workaround is preserved in the history (cd66772) for anyone pinned to an older SDK.

Problem

Closing a NutrientInstantView on Android did not stop that document's listen-for-server-changes long-poll. The InstantPdfDocument survived in the descriptor cache with its sync coordinator still listening, so every document ever viewed kept a background /sync connection open. After viewing enough distinct documents, these stale long-polls exhausted the per-host connection pool and the next document's auth/download queued behind them:

  • Document loads stalled for 40–60 seconds on a healthy local Document Engine, no fault injection.
  • Behind a reverse proxy with a read timeout, the stale polls surfaced as endless empty-body 502s for previously viewed documents.
  • iOS was unaffected: it correctly stops sync on view teardown (verified side by side).

Reported in ZD#134164.

The fix

Nutrient Android SDK 11.6.2 (released 6 Aug 2026, AND-1957):

Fixes slow Instant document loads when frequently switching between documents. Disabling listening for server changes (including closing an embedded Instant view) now cancels the in-flight sync long-poll instead of leaving it holding a connection until the server releases it.

This is the proper fix — real-time listening stays enabled, and no adapter or periodic sync timer is needed.

What this PR contains

  1. Bumps the pinned Nutrient Android SDK in android/config.gradle from 11.5.1 to 11.6.2.
  2. Adds the "Instant Switch Stress" example — automates document-switch / close-reopen cycles against a Document Engine and reports per-cycle load latency. Retained as a regression harness now that the bug is fixed.
  3. Removes the InstantSyncLifecycleAdapter workaround that the first commit introduced, along with its UI toggle and syncNow() helper.
  4. Fixes example/pubspec.yaml — removes dependency_overrides pointing outside the repository (a monorepo sync artifact that broke pub get on clean checkouts).

Verification

12 distinct, never-before-downloaded documents opened in sequence; profile build on a clean API 36 emulator against a local Document Engine; no workaround active in either run.

11.5.1 11.6.2
Median load 1392 ms 734 ms
Worst load 90014 ms (harness cap) 1386 ms
Timeouts 2 / 12 0 / 12
Wall clock for 12 cycles ~3 m 45 s 30 s

Re-verified after deleting the adapter: median 732 ms, worst 2115 ms (first-cycle cold start), 0 timeouts.

Not a document-specific artifact — fresh-doc-23 hit the 90 s cap on 11.5.1 and loaded in 1386 ms on 11.6.2.

For reference, the workaround measured in the first commit reached ~250 ms median with no stalls on 11.5.1; it is no longer required.

Note on the federated platform packages

nutrient_flutter_android hardcodes its own SDK default in android/build.gradle — 1.2.0 pins 11.5.1 and 2.0.0 pins 11.6.1, both below the fix. It is included as a Gradle subproject, so dependencyInsight shows 11.5.1 -> 11.6.2 resolved by conflict resolution and this build is correct. A matching bump is still needed in the platform-packages repo, otherwise an app resolving the federated package without the inline plugin lands below the fix.

Try it

cd example && flutter run
# open "Instant Switch Stress (repro #134164)", Run

The example defaults to a local Document Engine on :5001 with documents fresh-doc-01..30 and clean-doc-a uploaded (see the header of instant_switch_stress_example.dart for setup).

Apps that cannot take a plugin update yet can pick up the SDK fix directly by adding pspdfkit.version=11.6.2 to their app's android/local.properties.

mutumbakato and others added 2 commits July 30, 2026 14:54
Closing a NutrientInstantView on Android does not stop the document's
listen-for-server-changes long-poll: the InstantPdfDocument survives in
the descriptor cache with its sync coordinator still listening, so every
document ever viewed keeps a background /sync connection open. After
viewing enough distinct documents these stale long-polls exhaust the
per-host connection pool and the next document's auth/download queues
behind them, surfacing as 40-60 s document loads (and, behind a reverse
proxy with a read timeout, as endless empty-body 502s for previously
viewed documents). iOS already stops sync on view teardown.

The new "Instant Switch Stress" example demonstrates both the issue and
the workaround, using only public APIs and the stock pub.dev packages:

- InstantSyncLifecycleAdapter (an AndroidAdapter) prevents the
  listen-for-changes long-poll from starting at onPdfFragmentReady and
  stops listening on the cached document at onFragmentDetached. Timing
  matters: an already-running long-poll holds its pool slot until the
  server releases it (~47-60 s measured), so stopping at dispose alone
  is not enough.
- syncNow() pulls server changes on demand, replacing the disabled
  real-time listener (local edits still push automatically).
- The example automates document-switch / close-reopen cycles against a
  Document Engine and reports per-cycle load latency, with a UI toggle
  to compare baseline vs workaround.

Measured on a 12-distinct-document sweep: baseline median 746 ms with
47.6 s stalls; with the adapter enabled median ~250 ms and no stalls.
Verified against a licensed Document Engine that adapter registration
does not affect licensing.

Also removes the example's dependency_overrides that pointed outside
the repository (a monorepo sync artifact that broke pub get on clean
checkouts); all platform packages now resolve from pub.dev.

Repro/verification for https://pspdfkit.zendesk.com/agent/tickets/134164

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…round

11.6.2 fixes the stale Instant sync long-poll upstream (AND-1957):
disabling listening for server changes — which includes closing an
embedded Instant view — now cancels the in-flight long-poll instead of
leaving it holding a connection until the server releases it. That was
the root cause behind the 40-60 s document loads in cd66772, so the
InstantSyncLifecycleAdapter workaround is no longer needed.

Measured with the Instant Switch Stress example against a local Document
Engine, 12-cycle switch sweep over distinct never-downloaded documents,
profile build, workaround disabled in both runs:

  11.5.1   median 1392 ms   worst 90014 ms (harness cap)   2 timeouts
  11.6.2   median  734 ms   worst  1386 ms                 0 timeouts

Re-verified after removing the adapter: median 732 ms, 0 timeouts. Not a
document-specific artifact — fresh-doc-23 hit the 90 s cap on 11.5.1 and
loaded in 1386 ms on 11.6.2.

The switch-stress example stays in the catalog as a regression harness,
with its doc comment rewritten to record where the fix landed.

Note: the federated nutrient_flutter_android package hardcodes its own
SDK default (1.2.0 -> 11.5.1, 2.0.0 -> 11.6.1), both below the fix.
Gradle conflict resolution picks the inline plugin's 11.6.2 here, but
that package needs a matching bump in the platform-packages repo.

Resolves https://pspdfkit.zendesk.com/agent/tickets/134164

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