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

Fix 3319 - add unique constraint on identity table #3320

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

Conversation

mmzeeman
Copy link
Member

Description

Fix #3319

Add a unique constraint on rsc_id, type and key on the identity table. This ensures that only one entry is added as intended.

Added test cases to check this behavior.

Added an update routine to remove duplicate entries, and add the constraint to an existing database.

Checklist

  • documentation updated
  • tests added
  • no BC breaks

F = fun(Ctx) ->
insert_1(Rsc, Type, KeyNorm, Props, Ctx)
end,
z_db:transaction(F, Context);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we check here for an error like: #error{ codename = unique_violation } ?
And if so, check if it is because of the (Id, Type, Key) constraint?

Copy link
Member Author

@mmzeeman mmzeeman Mar 11, 2023

Choose a reason for hiding this comment

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

I tried that. When you catch the unique_violoation and retry the transaction it will also fail, because there is another concurrent transaction which is touching the same row. So maybe the best thing is to just return the error.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, the locking problem where we can't lock the key for update yet, because it doesn't exist yet.

Either a table lock, backing off for a couple of msec, or a transactional lock is needed in these situations.
I will check the options. In the paysub module I am now using a retry and a "select ... for update", which seems to work fine for now, but I see the problem with the race condition, especially if the insert takes longer.

Copy link
Member

Choose a reason for hiding this comment

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

Forgot, since PostgreSQL 9.5 the "INSERT ... ON CONFLICT .." exists. Maybe this is a good moment to start using it in the core:

https://www.postgresql.org/docs/9.5/sql-insert.html

As the oldest supported PostgreSQL version is now 11 we might want to up our minimum PostgreSQL version to 9.5?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah... we could extend z_db:insert/3 with some options...

Something like this?

z_db:insert(identity, #{ rsc_id => ..., ...}, 
            #{ on_conflict => <<"ON CONSTAINT(identity_rsc_id_type_key_unique)">>,
               action => <<"DO NOTHING">> },
            Context),

Copy link
Member Author

Choose a reason for hiding this comment

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

Merging would only work correctly when the only properties are real column, and not something which ends up in a props column.

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 thinking in two different directions. One is an option to handle the conflict with an update {on_conflict, update}. Second option is to add an extra function upsert, next to insert and update.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds logical... At the moment this would be impossible for erlang props columns of course. Maybe something for 1.1? We could make something to merge json props then.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, and in the meantime we can just have a "last one wins" strategy.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is an exceptional situation somehow caused by the way our app passes the apple device token, with QoS 1, to the server (In order to support apple push notifications). Somehow two publishes happen at almost the same time. I'm not sure how yet. Usually the identity is already there.

@mworrell mworrell changed the title Fix 3319 Fix 3319 - add unique constraint on identity table Mar 20, 2023
@mworrell
Copy link
Member

@mmzeeman added a "for update" to the select

@mworrell
Copy link
Member

mworrell commented Apr 5, 2023

@mmzeeman Shall we merge this as-is?

@mworrell mworrell added this to the 1.0 milestone Apr 5, 2023
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.

It is possible to insert duplicate rsc_id, type and key entries into the identity table
2 participants