Files
grecia/app/models
Javi Martín 6da53b5716 Add unique index to poll voters table
Note that Rails 7.1 changes `find_or_create_by!` so it calls
`create_or_find_by!` when no record is found, meaning we'll rarely get
`RecordNotUnique` exceptions when using this method during a race
condition.

Adding this index means we need to remove the uniqueness validation.
According to the `create_or_find_by` documentation [1]:

> Columns with unique database constraints should not have uniqueness
> validations defined, otherwise create will fail due to validation
> errors and find_by will never be called.

We're adding a test that checks what happens when using
`create_or_find_by!`.

Note that we're creating voters combining `create_with` with
`find_or_create_by!`. Using `find_or_create_by!(...)` with all
attributes (including non-key ones like `origin`) fails when a voter
already exists with different values, e.g. an existing `origin: "web"`
and an incoming `"booth"`. In this situation the existing record is not
matched and the unique index raises an exception.

`create_with(...).find_or_create_by!(user: ..., poll: ...)` searches by
the unique key only and applies the extra attributes only on creation.
Existing voters are returned unchanged, which is the intended behavior.

For the `take_votes_from` method, we're handling a (highly unlikely, but
theoretically possible) scenario where a user votes at the same time as
taking voters from another user. For that, we're doing something similar
to what `create_or_find_by!` does: we're updating the `user_id` column
inside a new transaction (using a new transactions avoids a
`PG::InFailedSqlTransaction` exception when there are duplicate
records), and deleting the existing voter when we get a
`RecordNotUnique` exception.

On `Poll::WebVote` we're simply raising an exception when there's
already a user who's voted via booth, because the `Poll::WebVote#update`
method should never be called in this case.

We still need to use `with_lock` in `Poll::WebVote`, but not due to
duplicate voters (`find_or_create_by!` method will now handle the unique
record scenario, even in the case of simultaneous transactions), but
because we use a uniqueness validation in `Poll::Answer`; this
validation would cause an error in simultaneous transactions.

[1] https://api.rubyonrails.org/v7.1/classes/ActiveRecord/Relation.html#method-i-create_or_find_by
2025-08-28 14:42:30 +02:00
..
2024-11-13 15:55:20 +01:00
2021-08-16 16:31:04 +02:00
2021-12-30 15:50:02 +01:00
2019-03-27 15:22:14 +01:00
2023-11-22 14:44:24 +01:00
2021-04-13 13:52:18 +02:00
2019-10-23 14:39:31 +02:00
2024-04-02 16:31:10 +02:00
2019-03-14 17:25:43 +01:00
2019-04-29 13:08:43 -05:00
2025-08-14 13:06:43 +02:00
2020-12-02 12:13:02 +01:00
2021-08-16 16:31:04 +02:00
2021-08-16 16:31:04 +02:00
2019-10-25 19:29:12 +02:00
2025-08-28 14:42:30 +02:00
2024-11-13 15:55:20 +01:00
2017-12-15 19:21:02 +01:00
2021-01-12 14:50:37 +01:00