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

Checkout addresses #5703

Merged

Conversation

Matt-Yorkley
Copy link
Contributor

@Matt-Yorkley Matt-Yorkley commented Jun 30, 2020

What? Why?

Closes #5702
Related to #5663 and possibly to #5705

  • Set timestamps to not be nullable again.
  • Fixes problems with updating default shipping / billing addresses during checkout processing.

So I think a nasty error was happening (due to the way we were updating default address records) part-way through the transaction for placing and order, which was then aborted/rolled back in a messy way (causing timestamps on associated objects to be NULL), and possibly triggering other issues within postgres itself (see below). Basically there was some really, really nasty stuff happening here that can only be descried as a fail cascade.

There's some related background in this Rails issue in a case where an exception is raised during a transaction at the point that postgres is dealing with caching important values relating to it's internal state (which we actually see some evidence of in the stacktraces on Katuma) and essentially all hell breaks loose: rails/rails#17607

Other related reading: Rails 4 has introduced breaking changes to the behavior of #clone (apparently without mentioning it in the upgrade guide 😒 ), which I think is what caused the problematic exception in this case. See: https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/core.rb#L217-L220

We didn't see this in the build before, mostly because of this: #5663 (comment) and partly because of a tiny bit of missing spec coverage for a particular case.

What should we test?

We need to very carefully test the "save as default billing address" and "save as default shipping address" checkout options with:

  • guest checkout
  • registered user checkout where the user does not already have default addresses saved
  • registered user checkout where the user does have default addresses saved

Update: it looks like it's actually just the scenario where the user already has default addresses saved, and they select the "save as default shipping/billing address" option again, which updates their existing default addresses.

Release notes

Fixed bug with updating default addresses during checkout in v3

Copy link
Contributor

@sauloperez sauloperez left a comment

Choose a reason for hiding this comment

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

This is looking good 👍

@Matt-Yorkley
Copy link
Contributor Author

I just added some explanatory notes in each commit 👍

Copy link
Contributor

@luisramos0 luisramos0 left a comment

Choose a reason for hiding this comment

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

From my comment here:
#5702 (comment)

I am convinced that we can make this code change and still keep the constraints relaxed. I dont understand what serious problems the relaxed constraints can create.

@Matt-Yorkley
Copy link
Contributor Author

Matt-Yorkley commented Jun 30, 2020

I now have feature specs that replicate this bug, and fail at checkout submission, with the expected error (when timestamps are not nullable in the database). The failing specs are fixed by the commits in this PR.

@Matt-Yorkley Matt-Yorkley marked this pull request as ready for review June 30, 2020 09:23
These specs fail with the "NULL values for created_at / updated_at" errors we've been seeing.
In Rails 4, #clone behaves differently. The attributes hash of the cloned object is shared with the original, it's not a separate object! https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/core.rb#L217-L220
Doing #clone or #dup of an object's attributes then passing it to an #update/#update_attributes call means we are manually passing values for created_at and updated_at, which can cause problems, especially if the object being duped hasn't been persisted yet: in this case we would be manually attempting to save timestamps with nil values, which is not a good idea. Here they are blacklisted from the attributes hash.
I considered deleting these tests, as they're not very good and are testing Rails functionality. I decided to leave them in case something explodes in a future upgrade. For reference: there are issues in Rails 4 when using `object.clone.attributes`, and with assigning a hash containing `created_at` and `updated_at` values with `object.update(attributes_hash)`.
@luisramos0
Copy link
Contributor

wow!!!! I had to give it a try, I can confirm, you are right!!!! 👏

I added the spec to master and see it passing!
I added the constraints and see the spec failing
image

and then added your fix and it's green again.

👏 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏

@luisramos0
Copy link
Contributor

This will be the cause and fix to the multiple stripe payments right Matt?
#5705

@Matt-Yorkley
Copy link
Contributor Author

This will be the cause and fix to the multiple stripe payments right Matt?

I really, really hope so.

@sauloperez
Copy link
Contributor

sauloperez commented Jun 30, 2020

But the root cause was the issue with #clone and the unordered rollback made it just hard to spot, right? That's what caused the rollbacks in the first place.

@Matt-Yorkley
Copy link
Contributor Author

Matt-Yorkley commented Jun 30, 2020

But the root cause was the issue with #clone and the unordered rollback made it just hard to spot, right?

I think problems due to the #clone issue caused an exception, which triggered a rollback, which fucked up, and the combination of some or all of the above possibly then caused an internal issue in postgres itself, on top of all that.

What made it hard to spot was this: #5663 (comment) and a missing spec.

@sigmundpetersen
Copy link
Contributor

So this is Test ready then? I guess we move it to the top as well?

@filipefurtad0 filipefurtad0 self-assigned this Jun 30, 2020
@filipefurtad0 filipefurtad0 added pr-staged-fr staging.coopcircuits.fr and removed pr-staged-fr staging.coopcircuits.fr labels Jun 30, 2020
@Matt-Yorkley
Copy link
Contributor Author

Matt-Yorkley commented Jun 30, 2020

I've adjusted the migration here to guard against NULL values whilst the table's NULL constraint is being altered. It needs some re-review and then it would be good to test deploying it to a staging server that does have some NULL timestamps in the database.

@filipefurtad0 filipefurtad0 removed the pr-staged-fr staging.coopcircuits.fr label Jun 30, 2020
@luisramos0
Copy link
Contributor

In uk live there are for example orders without these values, I think we will be ok 👍

Let's get another dev to review this before we test. What can go wrong?

@Matt-Yorkley
Copy link
Contributor Author

Interesting, there is a single order in UK Prod with NULL values in timestamps. I assume that order could create problems...

@@ -0,0 +1,253 @@
class RequireTimestamps < ActiveRecord::Migration
def up
current_time = Time.now
Copy link
Contributor

@sauloperez sauloperez Jul 1, 2020

Choose a reason for hiding this comment

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

Have you checked what's the actual value that the ALTER TABLE statement gets called with? It might not matter much in this context but we should always use Rails' Time.zone.now. It's a bit fuzzy in my memory but I remember Time.now was a no. What I do know is that calling zone is the only way to include time-zone information for date times in Rails. According to https://thoughtbot.com/blog/its-about-time-zones we should let AR do the UTC conversion.

It might be fine as it is, but I'd still check this to be fully confident.

Copy link
Contributor

@sauloperez sauloperez left a comment

Choose a reason for hiding this comment

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

Nice! great finding the fact that #change_column_null allows this option! Before that method, you had to change the schema in graduall and backward-compatible steps.

…straint

This argument in #change_column_null assigns this value where any NULL values are found, but it doesn't alter the table's `:default` value
@Matt-Yorkley
Copy link
Contributor Author

Before that method, you had to change the schema in graduall and backward-compatible steps.

Yeah, it's new in Rails 4 🎉

It would have been a huge pain to do this nicely without this option...

@sauloperez
Copy link
Contributor

It would have been a huge pain to do this nicely without this option...

well, it required patience more than anything

@sauloperez sauloperez added the dev-test A dev need to test this one label Jul 1, 2020
@sauloperez
Copy link
Contributor

I tested this locally and it works as intended. An order without timestamps (I set them to null from psql) ends up with timestamps set to the current time in UTC and not null column modifiers 👌

@sauloperez sauloperez removed the dev-test A dev need to test this one label Jul 1, 2020
@filipefurtad0 filipefurtad0 added the pr-staged-fr staging.coopcircuits.fr label Jul 1, 2020
@filipefurtad0
Copy link
Contributor

Hey @Matt-Yorkley ,

Checked the 3 scenarios you mentioned, focusing on Stripe-SCA payments:

i) guest checkout - OK
ii) registered user checkout where the user does not already have default addresses saved - OK
iii) registered user checkout where the user does have default addresses saved - OK

In iii) I tested 8 different options/combinations of the checkboxes on checkout, inserting new addresses when required

  • Save as default billing address
  • Shipping address same as billing address?
  • Save as default shipping address

These appear as expected in the dashboard on Stripe.

Also inserted a bit random and unusual characters (on addresses, names). In all cases checkout proceeded as expected.

Ready to go.

@filipefurtad0 filipefurtad0 removed the pr-staged-fr staging.coopcircuits.fr label Jul 1, 2020
@luisramos0 luisramos0 merged commit 7dcc2bb into openfoodfoundation:master Jul 1, 2020
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.

Require timestamps on all models that have them
5 participants