# Lazy instantiation and memoization with container auto-registration

**URL:** <https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79>\
**Category:** dry-web\
**Created:** [July 29, 2016, 4:54pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79 "2016-07-29T16:54:14Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![tom\_dalling](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/tom_dalling/32/38_2.png) [@tom\_dalling](https://discourse.dry-rb.org/u/tom_dalling)\
**Post date:** [July 29, 2016, 4:54pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/1 "2016-07-29T16:54:14Z")

</div>

I’ve got a pretty simple setup like this:

```
class App < Dry::Component::Container
  ROOT = Pathname.new( __FILE__ ).dirname.dirname

  load_paths! 'app'
  configure do |config|
    config.root = ROOT
    config.auto_register = ['app/controllers', 'app/queries', 'app/views']
  end
end

```

It’s working fine, but I noticed that `App['views.sign_in']` is instantiating a new object every time it’s called. I assumed that it would only instantiate one object, and memoize it. The objects I’m working with are immutable and some are expensive to instantiate (e.g. template loading and precompilation), so I really only want to instantiate one of them.

I know I can do `App.register('views.sign_in', Views::SignIn.new)`, but is there a way do the same thing through auto-registration? It would be nice if it was lazy, so I can run a single test without booting up everything.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [July 31, 2016, 5:33am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/2 "2016-07-31T05:33:18Z")

</div>

I’ve been aware of this since day 1. There’s [a PR](https://github.com/dry-rb/dry-component/pull/21) which adds support for singletons. I’m not sure yet if we want to support OOTB memoization though. We’ve had some brief discussions about it and decided not to memoize as this can be handled at a class level and you can control it. We have support for custom loaders so maybe it’d be worth to at least experiment with a loader that can be configured to memoize and see which strategy is better.

---

<div class="post-metadata">

**Author:** ![tom\_dalling](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/tom_dalling/32/38_2.png) [@tom\_dalling](https://discourse.dry-rb.org/u/tom_dalling)\
**Post date:** [July 31, 2016, 7:49am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/3 "2016-07-31T07:49:21Z")

</div>

I had a look through that PR, and it’s a bit different to what I was expecting. I dug into the code a bit, and came up with this, which gives me the behaviour that I’m after:

```
class MemoizingResolver < Dry::Container::Resolver
  def initialize(*)
    super
    @cache = {}
  end

  def call(container, key)
    @cache.fetch(key) do
      @cache[key] = super
    end
  end
end

class App < Dry::Component::Container
  configure do |config|
    config.resolver = MemoizingResolver.new
  end
end

```

This memoizes _everything_, so it’s not ideal in terms of flexibility, but it’s good enough for my purposes at the moment. To enable/disable it on a per-key basis, I’d need to get into `Dry::Container::Item`, probably changing the behaviour of `#call` based on a new key in the options hash.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [July 31, 2016, 11:56am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/4 "2016-07-31T11:56:26Z")

</div>

That works too, although using a custom loader should be simpler. And btw - I recommend using `Concurrent::Map#fetch_or_store` in such cases.

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [July 31, 2016, 12:44pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/5 "2016-07-31T12:44:29Z")

</div>

A (thread)safer version is

```ruby
class MemoizingResolver < Dry::Container::Resolver
  def initialize
    @_mutex = Mutex.new
    @_key_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
    @cache = {}
  end

  def call(container, k)
    key = k.to_s

    if @cache.key?(key)
      @cache[key]
    else
      synchronize(key) do
        if @cache.key?(key)
          @cache[key]
        else
          @cache[key] = super
        end
      end
    end
  end

  def synchronize(key)
    mutex(key).synchronize { yield }
  end

  private

  def mutex(key)
    @_mutex.synchronize { @_key_mutexes[key] }
  end
end

```

We need `@_key_mutexes` here and can’t use a single mutex for everything because it would end up with a deadlock.

@solnic `fetch_or_store` does not guarantee your block will be evaluated once and _only once_, this can lead to some unexpected consequences in this case.

---

<div class="post-metadata">

**Author:** ![arielvalentin](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arielvalentin/32/326_2.png) [@arielvalentin](https://discourse.dry-rb.org/u/arielvalentin)\
**Post date:** [December 14, 2018, 1:48am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/6 "2018-12-14T01:48:01Z")

</div>

Hello! I was hoping to revisit this conversation.

I would like to know if the team would be amenable to accepting a PR to change the [AutoRegistrar](https://github.com/dry-rb/dry-system/blob/master/lib/dry/system/auto_registrar.rb#L38) or the Container to accept a configuration value to memoize resolved objects. Any objections?

---

<div class="post-metadata">

**Author:** ![timriley](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/timriley/32/25_2.png) [@timriley](https://discourse.dry-rb.org/u/timriley)\
**Post date:** [December 15, 2018, 5:17am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/7 "2018-12-15T05:17:07Z")

</div>

I’m sure we’d be opening to exploring this, yes 🙂

---

<div class="post-metadata">

**Author:** ![arielvalentin](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arielvalentin/32/326_2.png) [@arielvalentin](https://discourse.dry-rb.org/u/arielvalentin)\
**Post date:** [December 15, 2018, 12:46pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/8 "2018-12-15T12:46:34Z")

</div>

What is the best course of action then? Should I be providing code snippets in this forum or start with a pull request and take the conversation from there?

---

<div class="post-metadata">

**Author:** ![arielvalentin](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arielvalentin/32/326_2.png) [@arielvalentin](https://discourse.dry-rb.org/u/arielvalentin)\
**Post date:** [December 18, 2018, 3:29pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/9 "2018-12-18T15:29:50Z")

</div>

Probably should have replied in a thread. What is the best course of action then? Should I be providing code snippets in this forum or start with a pull request and take the conversation from there?

---

<div class="post-metadata">

**Author:** ![timriley](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/timriley/32/25_2.png) [@timriley](https://discourse.dry-rb.org/u/timriley)\
**Post date:** [December 19, 2018, 9:43am UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/10 "2018-12-19T09:43:08Z")

</div>

Happy to look at this in a PR, @arielvalentin 🙂

---

<div class="post-metadata">

**Author:** ![arielvalentin](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arielvalentin/32/326_2.png) [@arielvalentin](https://discourse.dry-rb.org/u/arielvalentin)\
**Post date:** [December 20, 2018, 4:03pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/11 "2018-12-20T16:03:54Z")

</div>

@timriley I did start working on this but I am not entirely sure the best way to move forward.

I wanted to set a default value for `registry_options` e.g.

```auto
setting :registry_options, {}

```

And then override them in `auto_load` like so:

```auto
auto_register!('lib') do |config|
   config.registry_options[:memoize] = false
end

```

And then change `AutoRegistrar` to use the `registry_options` when it loads components:

```auto
container.require_component(component) do
    register(component.identifier, config.registry_options) { registration_config.instance.(component) }
end

```

The challenge with this solution is that `Dry::AutoRegistrar::Configuration` does not use `dry-configurable`. Would there be an objection to changing this class to use `dry-configurable` instead of its own implementation of the `setting` DSL?

---

<div class="post-metadata">

**Author:** ![jimcavoli](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jimcavoli/32/337_2.png) [@jimcavoli](https://discourse.dry-rb.org/u/jimcavoli)\
**Post date:** [February 11, 2019, 7:51pm UTC](https://discourse.dry-rb.org/t/lazy-instantiation-and-memoization-with-container-auto-registration/79/12 "2019-02-11T19:51:02Z")

</div>

Getting `Dry::AutoRegistrar::Configuration` into line with `dry-configurable` strikes me as the right move on the march toward 1.0 for sure; it’s also in-pattern with already-1.0’d siblings.
