Skip to content

EventStorage grows unbounded, no cleanup when sessions expire #537

@Catrya

Description

@Catrya

eventStore (Sembast) accumulates events indefinitely. When SessionNotifier._cleanup() removes expired sessions (after 72h), the associated events in eventStore are never deleted. The only way events are removed is through full-reset operations (user restore or key reset), which wipe the entire store.

Sources of accumulation
Three writers append to eventStore:

Writer Type tag What's stored Size per event
MostroService._onData() (none) id + created_at only ~100 bytes
ChatRoomNotifier._onChatEvent() / sendMessage() 'chat' Full gift wrap event (encrypted content, tags, sig, pubkey) ~1-5 KB
DisputeChatNotifier._onChatEvent() / sendMessage() 'dispute_chat' Full gift wrap event ~1-5 KB

Why nothing gets cleaned up
SessionNotifier._cleanup() runs every 30 minutes and deletes sessions older than 72h from sessionStorage:

  void _cleanup() async {                                                                                             
    final cutoff = DateTime.now()                           
        .subtract(const Duration(hours: Config.sessionExpirationHours));
    for (final session in expiredSessions) {                                                                          
      if (session.startTime.isBefore(cutoff)) {
        await _storage.deleteSession(session.orderId!);  // only sessionStorage                                       
        _sessions.remove(session.orderId!);                                                                           
      }
    }                                                                                                                 
  }                  

It never touches eventStore. The chat/dispute events for that orderId remain in the database forever.

The only two deletion paths are full wipes:

  • RestoreManager._clearAll()eventStorageProvider.deleteAll()
  • KeyManagementScreen (reset identity)eventStorage.deleteAll()

Impact

  • Low-frequency users: Negligible, a few dozen events per trade, a few trades per month
  • High-frequency users over months: Unbounded growth of encrypted blobs in Sembast. No upper limit.
  • MostroService dedup entries: Every Mostro protocol event (order updates, state changes) adds a stub entry (id + created_at) that is never cleaned up, even after the order is long gone

Suggested fix
When SessionNotifier._cleanup() deletes an expired session, also delete its associated events:

After deleting the session:

  final eventStore = ref.read(eventStorageProvider);        
  await eventStore.deleteWhere(Filter.equals('order_id', session.orderId));                                           

For MostroService dedup entries (which have no order_id or type field), a created_at-based cleanup could be added:

Periodically remove dedup stubs older than session expiration

  await eventStore.deleteWhere(                                                                                       
    Filter.and([                                                                                                      
      Filter.notEquals('type', 'chat'),                                                                               
      Filter.notEquals('type', 'dispute_chat'),                                                                       
      Filter.lessThan('created_at', cutoffTimestamp),                                                                 
    ]),
  );                                                                                                                  

Additional context

  • Config.sessionExpirationHours = 72
  • Config.cleanupIntervalMinutes = 30
  • BaseStorage already has deleteWhere(Filter), no new infrastructure needed
  • Chat and dispute events are tagged with order_id, making selective deletion straightforward

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions