Plugin upgrade and rollback: the old code keeps running until a restart, though the page says none is needed #599
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#599
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, reading how the registry loads plugins and checkingEntryPoint.load()against a module rewritten on disk.Related to #228.
Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atplugins/installing.py:1072-1088(_forget_metadata_cache),core/server_views.py:1395-1402andplugins/views.py:113-114;plugins/registry.py:190-194and:237-271andplugins/management/commands/plugins.py:92-95have not moved.catch_upnow takesactivateandrecord_stampfromplugins/record.py(#248), and nothing insrctouchessys.modules.Every process loads a plugin with
entry_point.load(), which imports its module. After an upgrade or aplugins rollback,catch_up()and_forget_metadata_cache()rebuild the registry, but the plugin's modules are still insys.modules, soload()hands back the old classes. Nothing insrcevicts them. The upload confirmation and the management command nevertheless say the new version is in use everywhere and that a restart would add nothing.plugins/registry.py:190-194(_load_third_party:plugin = entry_point.load()()),plugins/registry.py:237-271(catch_up, which clears the cache and the metadata finder but notsys.modules),plugins/installing.py:1246-1262(_forget_metadata_cache),core/server_views.py:1394-1401("every part of this instance is using it already. A restart would add nothing"),plugins/management/commands/plugins.py:92-95("It is in use everywhere already"), and the comment atplugins/views.py:112-113("reinstalling restarts the process").--max-requests; the scheduler is never retired. Any submodule the plugin imports for the first time after the upgrade (apprise imports lazily) comes from 1.1's files, so a process can run a mixture of both versions. Rolling back a broken upgrade withplugins rollbacklikewise leaves the broken code loaded. Checked: anEntryPointforprobe_mod:Ploaded version 1.0; after the file was rewritten as 2.0 andimportlib.invalidate_caches()called,load()still returned 1.0.RECORD, or found throughimportlib.metadata.packages_distributions(), fromsys.modules), or tell the truth: when the record already had a line for that name, say a restart is needed, and keep the "no restart" wording for first installs.Done when
tests/test_plugin_rollback.pyhas a test that installs version 1.0 of a wheel, loads its source throughregistry.plugins("source"), installs 2.0, and asserts that the registry's instance reports 2.0 (or, with the second fix, that the confirmation for an upgrade says a restart is needed).