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

            Bug ID: 524947
           Summary: Akonadi's agent registration path makes blocking D-Bus
                    calls on login
    Classification: Frameworks and Libraries
           Product: Akonadi
      Version First unspecified
       Reported In:
          Platform: Arch Linux
                OS: Linux
            Status: REPORTED
          Severity: major
          Priority: NOR
         Component: server
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]
  Target Milestone: ---

Created attachment 195609
  --> https://bugs.kde.org/attachment.cgi?id=195609&action=edit
Extra details (text files)

DESCRIPTION

Akonadi's agent registration path makes blocking D-Bus calls in both directions
at the same time, on both processes' main threads. akonadi_control blocks
waiting on an agent, while every `Akonadi::AgentManager` client (including the
agents themselves, and plasmashell via the `pimevents` calendar plugin) blocks
waiting on akonadi_control. Neither side can service the other's call because
neither is running its event loop.

Nothing breaks the deadlock except the 25 second default D-Bus reply timeout.
Startup then proceeds by one agent, and deadlocks again. With five Google
resources configured, a cold Akonadi start takes just over two minutes, and
plasmashell is completely frozen for that entire time on every login.

Every measured delay is an exact multiple of 25000 ms. No actual work is
happening during any of it.

STEPS TO REPRODUCE

1. Configure five `akonadi_google_resource` instances (five Google accounts),
plus a few local resources. Nine agent instances total in this case.
2. Enable the `pimevents` plugin in the Plasma digital clock applet, which is
what makes plasmashell an `Akonadi::AgentManager` client.
3. Log in to a Plasma Wayland session, or equivalently run `akonadictl restart`
in a running session.

OBSERVED RESULT

The desktop appears and then freezes solidly. Panels do not respond, the task
manager is stuck on the first frame of its layout animation, no hover events
land. kwin and krunner are separate processes and stay usable, so the session
is not fully dead, but plasmashell is unusable for 124 to 150 seconds depending
on agent count.

akonadi_control logs a run of these, spaced 25 or 50 seconds apart:

akonadi_control[1813498]: "D-Bus communication error
'org.freedesktop.DBus.Error.NoReply':
'Did not receive a reply. Possible causes include: the remote application did
not send a
reply, the message bus security policy blocked the reply, the reply timeout
expired, or
the network connection was broken.'"

Login boot timeline, thirteen agent instances:

21:42:09.688  Akonadi server is now operational
21:42:09.765  3 google resources begin startup
21:42:59.859  +50s   2x NoReply   ->  4th google resource begins
21:43:49.857  +50s   1x NoReply   ->  5th google resource begins
21:44:39.858  +50s   1x NoReply   ->  settled

150 seconds total, four timeout rounds. Trimming to nine agent instances gave
124 seconds and still four rounds, so the count of configured agents is not the
main driver.

EXPECTED RESULT

Agent registration completes without blocking either process' main thread.
plasmashell, and any other `Akonadi::AgentManager` client, stays responsive
while Akonadi starts.

SOFTWARE/OS VERSIONS

Operating System: Arch Linux
KDE Plasma Version: 6.7.4
KDE Frameworks Version: 6.29.0
Qt Version: 6.11.2
Kernel Version: 7.1.9-zen1-2-zen (64-bit)
Graphics Platform:  Wayland
akonadi: 26.08.0
kdepim-runtime: 26.08.0
libkgapi: 26.08.0

ADDITIONAL INFORMATION

Both halves of the deadlock

Captured with `eu-stack` against a controlled `akonadictl restart`, with
plasmashell stopped beforehand so it was not a participant. debuginfod resolves
the symbols fine.

akonadi_control, main thread, identical at t+12s and t+40s:

#3 QWaitCondition::wait(QMutex*, QDeadlineTimer)
#4 QDBusPendingCallPrivate::waitForFinished()
#5 AgentInstance::obtainAgentInterface()
#6 AgentManager::serviceOwnerChanged(QString const&, QString const&, QString
const&)
#7 QObject::event(QEvent*)

#16 QCoreApplication::exec()
#17 main

akonadi_google_resource, main thread, at the same instant. This stack is
identical in every agent, and identical in plasmashell:

#3 QWaitCondition::wait(QMutex*, QDeadlineTimer)
#4 QDBusPendingCallPrivate::waitForFinished()
#5 QDBusPendingReplyBase::argumentAt(int) const
#6 QDBusPendingReply<QString>::argumentAt<0>() const
#7 Akonadi::AgentManagerPrivate::fillAgentInstance(QString const&) const
#8 Akonadi::AgentManagerPrivate::agentInstanceAdded(QString const&)
#9 doActivate<false>(QObject*, int, void**)
#10 OrgFreedesktopAkonadiAgentManagerInterface::qt_static_metacall(...)
#12 QDBusConnectionPrivate::deliverCall(QObject*, QDBusMessage const&, ...)

#23 QCoreApplication::exec()
#24 int Akonadi::ResourceWidgetBase::init<GoogleResource>(int, char**)

So `AgentManager::serviceOwnerChanged` calls into the agent and blocks, while
the agent is inside its own `agentInstanceAdded` handler calling back into
akonadi_control and blocking. Both are on the main thread, inside event
delivery, so neither can answer.

Which calls, and how long they took

>From `dbus-monitor --session --profile` over the same restart, reply latency
matched by serial:

 74.5s  ->  org.freedesktop.Akonadi.Control    AgentManager.agentInstanceType
 50.0s  ->  org.freedesktop.Akonadi.Control    AgentManager.agentInstanceOnline
 50.0s  ->  org.freedesktop.Akonadi.Control   
AgentManager.agentInstanceAccountId
 50.0s  ->  akonadi_google_resource_2          Agent.Account.accountId
 50.0s  ->  akonadi_google_resource_2          Resource.name
 25.0s  ->  akonadi_google_resource_0          Resource.name

Client to control: `agentInstanceType`, `agentInstanceOnline`,
`agentInstanceAccountId`, all from `fillAgentInstance`. Control to agent:
`Resource.name` and `Agent.Account.accountId`, both from
`obtainAgentInterface`.

plasmashell is a bystander

plasmashell has no Akonadi-specific code in its stack beyond the client
library. It blocks purely because `pimevents` makes it an
`Akonadi::AgentManager` client, and every client blocks in `agentInstanceAdded`
for as long as akonadi_control is stuck. Any process using AgentManager will do
the same. kalendarac does too.

An Akonadi that is already warm never triggers this: restarting plasmashell
against a settled Akonadi produces no block at all, because no
`agentInstanceAdded` signals fire. Likewise, restarting a single agent on its
own is instant and clean. It takes a mass registration to form the cycle.

Side effect: the initial Google sync fails every time

Because the resources' event loops are frozen through the 25 second windows,
the HTTP/2 connections to Google time out and are closed server side. Every
cold start logs:

akonadi_google_resource[1813852]: stream 1 error: "Connection closed"
akonadi_google_resource[1813852]: stream 1 finished with error: "HTTP/2
protocol error"
akonadi_google_resource[1813852]: Got error: KGAPI2::CalendarFetchJob(0x...)
"Invalid response content type"
akonadi_google_resource[1813852]: Got error: KGAPI2::TaskListFetchJob(0x...)
"Invalid response content type"

These land exactly on the timeout boundaries. Calendars only populate on a
later retry. Fixing the deadlock should fix this too.

Ruled out

- Not KWallet. All five OAuth tokens are present in the `Akonadi Google` folder
and read back in 3 ms.
- Not the network. No TCP connections are open at all while the agents are
stalled.
- Not the database. MariaDB check and schema init complete in 0.4 s.
- Not agent count. Thirteen agents took 150 s, nine took 124 s, both four
rounds.

Suggested direction

`AgentManagerPrivate::fillAgentInstance` fetches type, online state and account
id with three separate blocking replies, from inside a signal handler. Making
those async, or having control push the instance data along with the
`agentInstanceAdded` signal instead of making clients ask for it, would remove
the client half of the cycle. The control half,
`AgentInstance::obtainAgentInterface`, would want the same treatment.

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

Reply via email to