Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

should inverse_of work? #357

Open
roccoblues opened this issue Nov 9, 2017 · 5 comments
Open

should inverse_of work? #357

roccoblues opened this issue Nov 9, 2017 · 5 comments

Comments

@roccoblues
Copy link

roccoblues commented Nov 9, 2017

Not sure if it's a misconfiguration on my side or if it's simply not supported.

Rails 5.1.4
identity_cache 0.5.1

Given:

class Person < ActiveRecord::Base
  include IdentityCache
  belongs_to :client, inverse_of: :persons
  cache_belongs_to :client
end

class Client < ActiveRecord::Base
  include IdentityCache
  has_many :persons, -> { where partner_id: nil }, dependent: :destroy, inverse_of: :client
  cache_has_many :persons
end

Without the cache the client isn't fetched again when we fetch persons on a client:

irb(main):002:0> client = Client.find(4)
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
=> #<Client id: 4, name: .... >
irb(main):003:0> client.persons.map{|p| p.client.object_id}
  Person Load (0.7ms)  SELECT `persons`.* FROM `persons` WHERE `persons`.`deleted_at` IS NULL AND `persons`.`client_id` = 4 AND `persons`.`partner_id` IS NULL
=> [70220559135500, 70220559135500, 70220559135500, 70220559135500, 70220559135500, 70220559135500, 70220559135500, 70220559135500]

With IdentityCache I get a client query for every person and different objects. Shouldn't IdentityCache simply setup the association to the existing client object?

irb(main):002:0> client = Client.find(4)
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
=> #<Client id: 4, name: .... >
irb(main):002:0> client.fetch_persons.map{|p| p.client.object_id}
   (1.2ms)  SELECT `persons`.`id` FROM `persons` WHERE `persons`.`deleted_at` IS NULL AND `persons`.`client_id` = 4 AND `persons`.`partner_id` IS NULL
Cache cas_multi: greenlight:1:IDC:7:blob:Person:7919784493729940308:9/IDC:7:blob:Person:7919784493729940308:10/IDC:7:blob:Person:7919784493729940308:11/IDC:7:blob:Person:7919784493729940308:3459/IDC:7:blob:Person:79197844937299403:md5:1e1faf6c2624ebf80c00411dbf35dcc9 ({:namespace=>"greenlight:1", :expires_in=>21600})
  Person Load (0.8ms)  SELECT `persons`.* FROM `persons` WHERE `persons`.`deleted_at` IS NULL AND `persons`.`id` IN (9, 10, 11, 3459, 4135, 4147, 4312, 4458)
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:9 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:10 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:11 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:3459 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:4135 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:4147 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:4312 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
Cache write: greenlight:1:IDC:7:blob:Person:7919784493729940308:4458 ({:namespace=>"greenlight:1", :expires_in=>21600, :unless_exist=>true})
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:9 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:10 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:11 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:3459 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:4135 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:4147 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:4312 (multi)
[IdentityCache] cache miss for IDC:7:blob:Person:7919784493729940308:4458 (multi)
  Client Load (0.6ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.6ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.6ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
  Client Load (0.5ms)  SELECT  `clients`.* FROM `clients` WHERE `clients`.`id` = 4 LIMIT 1
=> [70220565335420, 70220557688780, 70220557994400, 70220558006100, 70220589297320, 70220589903960, 70220589965920, 70220560291960]
@dylanahsmith
Copy link
Contributor

This is working as intended.

irb(main):002:0> client.fetch_persons.map{|p| p.client.object_id}

You need to replace the Person#client call with Person#fetch_client if you want the cached record. Try this instead

irb(main):002:0> client.fetch_persons.map{|p| p.fetch_client.object_id}

@roccoblues
Copy link
Author

That fetches the client from cache for every person and results in different client objects:

irb(main):002:0> client.fetch_persons.map{|p| p.fetch_client.object_id}
   (0.7ms)  SELECT `persons`.`id` FROM `persons` WHERE `persons`.`deleted_at` IS NULL AND `persons`.`client_id` = 4 AND `persons`.`partner_id` IS NULL
Cache cas_multi: greenlight:1:IDC:7:blob:Person:14507715566204489110:9/IDC:7:blob:Person:14507715566204489110:10/IDC:7:blob:Person:14507715566204489110:11/IDC:7:blob:Person:14507715566204489110:3459/IDC:7:blob:Person:1450771556620:md5:86dc7956880e3af3985af73a2db3aec2 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:9 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:10 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:11 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:3459 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:4135 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:4147 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:4312 (multi)
[IdentityCache] (backend) cache hit for IDC:7:blob:Person:14507715566204489110:4458 (multi)
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
Cache cas: greenlight:1:IDC:7:blob:Client:2431980387103363215:4 ({:namespace=>"greenlight:1", :expires_in=>21600})
[IdentityCache] (cache_backend) cache hit for IDC:7:blob:Client:2431980387103363215:4
=> [70107689539100, 70107689572200, 70107689434820, 70107689369160, 70107692043900, 70107689250420, 70107689202020, 70107666820400]

My problem is that I have some expensive associations already fetched in the client that I would like to reuse on person.client.[expensive_stuff] calls. That's why I want to have all person.client objects to be the same as the one we fetched the persons on.

I guess I can set that up myself with something like this:

irb(main):003:0> client.fetch_persons.map{|p| p.client = client; p}.map{|p| p.client.object_id}
=> [70107688039420, 70107688039420, 70107688039420, 70107688039420, 70107688039420, 70107688039420, 70107688039420, 70107688039420]

But I'm still wondering if it isn't something that IdentityCache could do. If you're open for it I could try to create a pull-request.

Thanks!

@dylanahsmith
Copy link
Contributor

Oh I see. It looks like cache_has_many with embed: :ids implements the fetch_#{association} method by just memoizing a fetch_multi on the associated class.

Yes, it would make sense if the inverse cached association were set on the returned records.

By the way, I think that works for cache_has_many associations with the embed: true option.

@roccoblues
Copy link
Author

I've started by adding tests for the inverse_of behaviour here: https://github.com/roccoblues/identity_cache/commit/01219cce14289431c57697a541c6bc4fa271b6e4

Do you agree that the two failing ones should be fixed?

  1) Failure:
PrefetchAssociationsTest#test_fetch_should_setup_association_for_cache_has_many [test/inverse_of_test.rb:38]:
Expected: 70228404239540
  Actual: 70228404877000

  2) Failure:
PrefetchAssociationsTest#test_fetch_should_setup_association_for_cache_has_many_embedded_ids [test/inverse_of_test.rb:60]:
Expected: 70228399614120
  Actual: 70228392031260

@dylanahsmith
Copy link
Contributor

Yes, although you don't need both of those tests, since the only difference is that one tests the default value of the cache_has_many embed keyword argument.

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

No branches or pull requests

2 participants