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

Update Python psycopg2 example with app retry loop #5085

Merged
merged 1 commit into from
Aug 5, 2019

Conversation

rmloveland
Copy link
Contributor

... and update the surrounding descriptive text to match the new code.

Addresses #5035.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@rmloveland rmloveland requested a review from bdarnell July 29, 2019 15:20
Copy link
Contributor

@bdarnell bdarnell left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @bdarnell and @rmloveland)


_includes/v19.1/app/insecure/basic-sample.py, line 41 at r1 (raw file):

    # Check the current balance.
    with conn.cursor() as cur:
        cur.execute("SELECT balance FROM accounts WHERE id = " + str(frm))

This read must occur within the transaction.


_includes/v19.1/app/insecure/basic-sample.py, line 62 at r1 (raw file):

                raise ValueError("Transaction did not succeed after 3 retries")

            cur.execute('BEGIN')

BEGIN is special and should not be executed as a normal statement. In python transactions are created automatically by default, so you don't need to run BEGIN explicitly.


_includes/v19.1/app/insecure/basic-sample.py, line 84 at r1 (raw file):

                logging.debug("e.pgcode: {}".format(e.pgcode))
                if e.pgcode == '40001':
                    cur.execute('ROLLBACK')

Use cur.rollback() instead of cur.execute('ROLLBACK').


_includes/v19.1/app/insecure/basic-sample.py, line 105 at r1 (raw file):


    # Make each statement commit immediately.
    conn.set_session(autocommit=True)

You're explicitly committing everything, so you want autocommit to be false (which is the default).

Copy link
Contributor Author

@rmloveland rmloveland left a comment

Choose a reason for hiding this comment

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

Thanks for the review, Ben. I think my latest changes address your feedback. PTAL!

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @bdarnell)


_includes/v19.1/app/insecure/basic-sample.py, line 41 at r1 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

This read must occur within the transaction.

Fixed (I hope) by moving it inside the try block in the retry loop (which is what I assume you mean by "within the transaction"?)


_includes/v19.1/app/insecure/basic-sample.py, line 62 at r1 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

BEGIN is special and should not be executed as a normal statement. In python transactions are created automatically by default, so you don't need to run BEGIN explicitly.

Fixed by removing.


_includes/v19.1/app/insecure/basic-sample.py, line 84 at r1 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

Use cur.rollback() instead of cur.execute('ROLLBACK').

Fixed.


_includes/v19.1/app/insecure/basic-sample.py, line 105 at r1 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

You're explicitly committing everything, so you want autocommit to be false (which is the default).

Fixed by deleting.

Copy link
Contributor

@bdarnell bdarnell left a comment

Choose a reason for hiding this comment

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

LGTM, although as I said in the gorm version it would be more idiomatic and realistic to make a run_transaction higher-order function instead of doing everything inline.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @bdarnell and @rmloveland)


_includes/v19.1/app/insecure/basic-sample.py, line 16 at r2 (raw file):

        logging.debug("create_accounts(): status message: {}".format(cur.statusmessage))
        conn.commit()
        cur.close()

cur.close() is not necessary since you're using with conn.cursor().

I think it's better to call conn.commit after the cursor closes (i.e., outside the with block), although I'm not sure.


_includes/v19.1/app/insecure/basic-sample.py, line 23 at r2 (raw file):

        cur.execute("SELECT id, balance FROM accounts")
        logging.debug("print_balances(): status message: {}".format(cur.statusmessage))
        conn.commit()

The commit should come after fetchall().


_includes/v19.1/app/insecure/basic-sample.py, line 69 at r2 (raw file):

                            UPSERT INTO accounts (id, balance)
                            VALUES
                            (%s, ((SELECT balance FROM accounts WHERE id = %s) - %s)),

An UPSERT with two subselects is a weird way to write this. We already queried the balance above (at least for the from account), so I think the most natural way to write it would be to query both account balances in the first SELECT, then pass those balances in here instead passing in the IDs and re-querying.

There's also the clever solution:

UPDATE accounts SET balance = balance + (IF (id = ?, -1, 1) * ? ) 
    WHERE id IN (?, ?) RETURNING id, balance

(parameters: from, amount, from, to). Look at the returned values and see if the from balance is negative before deciding to commit or abort.

But I'd do the simple thing for examples like this.

... and update the surrounding descriptive text to match the new code.

Addresses #5035.
Copy link
Contributor Author

@rmloveland rmloveland left a comment

Choose a reason for hiding this comment

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

Thanks, ended up doing that, in addition to addressing other items below. So much nicer!

PTAL.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @bdarnell)


_includes/v19.1/app/insecure/basic-sample.py, line 16 at r2 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

cur.close() is not necessary since you're using with conn.cursor().

I think it's better to call conn.commit after the cursor closes (i.e., outside the with block), although I'm not sure.

Deleted cur.close().

Updated to call conn.commit after the with block (It wasn't clear to me from looking at the psycopg.connection docs whether it's better or not, but the code worked after the update.)


_includes/v19.1/app/insecure/basic-sample.py, line 23 at r2 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

The commit should come after fetchall().

Fixed.


_includes/v19.1/app/insecure/basic-sample.py, line 69 at r2 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

An UPSERT with two subselects is a weird way to write this. We already queried the balance above (at least for the from account), so I think the most natural way to write it would be to query both account balances in the first SELECT, then pass those balances in here instead passing in the IDs and re-querying.

There's also the clever solution:

UPDATE accounts SET balance = balance + (IF (id = ?, -1, 1) * ? ) 
    WHERE id IN (?, ?) RETURNING id, balance

(parameters: from, amount, from, to). Look at the returned values and see if the from balance is negative before deciding to commit or abort.

But I'd do the simple thing for examples like this.

This should be addressed by the update to use run_transaction HOF wrapper. Now the transfer_funds function executes a SELECT and 2 UPDATEs in the same txn. Hopefully much simpler now.

Copy link
Contributor

@bdarnell bdarnell left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @bdarnell)

Copy link
Contributor

@ericharmeling ericharmeling left a comment

Choose a reason for hiding this comment

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

LGTM!

@rmloveland rmloveland merged commit 57edb36 into master Aug 5, 2019
@rmloveland rmloveland deleted the 20190724-app-retries-python branch August 5, 2019 14:25
rmloveland added a commit that referenced this pull request Sep 12, 2019
Also, delete the comments in the Python code that state that
`crdb_internal.force_retry()` must be run as root.  This restriction was
lifted in cockroachdb/cockroach#39246.

The 19.1 Python code samples were updated in #5085 and #5173.  We then
forgot to port those changes to 19.2.  This commit remedies that
oversight.

Addresses part of #5176.
rmloveland added a commit that referenced this pull request Sep 16, 2019
Also, delete the comments in the Python code that state that
`crdb_internal.force_retry()` must be run as root.  This restriction was
lifted in cockroachdb/cockroach#39246.

The 19.1 Python code samples were updated in #5085 and #5173.  We then
forgot to port those changes to 19.2.  This commit remedies that
oversight.

Addresses part of #5176.
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.

4 participants