https://bugs.kde.org/show_bug.cgi?id=521060

            Bug ID: 521060
           Summary: kalendarac: unbounded memory growth in
                    ETMCalendar::mItemsByCollection
    Classification: Applications
           Product: konsolekalendar
      Version First unspecified
       Reported In:
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: general
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

PROBLEM
-------
The kalendarac daemon exhibits unbounded memory growth over time, with RSS
reaching 2 GB or more under normal use. The issue is more pronounced with
calendars that produce frequent item updates (e.g., a work calendar with
recurring changes). A workaround of restarting the service once it exceeded
~400 MB has been necessary.

ROOT CAUSE
----------
mItemsByCollection in CalendarBasePrivate is a
QMultiHash<CollectionId, Akonadi::Item> used to index items by their
collection. At runtime, ETMCalendarPrivate::updateItem() is the path
called for every item-change notification from the Akonadi ETM, and the
code did:

    // Before (leaks):
    mItemsByCollection.insert(item.storageCollectionId(), item);

This always appends and never removes the stale entry. Worse, implicit
remove() calls elsewhere also fail silently, because
QMultiHash::remove(key, value) uses Akonadi::Item::operator==, which
compares both id and revision. Since the revision changes on every
Akonadi update, the old entry is never found and never erased.

Since itemsAdded() only runs at startup and updateItem() is the exclusive
runtime path, every single update accumulates a duplicate entry. Memory
growth is proportional to the total number of updates over the daemon's
lifetime, not the number of distinct items.

VALIDATION (BEFORE FIX)
-----------------------
Tested with a work calendar source over 7 hours of continuous operation:

- RSS: unbounded growth, 2 GB+
- memCal vs itemById size: divergent
- Duplicate entries in mItemsByCollection: accumulate indefinitely
- Updates processed: ~25,827 with no growth plateau

Heaptrack confirmed the leak site; icalmemory_new_buffer fragmentation
from libical is expected parser behavior, not the leak.

FIX
---
A merge request is already open with a fix:

  https://invent.kde.org/pim/akonadi-calendar/-/merge_requests/176

The fix introduces CalendarBasePrivate::upsertItemInCollectionIndex(),
which:

  1. Looks up the existing item by id() in mItemById.
  2. Uses equalRange(collectionId) and iterates to find the entry
     matching item.id() (revision-independent lookup).
  3. Erases the old entry via iterator.
  4. Inserts the new entry.

Applied in both internalInsert() and updateItem().

After the fix on the same workload:
  - RSS: stable ~300 KB
  - memCal vs itemById: consistent
  - Duplicate entries: zero drift

AFFECTED VERSIONS
-----------------
Reproducible on master / current akonadi-calendar (KF6). Likely also
affects the KF5-based releases shipped by distributions.

REPRODUCTION
------------
1. Run kalendarac connected to an Akonadi resource that emits frequent
   item changes (e.g., a calendar synced with a server, or a local
   resource that is being edited repeatedly).
2. Observe RSS over several hours:
     watch -n 60 ps -o rss= -C kalendarac
   RSS grows monotonically and without bound, correlated with the number
   of item-change notifications received.
3. Inspect mItemsByCollection via debugger or logging; the number of
   entries diverges from mItemById, with one new duplicate accumulated
   per update.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to