How to prevent duplicate key error when two processes do DELETE/INSERT simultaneously?

From: Brodie Thiesfield <brofield+pgsql(at)gmail(dot)com>
To: pgsql-general(at)postgresql(dot)org
Subject: How to prevent duplicate key error when two processes do DELETE/INSERT simultaneously?
Date: 2009-07-29 14:31:39
Message-ID: a6507e6c0907290731g3d2b9b49kd0fe576bc88da3dd@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Hi,

I've got a problem with a PG client that I'm not sure how to fix.

Essentially, I have two processes connecting to a single PG database
and simultaneously issuing the following statements:

BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DELETE FROM licence_properties WHERE key = xxx;
INSERT INTO licence_properties ... values with key = xxx;
COMMIT

One of these processes is getting to the INSERT and failing with
duplicate key error.
ERROR: duplicate key value violates unique constraint

The DELETE should prevent this duplicate key error from occurring. I
thought that the ISOLATION LEVEL SERIALIZABLE would fix this problem
(being that the second process can see the INSERT from the first
process after it has done the DELETE), but it doesn't.

I am obviously going about this the wrong way. The database layer is
implemented for a number of different servers and so I was trying to
keep it simple. However, perhaps this is something that I can't
simplify. Should I do SELECT FOR UPDATE and then either an INSERT or
UPDATE?

I would be very appreciative if someone more knowledgeable would point
me to the correct way of doing this? The full PG log of the two
processes follows (in case it is useful).

Regards,
Brodie

log_line_prefix = '%m %p %x %v '
(timestamp, process ID, transaction ID, virtual transaction ID)

logs are stable sorted by process ID then timestamp.

Process 1:
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement:
BEGIN;SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement:
SAVEPOINT _EXEC_SVP_03741378
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement: SELECT
lid FROM licences WHERE groupid = E'' AND userid = E'test';
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement: RELEASE
_EXEC_SVP_03741378
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement:
SAVEPOINT _EXEC_SVP_03740350
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement: DELETE
FROM licence_properties WHERE lid = '114' AND pkey = E'mytestprop' AND
sessionid IN (0, '0') AND readonly IN (0, 0);
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement: RELEASE
_EXEC_SVP_03740350
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement:
SAVEPOINT _EXEC_SVP_03742768
2009-07-29 23:01:01.218 JST 6108 0 3/65 LOG: statement: INSERT
INTO licence_properties (lid, sessionid, pkey, value, readonly) VALUES
('114', '0', E'mytestprop', E'update2', 0);
2009-07-29 23:01:01.218 JST 6108 453328 3/65 LOG: statement: RELEASE
_EXEC_SVP_03742768
2009-07-29 23:01:01.218 JST 6108 453328 3/65 LOG: statement:
SAVEPOINT _EXEC_SVP_03740868
2009-07-29 23:01:01.218 JST 6108 453328 3/65 LOG: statement: UPDATE
licences SET revision = revision + 1 WHERE groupid = E'' AND userid =
E'test';
2009-07-29 23:01:01.218 JST 6108 453328 3/65 LOG: statement: RELEASE
_EXEC_SVP_03740868
2009-07-29 23:01:01.218 JST 6108 453328 3/65 LOG: statement: COMMIT

Process 2:
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement:
BEGIN;SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement:
SAVEPOINT _EXEC_SVP_03741378
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement: SELECT
lid FROM licences WHERE groupid = E'' AND userid = E'test';
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement: RELEASE
_EXEC_SVP_03741378
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement:
SAVEPOINT _EXEC_SVP_03740350
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement: DELETE
FROM licence_properties WHERE lid = '114' AND pkey = E'mytestprop' AND
sessionid IN (0, '0') AND readonly IN (0, 0);
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement: RELEASE
_EXEC_SVP_03740350
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement:
SAVEPOINT _EXEC_SVP_03742768
2009-07-29 23:01:01.218 JST 5460 0 2/47 LOG: statement: INSERT
INTO licence_properties (lid, sessionid, pkey, value, readonly) VALUES
('114', '0', E'mytestprop', E'update2', 0);
2009-07-29 23:01:01.218 JST 5460 453330 2/47 ERROR: duplicate key
value violates unique constraint "pk_lic_prop"
2009-07-29 23:01:01.218 JST 5460 453330 2/47 STATEMENT: INSERT INTO
licence_properties (lid, sessionid, pkey, value, readonly) VALUES
('114', '0', E'mytestprop', E'update2', 0);
2009-07-29 23:01:01.218 JST 5460 453330 2/47 LOG: statement: ROLLBACK
to _EXEC_SVP_03742768
2009-07-29 23:01:01.218 JST 5460 453330 2/47 LOG: statement: RELEASE
_EXEC_SVP_03742768
2009-07-29 23:01:01.218 JST 5460 453330 2/47 LOG: statement: ROLLBACK

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Antonio José García Lagar 2009-07-29 14:35:29 OID in $_TD->{new}/$_TD->{old}
Previous Message nha 2009-07-29 14:26:54 Re: combining db's- importing primary keys