8000 Context manager to acquire Postgres advisory locks by guewen · Pull Request #138 · OCA/connector · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Context manager to acquire Postgres advisory locks #138

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

Merged
merged 1 commit into from
Nov 2, 2015

Conversation

guewen
Copy link
Member
@guewen guewen commented Nov 2, 2015

This Postgres feature is invaluable when dealing with synchronisations,
especially when importing concurrently records from a system.

When we export a record, we are able to acquire a lock on the exported record
to prevent 2 jobs to export it at the same time. This is different when we
import a record for the first time and with several jobs running in parallel,
chances are high that 2 jobs will import the same record at the same moment.

The Postgres advisory lock comes handy there for they allow to acquire an
application lock. Usually we'll acquire the lock at the beginning of an import
(beginning of Importer.run()) and we'll throw a RetryableJobError if
the lock cannot be acquired so the job is retried later. The lock will remain
in place until the end of the transaction.

Example:

  • Job 1 imports Partner A
  • Job 2 imports Partner B
  • Partner A has a category X which happens not to exist yet
  • Partner B has a category X which happens not to exist yet
  • Job 1 import category X as a dependency
  • Job 2 import category X as a dependency

Since both jobs are executed concurrently, they both create a record for category X.
With this lock:

  • Job 1 imports Partner A, it puts a lock for this partner
  • Job 2 imports Partner B, it puts a lock for this partner
  • Partner A has a category X which happens not to exist yet
  • Partner B has a category X which happens not to exist yet
  • Job 1 import category X as a dependency, it puts a lock for this category
  • Job 2 import category X as a dependency, try to put a lock but can't, Job 2
    is retried later, and when it is retried, it sees the category X created by
    Job 1

See http://topopps.com/implementing-postgres-advisory-locks/ for the article
where I learned about the computation of the hash for this purpose.

This Postgres feature is invaluable when dealing with synchronisations,
especially when importing concurrently records from a system.

When we export a record, we are able to acquire a lock on the exported record
to prevent 2 jobs to export it at the same time. This is different when we
import a record for the first time and with several jobs running in parallel,
chances are high that 2 jobs will import the same record at the same moment.

The Postgres advisory lock comes handy there for they allow to acquire an
application lock.  Usually we'll acquire the lock at the beginning of an import
(beginning of ``Importer.run()``) and we'll throw a ``RetryableJobError`` if
the lock cannot be acquired so the job is retried later. The lock will remain
in place until the end of the transaction.

Example:
 - Job 1 imports Partner A
 - Job 2 imports Partner B
 - Partner A has a category X which happens not to exist yet
 - Partner B has a category X which happens not to exist yet
 - Job 1 import category X as a dependency
 - Job 2 import category X as a dependency

Since both jobs are executed concurrently, they both create a record for category X.
With this lock:

 - Job 1 imports Partner A, it puts a lock for this partner
 - Job 2 imports Partner B, it puts a lock for this partner
 - Partner A has a category X which happens not to exist yet
 - Partner B has a category X which happens not to exist yet
 - Job 1 import category X as a dependency, it puts a lock for this category
 - Job 2 import category X as a dependency, try to put a lock but can't, Job 2
   is retried later, and when it is retried, it sees the category X created by
   Job 1

See http://topopps.com/implementing-postgres-advisory-locks/ for the article
where I learned about the computation of the hash for this purpose.
@damdam-s
Copy link
Member
damdam-s commented Nov 2, 2015

👍

@lmignon
Copy link
Contributor
lmignon commented Nov 2, 2015

@guewen This new functionality looks really great! I already see a lot of cases where I need it. As usual, the code looks fine and comes with tests and documentation.
👍 (Code review)

@pedrobaeza
Copy link
Member

Another contribution to make the framework more robust

👍

pedrobaeza added a commit that referenced this pull request Nov 2, 2015
Context manager to acquire Postgres advisory locks
@pedrobaeza pedrobaeza merged commit f68890c into OCA:8.0 Nov 2, 2015
be retried when the lock cannot be acquired.
"""
if pg_try_advisory_lock(self.env, lock):
yield
Copy link
Member Author

Choose a reason for hiding this comment

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

retrospectively I wonder if the context manager is a good choice as the lock is acquired until the end of the transaction anyway... Maybe the following is more clear?

def try_advisory_lock(self, lock, retry_seconds=1):
    if not pg_try_advisory_lock(self.env, lock):
        raise RetryableJobError('Could not acquire advisory lock',...)

Copy link
Member Author

Choose a reason for hiding this comment

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

retrospectively I wonder if the context manager is a good choice as the lock is acquired until the end of the transaction anyway... Maybe the following is more clear?

@lmignon @damdam-s @pedrobaeza opinions?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it can be clearer, but it isn't also very confused this way. What you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch @guewen ! The expected behavior of the contextmanager is to 'scope' the changes to the context to the code enclosed by the with statement. In this case the lock is acquired until the end of the transaction not the end of the enclosed lines.

Copy link
Member

Choose a reason for hiding this comment

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

But does it matter? They both end at the same more or less, isn't it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Exactly what I thought. I'll make a PR tomorrow.

@lmignon
Copy link
Contributor
lmignon commented Nov 2, 2015

@pedrobaeza IMO yes. With the 'with' statement I expect that the lock is released at the end of the block and it's not the case.

@pedrobaeza
Copy link
Member

OK, I understand. @guewen, please go ahead with the PR, please.

guewen added a commit to guewen/connector-magento that referenced this pull request Nov 3, 2015
guewen added a commit to guewen/connector-magento that referenced this pull request Nov 3, 2015
atchuthan pushed a commit to sodexis/connector-magento that referenced this pull request May 19, 2016
atchuthan pushed a commit to sodexis/connector-magento that referenced this pull request Oct 18, 2016
guewen added a commit to guewen/connector-magento that referenced this pull request Jun 20, 2017
IJOL pushed a commit to BITVAX/connector-magento that referenced this pull request Sep 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0