Repository navigation
profile deletion takes ~50 s and repeats endpoint cleanup #1522
Description
Observed
Deleting a profile with 0 contacts takes ~50 s on master and on #1510 alike (51.4 s against 49.8–54.6 s; emulator on staging). Nearly all of it is ~96 sequential homeserver requests for the user's own key. The SDK side is tracked in the linked Paykit issue. This issue covers the requests Bitkit itself adds, and the feedback during the wait.
Timeline (PR head f22714b):
| Step | Time from tap |
|---|---|
Deleted all contacts |
+11.0 s |
Session signed out |
+47.5 s |
| Onboarding shown | +49.8 s |
Expected
Deletion makes each cleanup call once and finishes in a few seconds. While it runs, the user can see that it is still working.
Where Bitkit adds requests
- Endpoint cleanup runs twice.
EditProfileViewModel.attemptDeleteProfilefirst callsprivatePaykitRepo.removePublishedEndpointsForCleanup(PrivatePaykitRepo.kt:493: private endpoints plussyncPaykitAppAfterCleanup). AfterdeletePaykitProfile(),PubkyRepo.signOut()callsremoveBitkitPaymentEndpoints(PubkyService.kt:66:syncPublicEndpoints(emptyList())plussyncPaykitApp(privatePaymentsEnabled = false)). - Once
deletePaykitProfile()has removed the Paykit storage, some of this may be redundant. But the order is a safety property: endpoints must be gone before the session is revoked, and a failed deletion must not leave stale endpoints. Any change needs the Paykit owner to confirm which writes the storage delete already covers. - The Noise key authorization is fetched three times in one deletion.
removeContacts(records.map { it.publicKey })(PubkyRepo.kt:1080) is already a single app call. Whether it costs one request per contact inside the SDK is being measured with 62 contacts.
Loading state
During the wait the only indicator is a small spinner replacing the Delete Profile label (ProfileEditForm.kt:243). Save, Cancel and the fields are disabled and Back is ignored. If the form is scrolled, the spinner can be off screen for most of the ~50 s. iOS shows a full-screen dim overlay with a spinner (EditProfileView.swift:43-56). Aligning the two, or adding a step label, needs a design; Figma has no such state today.
Reproduction: attribute the requests
- Use a disposable dev wallet with a profile created through Bitkit and Paykit on.
- Capture
adb logcatwithpaykitlines, and enable request-level logging in the Paykit FFI logger (method + path per request). - Open Profile › Edit Profile › Delete Profile › Yes, Delete, and note the tap time.
- Map each request between the tap and
Session signed outto the Bitkit call that issued it:removePublishedEndpointsForCleanup,contactRecords,removeContacts,deletePaykitProfile,removeBitkitPaymentEndpoints,signOut. Count duplicates. - Compare the time from tap to onboarding against the baseline above.
Related: #1511 (contention with background work, addressed by #1510).
SDK side: pubky/paykit-rs#197
With 62 contacts (Android, #1510 at f22714b; master runs pending)
| Run | Contacts deleted | Session signed out | Total | Resolving homeserver lines |
|---|---|---|---|---|
| Delete right after importing 62 contacts | +82 s | +123 s | 127 s | 270 |
| Delete after ~2 min idle | +31.7 s | +371.5 s | 373 s | 204 |
- Request count grows with contacts: 96 at 0 contacts, 204–270 at 62.
- The idle run also hit a 300 s uncertain-write cooldown. After one shared-state write during endpoint cleanup could not be confirmed, the SDK waited under its shared-state lock for 300 s before sign-out could run:
09:55:29.745 W paykit_sdk::storage::pubky_shared: shared-state commit could not be confirmed; retaining write marker
09:55:31.286 W paykit_sdk::storage::pubky_shared: waiting under shared-state lock for uncertain writes pending_writes=1 cooldown_seconds=300
10:00:31.314 I paykit_sdk::storage::pubky_shared: shared-state uncertain-write cooldown completed
The app then logged Failed to remove Bitkit payment endpoints [code=transport_error, context=write encrypted Pubky shared state could not be confirmed], with a suppressed policy_error: cannot disable Paykit app capability private_payments ….
On a deletion the shared state is about to be removed anyway. Whether the cooldown has to block the user for 5 minutes on that path is a question for the SDK.
With 62 contacts (iOS, synonymdev/bitkit-ios#954 at d7e0e1f; master runs pending)
| Run | Total |
|---|---|
| Delete right after importing 62 contacts | 58.9 s |
| Delete after ~2 min idle | 59.6 s |
Neither iOS run hit the uncertain-write cooldown. Both log Failed to remove public Paykit state before clearing session: Policy(code: "policy_error", context: "cannot disable Paykit app capability private_payments while app-owned work is still active") at sign-out, the same policy error as the Android idle run.
Activity