Skip to content

fix: unlock mailboxes after OOM#12411

Merged
kesselb merged 1 commit intomainfrom
fix/oom-unlock-mailbox
Feb 13, 2026
Merged

fix: unlock mailboxes after OOM#12411
kesselb merged 1 commit intomainfrom
fix/oom-unlock-mailbox

Conversation

@ChristophWurst
Copy link
Member

In contrast to try-catch and an error handler we can leverage a shutdown handler to still unlock mailboxes after running out of memory.

$ MEMPROF_PROFILE=dump_on_limit php -d memory_limit=59M ../../occ mail:account:sync 4783 -vvv
The current PHP memory limit is below the recommended value of 512MB.
[debug] account is up to date, skipping mailbox sync
[debug] Skipping mailbox sync for 8992
[debug] Skipping mailbox sync for 8993
[debug] Skipping mailbox sync for 8994
[debug] Skipping mailbox sync for 8995
[debug] Skipping mailbox sync for 8996
[debug] Skipping mailbox sync for 8997
[debug] Skipping mailbox sync for 8998
[debug] Skipping mailbox sync for 8999
[debug] Syncing 9000
[debug] Locking mailbox 9000 for new messages sync
[debug] Locking mailbox 9000 for changed messages sync
[debug] Locking mailbox 9000 for vanished messages sync
[debug] Running partial sync for 9000with criteria 42
[debug] partial sync 4783:Sent - get all known UIDs took 0s. 53/55MB memory used
[debug] partial sync 4783:Sent - get new messages via Horde took 1s. 54/55MB memory used
[debug] partial sync 4783:Sent - persist new messages took 0s. 55/55MB memory used
[debug] partial sync 4783:Sent - get changed messages via Horde took 0s. 55/55MB memory used
[debug] partial sync 4783:Sent - persist changed messages took 0s. 55/55MB memory used
[debug] partial sync 4783:Sent - get vanished messages via Horde took 0s. 55/55MB memory used
[debug] partial sync 4783:Sent - delete vanished messages took 0s. 55/55MB memory used
[debug] partial sync 4783:Sent took 1s
[debug] Unlocking mailbox 9000 from vanished messages sync
[debug] Unlocking mailbox 9000 from changed messages sync
[debug] Unlocking mailbox 9000 from new messages sync
[debug] Skipping mailbox sync for 9001
[debug] Skipping mailbox sync for 9002
[debug] Skipping mailbox sync for 9003
[debug] Skipping mailbox sync for 9004
[debug] Skipping mailbox sync for 9005
[debug] Skipping mailbox sync for 9006
[debug] Skipping mailbox sync for 9007
[debug] Skipping mailbox sync for 9008
[debug] Skipping mailbox sync for 9009
[debug] Skipping mailbox sync for 9010
[debug] Skipping mailbox sync for 9011
[debug] Skipping mailbox sync for 9012
[debug] Syncing 9013
[debug] Locking mailbox 9013 for new messages sync
[debug] Locking mailbox 9013 for changed messages sync
[debug] Locking mailbox 9013 for vanished messages sync
[debug] Running partial sync for 9013with criteria 42
PHP Fatal error:  Allowed memory size of 61865984 bytes exhausted (tried to allocate 20480 bytes) (memprof dumped to /tmp/memprof.callgrind.1856999238350022) in /nextcloud/3rdparty/doctrine/dbal/src/Driver/PDO/Result.php on line 119
Stack trace:
#0 /nextcloud/3rdparty/doctrine/dbal/src/Driver/PDO/Result.php(119): PDOStatement->fetchAll(2)
#1 /nextcloud/3rdparty/doctrine/dbal/src/Driver/PDO/Result.php(59): Doctrine\DBAL\Driver\PDO\Result->fetchAll(2)
#2 /nextcloud/3rdparty/doctrine/dbal/src/Result.php(103): Doctrine\DBAL\Driver\PDO\Result->fetchAllAssociative()
#3 /nextcloud/lib/private/DB/ResultAdapter.php(60): Doctrine\DBAL\Result->fetchAllAssociative()
#4 /nextcloud/apps/mail/lib/Db/MessageMapper.php(79): OC\DB\ResultAdapter->fetchAll()
#5 /nextcloud/apps/mail/lib/Db/MessageMapper.php(141): OCA\Mail\Db\MessageMapper->findUids(Object(OC\DB\QueryBuilder\QueryBuilder))
#6 /nextcloud/apps/mail/lib/Service/Sync/ImapToDbSynchronizer.php(415): OCA\Mail\Db\MessageMapper->findAllUids(Object(OCA\Mail\Db\Mailbox))
#7 /nextcloud/apps/mail/lib/Service/Sync/ImapToDbSynchronizer.php(277): OCA\Mail\Service\Sync\ImapToDbSynchronizer->runPartialSync(Object(OCA\Mail\IMAP\HordeImapClient), Object(OCA\Mail\Account), Object(OCA\Mail\Db\Mailbox), Object(OCA\Mail\Support\ConsoleLoggerDecorator), true, 42, NULL)
#8 /nextcloud/apps/mail/lib/Service/Sync/ImapToDbSynchronizer.php(134): OCA\Mail\Service\Sync\ImapToDbSynchronizer->sync(Object(OCA\Mail\Account), Object(OCA\Mail\IMAP\HordeImapClient), Object(OCA\Mail\Db\Mailbox), Object(OCA\Mail\Support\ConsoleLoggerDecorator), 42, NULL, false, true)
#9 /nextcloud/apps/mail/lib/Command/SyncAccount.php(88): OCA\Mail\Service\Sync\ImapToDbSynchronizer->syncAccount(Object(OCA\Mail\Account), Object(OCA\Mail\Support\ConsoleLoggerDecorator), false)
#10 /nextcloud/apps/mail/lib/Command/SyncAccount.php(72): OCA\Mail\Command\SyncAccount->sync(Object(OCA\Mail\Account), false, Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /nextcloud/3rdparty/symfony/console/Command/Command.php(326): OCA\Mail\Command\SyncAccount->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 /nextcloud/3rdparty/symfony/console/Application.php(1078): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#13 /nextcloud/3rdparty/symfony/console/Application.php(324): Symfony\Component\Console\Application->doRunCommand(Object(OCA\Mail\Command\SyncAccount), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#14 /nextcloud/3rdparty/symfony/console/Application.php(175): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#15 /nextcloud/lib/private/Console/Application.php(187): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#16 /nextcloud/console.php(92): OC\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput))
#17 /nextcloud/occ(33): require_once('/nextcloud...')
#18 {main}
[debug] Unlocking mailbox 9013 from vanished messages sync
[debug] Unlocking mailbox 9013 from changed messages sync
[debug] Unlocking mailbox 9013 from new messages sync

On main I would have to wait 5 minutes or unlock the mailbox manually. Here I can try again right away.

This doesn't fix the root cause of the OOM, but it makes it a bit more pleasant to retry and continue using the app.

@ChristophWurst
Copy link
Member Author

/backport to stable5.7

Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com>
@kesselb kesselb force-pushed the fix/oom-unlock-mailbox branch from 4773c1d to 789c7d0 Compare February 13, 2026 15:37
* Important: reserve early to avoid OOM after locking but before the
* shutdown handler registration
*/
$oomBuffer = str_repeat(' ', 1024 * 1024);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$oomBuffer = str_repeat(' ', 1024 * 1024);
$oomBuffer = random_bytes(1024);

@kesselb
Copy link
Contributor

kesselb commented Feb 13, 2026

Passing $oomBuffer as reference seems to break psalm.
It seems to work also without the oomBuffer 🤷

@kesselb kesselb merged commit bb4d4b7 into main Feb 13, 2026
40 of 42 checks passed
@kesselb kesselb deleted the fix/oom-unlock-mailbox branch February 13, 2026 17:08
@github-project-automation github-project-automation bot moved this from 🏗️ In progress to ☑️ Done in 💌 📅 👥 Groupware team Feb 13, 2026
@ChristophWurst
Copy link
Member Author

Yes it probably works because we already have loaded the classes necessary for unlocking

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: ☑️ Done

Development

Successfully merging this pull request may close these issues.

2 participants