Skip to content

Fix deadlock in belongs_to/has_many :through; support polymorphic source_type#336

Open
kbrock wants to merge 3 commits into
active-hash:masterfrom
kbrock:issue_334
Open

Fix deadlock in belongs_to/has_many :through; support polymorphic source_type#336
kbrock wants to merge 3 commits into
active-hash:masterfrom
kbrock:issue_334

Conversation

@kbrock
Copy link
Copy Markdown
Collaborator

@kbrock kbrock commented Aug 18, 2025

Resolving classes at definition time can deadlock in production. AR holds a schema lock and a class-load lock. Loading one class that triggers loading another will hit both locks and hang.

Note: belongs_to_active_hash is unchanged and still available for direct use.

Thank you, @alexgriff, for the reproducer.

We undefine these classes in the after {}
So in theory, they should not be a duplicate.

rspec often uses stub_const, but active record has issues with this since
they dig into the class.name and stuff
@kbrock kbrock force-pushed the issue_334 branch 2 times, most recently from f91dc58 to b3a12f3 Compare April 4, 2026 06:07
kbrock and others added 2 commits April 6, 2026 22:20
Move class resolution out of definition time and into method bodies.
Call super first so AR registers reflections (preserving eager loading
support), then override accessors with runtime ActiveHash checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When source_type is present, resolve the class at runtime and look up
by foreign key directly, bypassing AR's polymorphic belongs_to which
does not work with ActiveHash. Fixes active-hash#334.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kbrock
Copy link
Copy Markdown
Collaborator Author

kbrock commented Apr 7, 2026

update: removed class load
update: removed another class loader (total tangent, but solves a similar issue)

@kbrock kbrock changed the title Handle belongs_to :through, :polymorphic: true Fix deadlock in belongs_to/has_many :through; support polymorphic source_type Apr 21, 2026
@kbrock kbrock requested a review from flavorjones April 21, 2026 00:21
@kbrock kbrock requested a review from zilkey May 2, 2026 22:53
Copy link
Copy Markdown
Collaborator

@flavorjones flavorjones left a comment

Choose a reason for hiding this comment

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

@kbrock Sorry for not reviewing this earlier. It looks good, I left one comment about whether using :source_type will generate N+1 queries, but I think we can just collapse that to a where(id: ids).

klass = source_type.safe_constantize
if klass < ActiveHash::Base
ids = send(options[:through]).map { |jm| jm.send(source_foreign_key) }.compact.uniq
ids.flat_map { |id| klass.find_by_id(id) }.compact
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This could potentially make a lot of queries. Could we instead do:

Suggested change
ids.flat_map { |id| klass.find_by_id(id) }.compact
klass.where(id: ids)

? If we need to keep the order we could do something like this to keep it to a single query:

records = klass.where(id: ids).index_by(&:id)
ids.filter_map { |id| records[id] }

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.

Polymorphic has_many with a source_type raise an exception in ActiveHash 4.0

2 participants