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

Prepare caching spec for Rails 7 #10487

Merged
merged 4 commits into from
Mar 8, 2023
Merged

Conversation

mkllnk
Copy link
Member

@mkllnk mkllnk commented Feb 28, 2023

What? Why?

I found some specs which seemed to pass even though the tested code failed. So I tried to make the specs fail correctly and marked them as pending. Then I found that the spec code was faulty and improved it. Now we have a spec that, I think, is correct and also passes with Rails 7.

What should we test?

  • Specs only.

Release notes

Changelog Category: Technical changes

The title of the pull request will be included in the release notes.

Dependencies

Documentation updates


it "touches enterprise when a classification on that product changes" do
pending "product touches distributor on change"

expect {
classification.save!
enterprise.reload
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, so if we don't reload on line 73 then the spec passes because the OC touches the enterprise. Do I understand the issue correctly?

Copy link
Member Author

Choose a reason for hiding this comment

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

At the moment, classification.save! does not touch the distributor. That's why the spec is pending.

The OC may touch the enterprise but, more importantly, the enterprise is created when we create the enterprise. So similar to before, we need to reload after setup and before executing the code that's supposed to touch.

@@ -5,7 +5,8 @@
describe Enterprise do
context "key-based caching invalidation" do
describe "is touched when a(n)" do
let(:enterprise) { create(:distributor_enterprise, updated_at: Time.zone.now - 1.week) }
# Reloading changes timestamps to the precision of the database.
let(:enterprise) { create(:distributor_enterprise, updated_at: 1.week.ago).reload }
Copy link
Contributor

@filipefurtad0 filipefurtad0 Mar 1, 2023

Choose a reason for hiding this comment

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

Do we need to reload the enterprise upon creation, and before any action "touched" it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, otherwise our reload after the expected touch changes the timestamp regardless, touched or not.

Copy link
Contributor

@filipefurtad0 filipefurtad0 left a comment

Choose a reason for hiding this comment

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

Ok, we expect that changing the:

  • classification
  • product property
  • producer property
  • enterprise relationship
    changes enterprise.updated_at (aka. touching).

Thanks for pinging. Surely worth testing manually. It should be even possible to verify these changes by logging into the server.

@@ -5,7 +5,8 @@
describe Enterprise do
context "key-based caching invalidation" do
describe "is touched when a(n)" do
let(:enterprise) { create(:distributor_enterprise, updated_at: Time.zone.now - 1.week) }
# Reloading changes timestamps to the precision of the database.
let(:enterprise) { create(:distributor_enterprise, updated_at: 1.week.ago).reload }
Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, otherwise our reload after the expected touch changes the timestamp regardless, touched or not.


it "touches enterprise when a classification on that product changes" do
pending "product touches distributor on change"

expect {
classification.save!
enterprise.reload
Copy link
Member Author

Choose a reason for hiding this comment

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

At the moment, classification.save! does not touch the distributor. That's why the spec is pending.

The OC may touch the enterprise but, more importantly, the enterprise is created when we create the enterprise. So similar to before, we need to reload after setup and before executing the code that's supposed to touch.


it "touches enterprise when a classification on that product changes" do
pending "product touches distributor on change"

expect {
classification.save!
enterprise.reload
}.to change { enterprise.updated_at }
Copy link
Member Author

Choose a reason for hiding this comment

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

Alternatively, we could reload the enterprise before and after:

Suggested change
}.to change { enterprise.updated_at }
}.to change { enterprise.reload.updated_at }

I think that this is clearer. That block is executed before and after and then compared. Then it doesn't matter which setup code needed reloading.

My current solution came from the rails7 branch where we don't actually need to reload after create because Rails adjusts the precision of timestamps on creation already. But we still have the potential problem of other setup code touching the enterprise.

So big thank you for your feedback here. I will revise my pull request with this code instead. It will be much easier to understand.

mkllnk added 2 commits March 3, 2023 12:34
Storing a timestamp to the database has less accuracy than a Ruby Time
object. So `updated_at` changes after being written and loaded from the
database. Rails 7 accounts for that by rounding it in the model already
before it's written to the database. That made one spec fail.
I found this because Rails 7 converts timestamps to database precision
straight away. While we may have some broken logic in the code, most of
these cases may just be broken spec code. Watch this space.
@mkllnk mkllnk marked this pull request as ready for review March 3, 2023 01:47
@mkllnk mkllnk changed the title Spec broken caching Prepare caching spec for Rails 7 Mar 3, 2023
mkllnk added 2 commits March 3, 2023 12:51
I didn't observe it but if the spec code would run within the same
millisecond then we wouldn't be able to observe a change to
`updated_at`. Time travel solves this potential problem.
When calling `save!` without changing any attributes then Rails doesn't
always touch other records because nothing changed. So I changed the
spec to `touch` explicitely and it turns out that everything passes.

Tada, our code seems correct and it was only the spec which seemed
broken in Rails 7.
Copy link
Contributor

@jibees jibees left a comment

Choose a reason for hiding this comment

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

Congrats! 🎉
Are we ready to upgrade to Rails7 ? ;) 👏

@mkllnk
Copy link
Member Author

mkllnk commented Mar 5, 2023

Are we ready to upgrade to Rails7 ?

No, we still have some failing specs:

Copy link
Member

@dacook dacook left a comment

Choose a reason for hiding this comment

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

Good fix!

@@ -309,7 +309,7 @@ module Spree
it "defaults available_on to now" do
Timecop.freeze do
product = Product.new
expect(product.available_on).to eq(Time.zone.now)
expect(product.available_on).to be_within(0.000001).of(Time.zone.now)
Copy link
Member

Choose a reason for hiding this comment

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

I'm just wondering, would be_within(1) be good enough?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, I'm using the full precision of the timestamp but you are probably right, from a user perspective, "now" just needs the accuracy to the second.

@mkllnk mkllnk merged commit df82836 into openfoodfoundation:master Mar 8, 2023
@mkllnk mkllnk deleted the time-spec branch March 8, 2023 00:37
@filipefurtad0
Copy link
Contributor

Hey @mkllnk ,

I've tested the scenarios:

  • changing the product category (which refers to taxon/classification, right?) -> it indeed touches the enterprise

image

  • producer property

image

  • enterprise relationship

image

I'm not entirely sure about the difference between classification vs. product category taxon. Other than that, I can confirm that making these changes indeed touches the respective enterprise 👍

@mkllnk
Copy link
Member Author

mkllnk commented Mar 9, 2023

Thank you for thorough testing @filipefurtad0!

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.

4 participants