Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Change emails foreign key - 2 #3896

Merged
merged 3 commits into from
Jan 20, 2016
Merged

Conversation

aandis
Copy link
Contributor

@aandis aandis commented Jan 19, 2016

This is the next step after #3893. Pr includes a migration to fill the new emails.participant_id column with existing data and modifies email reads to then read from the new column instead of emails.participant.

@aandis aandis added the Review label Jan 19, 2016
@aandis
Copy link
Contributor Author

aandis commented Jan 19, 2016

I tried to modify all reads which use emails.participant to use emails.participant_id. But a review is needed to make sure I didn't miss anything.

SELECT p.username
FROM emails e INNER JOIN participants p
ON e.participant_id = p.id
WHERE e.address = %(email)s
Copy link
Contributor

Choose a reason for hiding this comment

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

change_emails_foreign_key_-_2_by_aandis_ _pull_request__3896_ _gratipay_gratipay_com

😠

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok so that's what the uneven indentation at the start of the line is about! I was scratching my head with that. Man, now you're just messing with a java guy. 😏

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regardless, I'll admit that this does look cleaner. Fixing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Should answer this 😄

@rohitpaulk
Copy link
Contributor

But a review is needed to make sure I didn't miss anything.

I hope our tests are exhaustive enough to catch this...

Try removing the participant column in your local, and see if the tests pass? If they do, then I think we're good to go...

If we do encounter errors in production, we'll patch them and add tests so that they never happen again.

@aandis
Copy link
Contributor Author

aandis commented Jan 19, 2016

Running test.

AND address=%s
AND verified IS NULL
RETURNING nonce
""", (verification_start, self.username, email))
""", (verification_start, self.id, email))
if not nonce:
return self.add_email(email)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rohitpaulk tests are failing. 😞 All the email tests get stuck in this recursive call. Do you have any idea why? I have dropped the participant column and modified email writes to only include participant_id

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you share a diff of the changes you made vs the code in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

diff --git a/gratipay/models/participant.py b/gratipay/models/participant.py
index 242ebff..e64c508 100644
--- a/gratipay/models/participant.py
+++ b/gratipay/models/participant.py
@@ -410,9 +410,9 @@ class Participant(Model):
                 add_event(c, 'participant', dict(id=self.id, action='add', values=dict(email=email)))
                 c.run("""
                     INSERT INTO emails
-                                (address, nonce, verification_start, participant, participant_id)
-                         VALUES (%s, %s, %s, %s, %s)
-                """, (email, nonce, verification_start, self.username, self.id))
+                                (address, nonce, verification_start, participant_id)
+                         VALUES (%s, %s, %s, %s)
+                """, (email, nonce, verification_start, self.id))
         except IntegrityError:
             nonce = self.db.one("""
                 UPDATE emails

Copy link
Contributor

Choose a reason for hiding this comment

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

diff --git a/gratipay/models/participant.py b/gratipay/models/participant.py
index 242ebff..e64c508 100644
--- a/gratipay/models/participant.py
+++ b/gratipay/models/participant.py
@@ -410,9 +410,9 @@ class Participant(Model):
                 add_event(c, 'participant', dict(id=self.id, action='add', values=dict(email=email)))
                 c.run("""
                     INSERT INTO emails
-                                (address, nonce, verification_start, participant, participant_id)
-                         VALUES (%s, %s, %s, %s, %s)
-                """, (email, nonce, verification_start, self.username, self.id))
+                                (address, nonce, verification_start, participant_id)
+                         VALUES (%s, %s, %s, %s)
+                """, (email, nonce, verification_start, self.id))
         except IntegrityError:
             nonce = self.db.one("""
                 UPDATE emails
diff --git a/sql/branch.sql b/sql/branch.sql
index 4a74cea..f316636 100644
--- a/sql/branch.sql
+++ b/sql/branch.sql
@@ -2,4 +2,6 @@ UPDATE emails AS e
 SET participant_id = p.id
 FROM participants AS p
 WHERE p.username = e.participant
-AND e.participant_id IS NULL;
\ No newline at end of file
+AND e.participant_id IS NULL;
+
+ALTER TABLE emails DROP COLUMN participant;

That's weird... I applied the above diff and all tests are passing for me.

Copy link
Contributor

Choose a reason for hiding this comment

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

What's your test output? Which test is getting stuck in the loop?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. The Integrity error is being hit, which means a record exists. If a record exists though, we shouldn't be hitting the recursive loop...

Given that the loop exited right there, we should be able to inspect the DB and see what's happening.. Open psql and try? Let's make sure we have the same setup though... here's mine

$ psql gratipay-test

gratipay-test=# \d emails
                                        Table "public.emails"
       Column       |           Type           |                      Modifiers
--------------------+--------------------------+-----------------------------------------------------
 id                 | integer                  | not null default nextval('emails_id_seq'::regclass)
 address            | text                     | not null
 verified           | boolean                  |
 nonce              | text                     |
 verification_start | timestamp with time zone | not null default now()
 verification_end   | timestamp with time zone |
 participant_id     | bigint                   |
Indexes:
    "emails_pkey" PRIMARY KEY, btree (id)
    "emails_address_verified_key" UNIQUE CONSTRAINT, btree (address, verified)
    "emails_participant_id_address_key" UNIQUE CONSTRAINT, btree (participant_id, address)
Check constraints:
    "verified_cant_be_false" CHECK (verified IS NOT FALSE)
Foreign-key constraints:
    "emails_participant_id_fkey" FOREIGN KEY (participant_id) REFERENCES participants(id) ON UPDATE RESTRICT ON DELETE RESTRICT

gratipay-test=#

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok I have no idea what the problem was but I couldn't even run the site locally at localhost:8537. make run kept exiting. So I restarted my PC and now everything works! All tests pass. 💃

Copy link
Contributor

Choose a reason for hiding this comment

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

Aha. So this is ready to go, eh?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha! I see. I applied your diff before running test. participant was still existing in gratipay-test. I had no idea branch.sql is run by the test suite. Figured it'd be like rails where the test database is created from the current schema.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha. So this is ready to go, eh?

Yes! 💃

@aandis
Copy link
Contributor Author

aandis commented Jan 19, 2016

@rohitpaulk on a side note, why is that recursive call there anyway? If record failed to be created once because of a reason like this, it'll surely keep failing.

@rohitpaulk
Copy link
Contributor

Okay we need one last thing - we want to constrain this participant column to NOT NULL. I guess that we should be able to do that in this PR itself, right?

@aandis
Copy link
Contributor Author

aandis commented Jan 19, 2016

we want to constrain this participant column to NOT NULL

participant_id you mean. Okay on it.

@aandis aandis force-pushed the change-emails-foreign-key-2 branch from 1c73db5 to e94593c Compare January 19, 2016 19:08
@rohitpaulk
Copy link
Contributor

on a side not, why is that recursive call there anyway?

The recursive call handles a specific race condition, I think.

What if an email address existed - and was unverified before the try\except block, but by the time we hit the except, it was verified?

participant_py_ _gratipay_com

I do get that the code can be kinda extremely hard to read though...

"""
           This is called when
            1) Adding a new email address
            2) Resending the verification email for an unverified email address

            Returns the number of emails sent.
"""

This might benefit from being split into two separate functions (and extracting common stuff out into different functions), seems to me like we're trying to stuff too much logic in here.

@rohitpaulk
Copy link
Contributor

Okay, this looks good to me. I'll try deploying this tomorrow. If anyone gets to this before me, keep in mind that we should run the migration script before deploying code.

@aandis
Copy link
Contributor Author

aandis commented Jan 19, 2016

👍

rohitpaulk added a commit that referenced this pull request Jan 20, 2016
@rohitpaulk rohitpaulk merged commit d91499d into master Jan 20, 2016
@aandis aandis deleted the change-emails-foreign-key-2 branch January 20, 2016 17:10
@aandis
Copy link
Contributor Author

aandis commented Jan 20, 2016

@rohitpaulk Did you get into any issues while running the migration for copying values in production?

@rohitpaulk
Copy link
Contributor

Did you get into any issues while running the migration for copying values in production?

Nope, smooth sailing ⛵

@techtonik
Copy link
Contributor

@rohitpaulk awesome graphical analysis. I guess that the bugs like this is the primary use case that is solved by Flux architecture - https://facebook.github.io/flux/docs/overview.html

@chadwhitacre chadwhitacre mentioned this pull request Feb 1, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants