Skip to content

[Bug] Notes app boot() forces LazyFolder materialization on every request causing severe performance degradation #1835

@arkDisk

Description

@arkDisk

Bug description

The Notes app’s hook registration in NotesHooks.php calls listenTo() on a LazyFolder during the app’s boot() phase. This forces materialization of the LazyFolder via LazyFolder::__call()getRealFolder(), which then triggers:

  • Files\Node\Root::getValueArray()
  • AppConfig::getTypedValue()
  • AppConfig::matchAndApplyLexiconDefinition()
  • AppConfig::getLexiconPreset()

As a result, getLexiconPreset() is invoked on every Nextcloud request (index.php, ocs/v2.php, remote.php), causing severe performance degradation — especially when the lazy AppConfig backend is slow or under load.

This appears to be the same anti-pattern that was fixed for the files_external app in PR #55830 (fix(files-external): do not load lazy appconfig from construct), but Notes still uses the old approach.

Steps to reproduce

  1. Install Nextcloud 33.0.2 with Notes app 4.13.1 enabled.
  2. Enable PHP-FPM slowlog with request_slowlog_timeout = 10s.
  3. Generate normal traffic (DAV uploads, OCS calls, web UI usage).
  4. Observe slowlog accumulating thousands of entries with the stack trace below.
  5. Run occ app:disable notes — slowlog accumulation stops immediately.

Expected behavior

The Notes app should register its hooks/listeners using the modern IRegistrationContext::registerEventListener() pattern in the register() method, not by calling listenTo() on a LazyFolder during boot().

LazyFolder should only materialize on-demand when an event actually fires, not on every request during boot.

Actual behavior

Every Nextcloud request triggers full materialization of LazyFolder and lazy AppConfig loading through the Notes app boot process, leading to:

  • Heavy CPU load on every request
  • ~20,700 slowlog entries in 24 hours on a moderately used instance
  • Visible 30+ second hangs for end users during file uploads and folder browsing
  • Effective service unavailability under sustained traffic

In our case, this caused approximately 12 hours of degraded service before we identified and disabled the Notes app.

Stack trace

Reproduced 1161 times in a single 24-hour log file. Sample entry:

[24-Apr-2026 04:02:28]  [pool www] pid 912049
script_filename = /var/www/nextcloud/ocs/v2.php
[0x...] getLexiconPreset() /var/www/nextcloud/lib/private/AppConfig.php:1726
[0x...] matchAndApplyLexiconDefinition() /var/www/nextcloud/lib/private/AppConfig.php:481
[0x...] getTypedValue() /var/www/nextcloud/lib/private/AppConfig.php:453
[0x...] getValueArray() /var/www/nextcloud/lib/private/Files/Node/Root.php:83
[0x...] __construct() /var/www/nextcloud/lib/private/Server.php:401
[0x...] {closure:OC\Server::__construct():396}() /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php:186
[0x...] offsetGet() /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php:149
[0x...] query() /var/www/nextcloud/lib/private/ServerContainer.php:155
[0x...] query() /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php:46
[0x...] get() /var/www/nextcloud/lib/private/Server.php:433
[0x...] {closure:{closure:OC\Server::__construct():431}:432}() /var/www/nextcloud/lib/private/Files/Node/LazyFolder.php:51
[0x...] call_user_func() /var/www/nextcloud/lib/private/Files/Node/LazyFolder.php:51
[0x...] getRealFolder() /var/www/nextcloud/lib/private/Files/Node/LazyFolder.php:65
[0x...] __call() /var/www/nextcloud/lib/private/Files/Node/LazyFolder.php:79
[0x...] listen() /var/www/nextcloud/apps/notes/lib/AppInfo/NotesHooks.php:75
[0x...] listenTo() /var/www/nextcloud/apps/notes/lib/AppInfo/NotesHooks.php:35
[0x...] register() /var/www/nextcloud/apps/notes/lib/AppInfo/Application.php:47
[0x...] {closure:OCA\Notes\AppInfo\Application::boot():46}() /var/www/nextcloud/lib/private/AppFramework/Bootstrap/FunctionInjector.php:28
[0x...] injectFn() /var/www/nextcloud/lib/private/AppFramework/Bootstrap/BootContext.php:32

Suggested fix

Refactor NotesHooks.php and Application.php to use IRegistrationContext::registerEventListener() for the relevant filesystem events (NodeCreatedEvent, NodeDeletedEvent, etc.) instead of calling listenTo() on a LazyFolder during boot.

This should ensure that:

  • listeners are registered without instantiating the Root folder
  • LazyFolder remains lazy until actually needed
  • lazy AppConfig is not loaded on every request

Environment

  • Notes app version: 4.13.1
  • Nextcloud version: 33.0.2
  • OS: Debian 13
  • Web server: Apache 2.4
  • PHP version: 8.4
  • Database: MariaDB
  • Storage: NFS-backed ZFS RAIDZ2

Related

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions