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

Fixes Rails/UniqueValidationWithoutIndex (part of #11482) #13163

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

cyrillefr
Copy link
Contributor

@cyrillefr cyrillefr commented Feb 17, 2025

What? Why?

Contributes to #11482

The Rails/UniqueValidationWithoutIndex cop checks (via the db/schema.rb file) that AR models indexes are also defined at the DB level.
Example : -when you define a uniqueness validation in Active Record model, you also should add a unique index for the column.

What should we test?

Automatic specs should all pass.
Migration should proceed without any errors. (ie running rails db:migrate)

Any error would mean data inserted that do not respect DB constraints.
In dev/test environment, it should be fine.

But the real test would be in instances. Though checks in AR models should have prevented bad data to have been inserted.

Release notes

Changelog Category (reviewers may add a label for the release notes):

  • User facing changes
  • API changes (V0, V1, DFC or Webhook)
  • Technical changes only
  • Feature toggled

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

Copy link
Collaborator

@rioug rioug left a comment

Choose a reason for hiding this comment

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

Thanks @cyrillefr 🙏 , There is a mistake on the spree_tax_categories migration file, could you fix that ?

@openfoodfoundation/developers I added the SQL we should run to check for duplicates on the prod servers before we run the migrations. Could you have a quick review of them ?

Comment on lines 5 to 6
remove_index :customers, :email, name: :index_customers_on_email
add_index(:customers, [:email, :enterprise_id], unique: true, name: :index_customers_on_email)
Copy link
Collaborator

Choose a reason for hiding this comment

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

SELECT email, enterprise_id, COUNT(*) FROM customers GROUP BY email, enterprise_id HAVING count(*) > 1;

Copy link
Member

Choose a reason for hiding this comment

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

We have a problem here.

openfoodnetwork.org.au | CHANGED | rc=0 >>
(1 row)
openfoodnetwork.org.nz | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.in | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.ca | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.net | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.de | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.org.uk | CHANGED | rc=0 >>
(0 rows)
app.katuma.org | CHANGED | rc=0 >>
(0 rows)
openfoodnetwork.ie | CHANGED | rc=0 >>
(0 rows)
coopcircuits.fr | CHANGED | rc=0 >>
(4 rows)
openfood.hu | CHANGED | rc=0 >>
(300 rows)
openfoodnetwork.be | CHANGED | rc=0 >>
(0 rows)

The Australian server has only one test account in there that we can probably remove easily. The French email addresses all look like fake test accounts as well. But I'm not sure what's going on with openfood.hu. That needs some investigation. All those customer accounts are for the same enterprise (id 3) except one (id 60). One email is n/a and has 1718 customer records. That looks like someone did manual SQL queries. Maybe some data import?

Comment on lines +5 to +6
remove_index :exchanges, :sender_id, name: :index_exchanges_on_sender_id
add_index(:exchanges, [:sender_id, :order_cycle_id, :receiver_id, :incoming],
Copy link
Collaborator

Choose a reason for hiding this comment

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

SELECT sender_id, order_cycle_id, receiver_id, incoming, COUNT(*) FROM exchanges GROUP BY sender_id, order_cycle_id, receiver_id, incoming HAVING COUNT(*) >1;

Copy link
Member

Choose a reason for hiding this comment

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

We have some offenders here, too:

$ ansible all_prod -u openfoodnetwork -a "psql -h localhost openfoodnetwork ofn_user -c '$sql'"
openfoodnetwork.org.au | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.org.nz | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.net | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.ca | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
coopcircuits.fr | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
      3322 |          12333 |        3226 | t        |     2
       703 |          12260 |        1855 | t        |     2
      5792 |          31804 |        5792 | f        |     2
       209 |           7681 |         347 | t        |     2
      3949 |          13878 |        1680 | t        |     2
      3468 |          12333 |        3226 | t        |     2
       370 |          10482 |         370 | f        |     2
      3156 |          12260 |        1855 | t        |     2
      1680 |          13878 |        1680 | t        |     2
(9 rows)
openfoodnetwork.de | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.in | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.org.uk | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
    204016 |         217768 |      201237 | t        |     2
    201237 |         210286 |      201237 | f        |     2
(2 rows)
openfoodnetwork.be | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
        91 |           3996 |          91 | f        |     2
(1 row)
openfood.hu | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
app.katuma.org | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
(0 rows)
openfoodnetwork.ie | CHANGED | rc=0 >>
 sender_id | order_cycle_id | receiver_id | incoming | count 
-----------+----------------+-------------+----------+-------
        36 |            704 |          36 | f        |     2
(1 row)

Those numbers are small enough to resolve manually, I think.

Copy link
Member

@mkllnk mkllnk Feb 20, 2025

Choose a reason for hiding this comment

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

We have four servers to resolve:

  • coopcircuits.fr
  • openfoodnetwork.org.uk
  • openfoodnetwork.be
  • openfoodnetwork.ie

Copy link
Member

Choose a reason for hiding this comment

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

I had a look at openfoodnetwork.ie and the duplicate exchange has been created 100ms after the first one. I'm not sure where the glitch is. Maybe something in the Javascript sending two items? They are exactly the same. So I can delete one.

Copy link
Member

@mkllnk mkllnk left a comment

Choose a reason for hiding this comment

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

Thank you for this work!

It looks like you need to run rails db:migrate to change the schema as well.

Comment on lines 5 to 6
remove_index :customers, :email, name: :index_customers_on_email
add_index(:customers, [:email, :enterprise_id], unique: true, name: :index_customers_on_email)
Copy link
Member

Choose a reason for hiding this comment

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

I was wondering why you removed the previous index instead of adding a new one. Apparently, when you have a multi-column index, you can also use it for the first column of that index alone. So queries by email alone are covered by the new index as well. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added the run migration in the "What should we test" bullet.

Comment on lines 5 to 6
remove_index :customers, :email, name: :index_customers_on_email
add_index(:customers, [:email, :enterprise_id], unique: true, name: :index_customers_on_email)
Copy link
Member

Choose a reason for hiding this comment

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

Why did you set the name of the index again? Do we care?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not know this was optional actually. I will remove wherever I can. I might leave some when generated names hit the length max size (63 if I remember right).

…11482)

- When you define a uniqueness validation in Active Record model,
  you also should add a unique index for the column.
- Cf. https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsuniquevalidationwithoutindex
- Therefore : migration files to match DB structure and Ruby code.
@cyrillefr cyrillefr force-pushed the FixRailsUniqueValidationWithoutIndex branch from 36bc465 to 680b582 Compare February 18, 2025 14:20
Copy link
Member

@mkllnk mkllnk left a comment

Choose a reason for hiding this comment

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

Nice! We still need the updated schema.rb file.

@cyrillefr
Copy link
Contributor Author

Just added schema.rb

@mkllnk mkllnk added blocked technical changes only These pull requests do not contain user facing changes and are grouped in release notes labels Feb 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked technical changes only These pull requests do not contain user facing changes and are grouped in release notes
Projects
Status: In Progress ⚙
Development

Successfully merging this pull request may close these issues.

3 participants