3

I discovered that under heavy load my pyramid web app throws py-postgresql exceptions like postgresql.exceptions.ProtocolError. Some search revealed, that py-postgresql is not thread-safe and one connection can not be used by several threads concurrently.

I tried to make some sort of pooling mechanism, but I still get ProtocolErrors :(

What am I doing wrong?

First I create number of connection objects:

    for x in range(num_db_connections):
        self.pool.append(Connection(conn_string,x))

Each object in pool contains db_lock = threading.Lock() and a connection to database self.conn = postgresql.open( conn_string )

Then I try to acquire lock on a connection and do some work with it. This code can be executed by many threads concurrently, but i think no two threads can run work on one connection concurrently because of lock.

    time_start = time.time()
    while time.time() - time_start < self.max_db_lock_wait_time:
        for conn in self.pool:
            acquired = conn.db_lock.acquire(False)
            if acquired:
                try:
                        lst = conn.work()
                finally:
                    conn.db_lock.release()
                return lst
        time.sleep(0.05)
    raise Exception('Could not get connection lock in time')

Maybe there is flaw in my code, or I misunderstood the nature of "thread unsafety" of py-postgresql? Please, help me!

5
  • Are you properly committing changes before releasing the lock? Commented Sep 27, 2012 at 1:20
  • 1
    I am testing on selects only and postgres is in auto-commit mode. Commented Sep 27, 2012 at 2:01
  • What errors are you getting? Do you get them on a single thread? Commented Sep 27, 2012 at 13:39
  • Have you tried to use Psycopg instead? Commented Sep 27, 2012 at 13:45
  • 1
    @ChrisTravers i am getting py-postgresql ProtocolError exceptions. I dont get them when single threaded, and errors pop up on random threads when multithreaded. javex I tried, but discovered, that it prepares queries with string literals emmbedded in sql, instead of just binding parameters. It is just plain wrong. Commented Sep 27, 2012 at 21:22

1 Answer 1

2

Are you sure that you are not using cursor objects outside your lock?

Just a suggestion: instead of using time.sleep() and "trying" to lock, use a Queue to pop/push connection objects from/into the pool. That is already thread safe, and it has a timeout parameter. Much more effective. Especially if you have many threads and just a few connections. (Those tiny sleep instructions add up, when you need to run 100 000 queries. All of them will increase your response times.)

Sign up to request clarification or add additional context in comments.

3 Comments

I was thinking about queue, but I intended to put queries, not connection objects into queue. I tried using queue as you described, but errors are same.
Thanks, you pointed me in right direction! I did not use cursors outside my lock, but it looks like I had implemented thread-unsafe connection object - it had cached prepared statements pool. I still don't know, why it's not thread safe, because one connection object should be used by one thread, but when I got rid of prep stmt cache - errors disappeared.
Glad that I could help. The DB API specification does not specify the validity of "prepared statement objects". But you can't be wrong if you assume that they work like cursors. So is you have thread safety level 2, then you can only use those prepared statements from the same thread where you have created them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.