Skip to content

Prevent ThreadedWorker from leaking enqueue lifecycle callbacks - #5358

Open
philippthun wants to merge 1 commit into
cloudfoundry:mainfrom
sap-contributions:fix-threaded-worker-lifecycle-leak
Open

Prevent ThreadedWorker from leaking enqueue lifecycle callbacks#5358
philippthun wants to merge 1 commit into
cloudfoundry:mainfrom
sap-contributions:fix-threaded-worker-lifecycle-leak

Conversation

@philippthun

@philippthun philippthun commented Aug 11, 2026

Copy link
Copy Markdown
Member

Delayed::Plugin callbacks register against Delayed::Worker.lifecycle (the base class), and Delayed::Job.enqueue runs that same base lifecycle. Delayed::Worker#initialize calls self.class.setup_lifecycle, which rebuilds the lifecycle on the class it is called on. Since ThreadedWorker is a subclass, setup_lifecycle reset a different lifecycle than the base one the plugins append to. The base lifecycle was therefore never reset, and every ThreadedWorker.new added another before(:enqueue) callback to it.

The before(:enqueue) callback creates a PollableJobModel row, so the accumulation caused a single enqueue to create many pollable rows. Production is unaffected since a worker is created once per process.

Delegate setup_lifecycle and lifecycle on ThreadedWorker to the base class so each instantiation rebuilds the same lifecycle cleanly.

  • I have reviewed the contributing guide

  • I have viewed, signed, and submitted the Contributor License Agreement

  • I have made this pull request to the main branch

  • I have run all the unit tests using bundle exec rake

  • I have run CF Acceptance Tests

Delayed::Plugin callbacks register against Delayed::Worker.lifecycle
(the base class), and Delayed::Job.enqueue runs that same base
lifecycle. Delayed::Worker#initialize calls self.class.setup_lifecycle,
which rebuilds the lifecycle on the class it is called on. Since
ThreadedWorker is a subclass, setup_lifecycle reset a different
lifecycle than the base one the plugins append to. The base lifecycle
was therefore never reset, and every ThreadedWorker.new added another
before(:enqueue) callback to it.

The before(:enqueue) callback creates a PollableJobModel row, so the
accumulation caused a single enqueue to create many pollable rows.
Production is unaffected since a worker is created once per process.

Delegate setup_lifecycle and lifecycle on ThreadedWorker to the base
class so each instantiation rebuilds the same lifecycle cleanly.
@philippthun
philippthun requested a review from johha August 11, 2026 16:15
@philippthun
philippthun marked this pull request as ready for review August 11, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant