开发者

How to write my own global lock / unlock functions for PostgreSQL

I have postgresql (in perlu) function getTravelTime(integer, timestamp), which tries to select data for specified ID and timestamp. If there are no data or if data is old, it downloads them from external server (downloading time ~300ms).

Multiple process use this database and this function. There is an error whe开发者_开发知识库n two process do not find data and download them and try to do an insert to travel_time table (id and timestamp pair have to be unique). I thought about locks. Locking whole table would block all processes and allow only one to proceed. I need to lock only on id and timestamp. pg_advisory_lock seems to lock only in "current session". But my processes uses their own sessions.

I tried to write my own lock/unlock functions. Am I doing it right? I use active waiting, how can I omit this? Maybe there is a way to use pg_advisory_lock() as global lock?

My code:

CREATE TABLE travel_time_locks (
    id_key integer NOT NULL,
    time_key timestamp without time zone NOT NULL,
    UNIQUE (id_key, time_key) 
);

------------
-- Function: mylock(integer, timestamp)
DROP FUNCTION IF EXISTS mylock(integer, timestamp) CASCADE;
-- Usage: SELECT mylock(1, '2010-03-28T19:45');
-- function tries to do a global lock similar to pg_advisory_lock(key, key)
CREATE OR REPLACE FUNCTION mylock(id_input integer, time_input timestamp)
  RETURNS void AS
$BODY$
DECLARE
    rows int;
BEGIN
    LOOP

        BEGIN
            -- active waiting here !!!! :(
            INSERT INTO travel_time_locks (id_key, time_key) VALUES (id_input, time_input);
        EXCEPTION WHEN unique_violation THEN
            CONTINUE;
        END;
        EXIT;
    END LOOP;
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE
    COST 1;

------------
-- Function: myunlock(integer, timestamp)
DROP FUNCTION IF EXISTS myunlock(integer, timestamp) CASCADE;
-- Usage: SELECT myunlock(1, '2010-03-28T19:45');
-- function tries to do a global unlock similar to pg_advisory_unlock(key, key)
CREATE OR REPLACE FUNCTION myunlock(id_input integer, time_input timestamp)
  RETURNS integer AS
$BODY$
DECLARE
BEGIN
    DELETE FROM ONLY travel_time_locks WHERE id_key=id_input AND time_key=time_input;
    RETURN 1;
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE
    COST 1;


Use pg_advisory_lock(). It is exactly it what you need.


After further testing I found that "current session" means whole instance of PostgreSQL backend, not just a single connection.

My solution is :PERFORM pg_advisory_lock(id, extract(epoch from my_time)::int ); (+ unlock) inside my getTravelTime function.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜