Skip to content

Commit

Permalink
Refactor optimized relation reader
Browse files Browse the repository at this point in the history
  • Loading branch information
flash-gordon committed Jan 8, 2025
1 parent 520bc19 commit 01355a9
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 52 deletions.
4 changes: 2 additions & 2 deletions core/lib/rom/plugins/relation/registry_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class RegistryReader < ::Module
EMPTY_REGISTRY = RelationRegistry.build(EMPTY_HASH).freeze

# @api private
def initialize(klass:, relation_readers_module:)
def initialize(readers:)
super()
klass.include relation_readers_module
include readers
end

# @api private
Expand Down
18 changes: 8 additions & 10 deletions core/lib/rom/setup/finalize/finalize_relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ class Finalize
class FinalizeRelations
attr_reader :notifications

module BuildRelationReaders
def self.build(relations)
Module.new do
relations.each do |name|
define_method(name) do
__registry__[name]
end
end
class RegistryReaders < ::Module
def initialize(relations)
super()

relations.each do |name|
define_method(name) { __registry__[name] }
end
end
end
Expand Down Expand Up @@ -44,7 +42,7 @@ def initialize(gateways, relation_classes, notifications:, mappers: nil, plugins
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def run!
relation_registry = RelationRegistry.new do |registry, relations|
relation_readers_module = BuildRelationReaders.build(relation_names)
registry_readers = RegistryReaders.new(relation_names)
@relation_classes.each do |klass|
unless klass.adapter
raise MissingAdapterIdentifierError,
Expand All @@ -58,7 +56,7 @@ def run!
"Relation with name #{key.inspect} registered more than once"
end

klass.use(:registry_reader, klass: klass, relation_readers_module: relation_readers_module)
klass.use(:registry_reader, readers: registry_readers)

notifications.trigger(
'configuration.relations.class.ready',
Expand Down
2 changes: 1 addition & 1 deletion repository/lib/rom/repository/class_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def new(container = nil, **options)
container ||= options.fetch(:container)

unless relation_reader
relation_reader(RelationReader.new(self, container.relations.elements.keys))
relation_reader(RelationReader.new(container.relations.elements.keys))
include(relation_reader)
end

Expand Down
86 changes: 50 additions & 36 deletions repository/lib/rom/repository/relation_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,84 @@ class RelationReader < ::Module
extend ::Dry::Core::ClassAttributes

# @api private
attr_reader :klass

# @api private
attr_reader :relations

defines :relation_readers

defines :mutex
mutex(Mutex.new)

defines :relation_cache
relation_cache(Concurrent::Hash.new)
# @api private
defines :lock
lock ::Mutex.new

module InstanceMethods
# @api private
def set_relation(name) # rubocop:disable Naming/AccessorMethodName
def prepare_relation(name)
container
.relations[name]
.with(auto_struct: auto_struct, struct_namespace: struct_namespace)
.with(
auto_struct: auto_struct,
struct_namespace: struct_namespace
)
end

# @api private
def relation_reader(name, relation_cache)
key = [name, auto_struct, struct_namespace]
relation_cache[key] ||= set_relation(name)
relation_cache[key] ||= prepare_relation(name)
end
end

# @api private
def mutex
ROM::Repository::RelationReader.mutex
end
class Readers < ::Module
# @api private
attr_reader :cache

# @api private
def initialize(klass, relations)
super()
@relations = relations
mutex.synchronize do
unless self.class.relation_readers
self.class.relation_readers(
build_relation_readers(relations, self.class.relation_cache)
)
def initialize(relations)
super()
cache = ::Concurrent::Hash.new

relations.each do |name|
define_readers_for_relation(name, cache)
end
end

# @api private
def define_readers_for_relation(name, cache)
define_method(name) do
relation_reader(name, cache)
end
end
klass.include self.class.relation_readers
end

# @api private
def included(klass)
super
klass.include(InstanceMethods)
end
def lock = self.class.lock

private
# @api private
def initialize(relations)
super()

include relation_readers(relations)
end

# @api private
def build_relation_readers(relations, relation_cache)
Module.new do
relations.each do |name|
define_method(name) do
relation_reader(name, relation_cache)
def relation_readers(relations)
existing = self.class.relation_readers

if existing.nil?
lock.synchronize do
unless self.class.relation_readers
self.class.relation_readers Readers.new(relations)
end
end

self.class.relation_readers
else
existing
end
end

# @api private
def included(klass)
super
klass.include(InstanceMethods)
end
end
end
end
2 changes: 1 addition & 1 deletion repository/lib/rom/repository/root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def self.inherited(klass)
# @see Repository#initialize
def initialize(*, **)
super
@root = set_relation(self.class.root)
@root = prepare_relation(self.class.root)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion repository/spec/integration/plugin_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.apply(target, **)
target.prepend(self)
end

def set_relation(*)
def prepare_relation(*)
super.where { `1 = 0` }
end
end
Expand Down
1 change: 0 additions & 1 deletion repository/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def self.remove_constants
config.after do
Test.remove_constants
ROM::Repository::RelationReader.relation_readers(nil)
ROM::Repository::RelationReader.relation_cache(Concurrent::Hash.new)
end

Dir[SPEC_ROOT.join('support/*.rb').to_s].each do |f|
Expand Down

0 comments on commit 01355a9

Please sign in to comment.