I'm using PostgreSQL 9.4.4 and the default configuration. I use a JDBC connection to run multiple queries in succession. Only one Java thread is using the connection and before a new query is executed the result set of the former one is fetched. Below a simplified sketch:
while(true) {
PreparedStatement stmt = //...
ResultSet rs = stmt.executeQuery();
//process result
}
The query that is causing trouble invokes the following stored procedure:
CREATE OR REPLACE FUNCTION delete_top_message(_queue_id BIGINT
, _source_client_id BIGINT)
RETURNS TEXT AS
$$
DECLARE rv TEXT;
BEGIN
DELETE FROM message m
USING (
SELECT * FROM message
WHERE message.queue_id = _queue_id
AND (message.receiver_client_id = _source_client_id OR
message.receiver_client_id = -1)
ORDER BY insertion_time ASC LIMIT 1) AS tmp
WHERE m.id = tmp.id
RETURNING m.content INTO rv;
IF (SELECT rv IS NULL) THEN
RAISE 'No message available.';
END IF;
RETURN rv;
END;
$$ LANGUAGE plpgsql;
I am observing behavior as it is described here http://www.postgresql.org/docs/9.1/static/transaction-iso.html in the section 13.2.1. Read Committed Isolation Level.
What happens is that the exception in the procedure is triggered although when I abort everything when the exception happens and invoke the procedure manually the exception is not raised. I noticed further that if a transaction is already holding a row exclusive lock on the message table that then the exception is raised.
Also if I add
LOCK TABLE message IN ROW EXCLUSIVE MODE;
at the beginning of the procedure the problem disappears.
I thus suspect that in the above loop that a query is returning a result before its transaction is actually committed such that the next transaction is conflicting with it. Can that happen? N.B. I did not find something that explains this in the PostgreSQL documentation. Also I don't have Async commit enabled.