fix(#306): guard every legacy-key removal, not just the channel-settings one

The previous commit fixed loadSmazEnabled and killed the post-connect stall
(startup-load channelSettings: 37792ms -> 0ms, measured twice). The contact
pull still choked, in multi-second bursts, each landing immediately after an
"Added new contact" line.

No frame handler exceeded the 100ms threshold, because the work is not in a
handler: the contact handler fires _loadMessagesForContact unawaited, so it
runs after the handler returns and escapes that timing.

message_store.loadMessages had the same unconditional prefs.remove(oldKey),
and it runs ONCE PER CONTACT during a pull. On Windows every prefs mutation
rewrites the entire file, so a 234-contact pull meant 234 full multi-MB writes
for legacy keys that do not exist.

A sweep found the identical pattern in six more stores: contact_store,
unread_store, channel_store, community_store, contact_group_store and
channel_order_store. Those run once per load rather than per item, so they are
far cheaper, but it is the same bug and they are fixed the same way.

In every case the removal now happens only when a legacy key actually exists,
paired with the write that migrates it, so the cleanup still occurs on a real
migration.

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

@ -29,13 +29,15 @@ class ChannelOrderStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating channel order from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating channel order from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -22,13 +22,15 @@ class ChannelStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -28,13 +28,15 @@ class CommunityStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating communities from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating communities from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -21,13 +21,15 @@ class ContactGroupStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -23,13 +23,15 @@ class ContactStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating contacts from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating contacts from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -40,13 +40,18 @@ class MessageStore {
String? jsonString = prefs.getString(key); String? jsonString = prefs.getString(key);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only touch storage when a legacy key actually exists. This ran
// unconditionally, and loadMessages is called once per contact during a
// contact pull. On Windows shared_preferences rewrites the WHOLE prefs
// file on every mutation, so each no-op remove cost a full multi-MB
// write - hundreds of them back to back on a large address book.
final legacyJsonString = prefs.getString(oldKey); final legacyJsonString = prefs.getString(oldKey);
prefs.remove(oldKey);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating messages from legacy key $oldKey to scoped key $key', 'Migrating messages from legacy key $oldKey to scoped key $key',
); );
await prefs.setString(key, legacyJsonString); await prefs.setString(key, legacyJsonString);
await prefs.remove(oldKey);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

@ -35,13 +35,15 @@ class UnreadStore {
String? jsonString = prefs.getString(keyFor); String? jsonString = prefs.getString(keyFor);
if (jsonString == null || jsonString.isEmpty) { if (jsonString == null || jsonString.isEmpty) {
// Attempt migration from legacy unscoped key on first load // Attempt migration from legacy unscoped key on first load
// Only remove the legacy key when it actually exists: every prefs
// mutation rewrites the whole file on Windows.
final legacyJsonString = prefs.getString(_keyPrefix); final legacyJsonString = prefs.getString(_keyPrefix);
prefs.remove(_keyPrefix);
if (legacyJsonString != null && legacyJsonString.isNotEmpty) { if (legacyJsonString != null && legacyJsonString.isNotEmpty) {
appLogger.info( appLogger.info(
'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor',
); );
await prefs.setString(keyFor, legacyJsonString); await prefs.setString(keyFor, legacyJsonString);
await prefs.remove(_keyPrefix);
jsonString = legacyJsonString; jsonString = legacyJsonString;
} }
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.