fix(#306): stop rewriting the whole prefs file per channel on connect

The connect freeze is a legacy-key migration that writes even when there is
nothing to migrate.

loadSmazEnabled called prefs.remove(oldKey) unconditionally whenever the
scoped key was absent, which is the normal case for a channel that has never
had the setting changed. On Windows shared_preferences re-serialises and
rewrites the ENTIRE prefs file on any mutation, so each of those no-op removes
cost a full multi-MB write. Repeated across every channel slot on every
connect, that is tens of full-file rewrites back to back.

Measured with the instrumentation from the previous commit, on the reporting
device (6.9 MB prefs, ~42 channel slots):

  startup-load channelOrder          0ms
  startup-load contactCache         12ms
  startup-load channelSettings   37792ms   <-- here
  startup-load cachedChannels        0ms
  startup-load channelMessages      21ms
  startup-load unreadState           0ms
  startup-load discoveredContacts   11ms

No frame handler exceeded 100ms, confirming the stall was local storage work
and not radio traffic.

The remove now happens only when a legacy key actually exists, alongside the
write that migrates it. contact_settings_store had the identical pattern per
contact and is fixed the same way.

This also explains the platform gap in the original report: Android's backend
batches into native storage, so the same code costs almost nothing there,
while every no-op remove on Windows is a full file rewrite.

flutter analyze clean, dart format clean, 469 tests pass.
integration/290-306-335
Strycher 2 days ago
parent bd8779697a
commit 4ee2f50703

@ -25,13 +25,18 @@ class ChannelSettingsStore {
bool? enabled = prefs.getBool(key);
if (enabled == null) {
// Attempt migration from legacy unscoped key on first load
// Only touch storage when a legacy key actually exists. This ran
// unconditionally, and on Windows shared_preferences re-serialises and
// rewrites the WHOLE prefs file on every mutation, so removing a key
// that was never there still cost a full multi-MB write. Repeated per
// channel/contact on connect, that blocked the UI isolate for ~38s.
enabled = prefs.getBool(oldKey);
prefs.remove(oldKey);
if (enabled != null) {
appLogger.info(
'Migrating channel settings from legacy key $oldKey to scoped key $key',
);
await prefs.setBool(key, enabled);
await prefs.remove(oldKey);
}
}
return enabled ?? false;

@ -25,13 +25,18 @@ class ContactSettingsStore {
bool? enabled = prefs.getBool(key);
if (enabled == null) {
// Attempt migration from legacy unscoped key on first load
// Only touch storage when a legacy key actually exists. This ran
// unconditionally, and on Windows shared_preferences re-serialises and
// rewrites the WHOLE prefs file on every mutation, so removing a key
// that was never there still cost a full multi-MB write. Repeated per
// channel/contact on connect, that blocked the UI isolate for ~38s.
enabled = prefs.getBool(oldKey);
prefs.remove(oldKey);
if (enabled != null) {
appLogger.info(
'Migrating contact settings from legacy key $oldKey to scoped key $key',
);
await prefs.setBool(key, enabled);
await prefs.remove(oldKey);
}
}
return prefs.getBool(key) ?? false;

Loading…
Cancel
Save

Powered by TurnKey Linux.