mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 17:06:33 +03:00
Check/set connection status at commit inside the critical section
Failing to do so was causing the issue reported in ticket #103. The issue as reported was fixed when SET ISOLATION LEVEL was dropped, but the real problem wasn't fixed.
This commit is contained in:
parent
b2c61eaa18
commit
5fcbe7bd0f
1
NEWS
1
NEWS
|
@ -12,6 +12,7 @@ What's new in psycopg 2.4.5
|
||||||
- Fixed 'rownumber' during iteration on cursor subclasses.
|
- Fixed 'rownumber' during iteration on cursor subclasses.
|
||||||
Regression introduced in 2.4.4 (ticket #100).
|
Regression introduced in 2.4.4 (ticket #100).
|
||||||
- Added support for 'inet' arrays.
|
- Added support for 'inet' arrays.
|
||||||
|
- Fixed 'commit()' concurrency problem (ticket #103).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.4.4
|
What's new in psycopg 2.4.4
|
||||||
|
|
|
@ -444,34 +444,35 @@ pq_commit(connectionObject *conn)
|
||||||
PGresult *pgres = NULL;
|
PGresult *pgres = NULL;
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&conn->lock);
|
||||||
|
|
||||||
Dprintf("pq_commit: pgconn = %p, autocommit = %d, status = %d",
|
Dprintf("pq_commit: pgconn = %p, autocommit = %d, status = %d",
|
||||||
conn->pgconn, conn->autocommit, conn->status);
|
conn->pgconn, conn->autocommit, conn->status);
|
||||||
|
|
||||||
if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) {
|
if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) {
|
||||||
Dprintf("pq_commit: no transaction to commit");
|
Dprintf("pq_commit: no transaction to commit");
|
||||||
return 0;
|
retvalue = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
conn->mark += 1;
|
||||||
|
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error, &_save);
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
pthread_mutex_lock(&conn->lock);
|
|
||||||
conn->mark += 1;
|
|
||||||
|
|
||||||
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error, &_save);
|
|
||||||
|
|
||||||
Py_BLOCK_THREADS;
|
Py_BLOCK_THREADS;
|
||||||
conn_notice_process(conn);
|
conn_notice_process(conn);
|
||||||
Py_UNBLOCK_THREADS;
|
Py_UNBLOCK_THREADS;
|
||||||
|
|
||||||
|
/* Even if an error occurred, the connection will be rolled back,
|
||||||
|
so we unconditionally set the connection status here. */
|
||||||
|
conn->status = CONN_STATUS_READY;
|
||||||
|
|
||||||
pthread_mutex_unlock(&conn->lock);
|
pthread_mutex_unlock(&conn->lock);
|
||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
if (retvalue < 0)
|
if (retvalue < 0)
|
||||||
pq_complete_error(conn, &pgres, &error);
|
pq_complete_error(conn, &pgres, &error);
|
||||||
|
|
||||||
/* Even if an error occurred, the connection will be rolled back,
|
|
||||||
so we unconditionally set the connection status here. */
|
|
||||||
conn->status = CONN_STATUS_READY;
|
|
||||||
|
|
||||||
return retvalue;
|
return retvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,35 @@ class ConnectionTests(unittest.TestCase):
|
||||||
gc.collect()
|
gc.collect()
|
||||||
self.assert_(w() is None)
|
self.assert_(w() is None)
|
||||||
|
|
||||||
|
def test_commit_concurrency(self):
|
||||||
|
# The problem is the one reported in ticket #103. Because of bad
|
||||||
|
# status check, we commit even when a commit is already on its way.
|
||||||
|
# We can detect this condition by the warnings.
|
||||||
|
conn = self.conn
|
||||||
|
notices = []
|
||||||
|
stop = []
|
||||||
|
|
||||||
|
def committer():
|
||||||
|
while not stop:
|
||||||
|
conn.commit()
|
||||||
|
while conn.notices:
|
||||||
|
notices.append((2, conn.notices.pop()))
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
t1 = threading.Thread(target=committer)
|
||||||
|
t1.start()
|
||||||
|
i = 1
|
||||||
|
for i in range(1000):
|
||||||
|
cur.execute("select %s;",(i,))
|
||||||
|
conn.commit()
|
||||||
|
while conn.notices:
|
||||||
|
notices.append((1, conn.notices.pop()))
|
||||||
|
|
||||||
|
# Stop the committer thread
|
||||||
|
stop.append(True)
|
||||||
|
|
||||||
|
self.assert_(not notices, "%d notices raised" % len(notices))
|
||||||
|
|
||||||
|
|
||||||
class IsolationLevelsTestCase(unittest.TestCase):
|
class IsolationLevelsTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user