Re: finding reusable ids - Mailing list pgsql-general

From Michael Glaesemann
Subject Re: finding reusable ids
Date
Msg-id 0D417BF5-5523-4F54-BF12-0EF9EF4EB6FC@seespotcode.net
Whole thread Raw
In response to Re: finding reusable ids  (Kenji Morishige <kenjim@juniper.net>)
List pgsql-general
[Please don't top post as it makes the discussion more difficult to
follow.]

On Aug 7, 2007, at 9:05 , Kenji Morishige wrote:


> On Tue, Aug 07, 2007 at 12:23:00PM +0200, Nis Jørgensen wrote:
>>
>> This doesn't solve your problem, but might simplify the query to
>> find a
>> new id - something like this (untested):
>>
>> SELECT min(q.co_id) +1
>> FROM (
>> SELECT (co_id + 1) as co_id FROM checked_out
>> EXCEPT
>> SELECT co_id FROM checked_out
>> ) q;

I don't believe this is concurrency safe, even if wrapped in a
transaction. Two concurrent transactions could end up calculating the
same next co_id. This could be caught by having appropriate
constraints on checked_out and retrying on error.

> Actually, I already have a resource table that stores the uid of
> the item in
> question.  The checkout table does double duty as a history
> mechanism and a
> check-out mechanism.  I think you are on the right track, I should
> seperate
> these two tasks and possibly create another table.  The actual
> design is a
> bit more complicated as we actually don't have a a checked-in flag,
> but a
> start and finish time where users can actually store multiple
> overlapping
> records.

I agree that you should probably tweak the schema a bit. Also, as you
want to reuse your checkout ids, you're actually considering them a
separate resoure, so you might consider putting them in a separate
table. Here's what I came up with:

As an aside, I wouldn't call them checkout_ids (even though I
did :)), as id often connotes a unique identifier something (like
your uids), and you're reusing them. I might call them
checkout_reference or checkout_number or something.

CREATE TABLE checkout_ids
(
     checkout_id INTEGER PRIMARY KEY
     , is_checked_out BOOLEAN NOT NULL
         DEFAULT FALSE
     , UNIQUE (checkout_id, is_checked_out)
);
-- populate the table with the values you'll use
INSERT INTO checkout_ids (checkout_id)
SELECT generate_series(1,999999);

CREATE TABLE checkouts
(
     checkout_id INTEGER PRIMARY KEY
     , is_checked_out BOOLEAN NOT NULL
         CHECK (is_checked_out)
         DEFAULT TRUE
     , FOREIGN KEY (checkout_id, is_checked_out)
         REFERENCES checkout_ids (checkout_id, is_checked_out)
     , uid INTEGER NOT NULL -- with some fk
);

-- Of course, you can add the checkout start and end dates/timestamps
to this table:
-- they're independent of managing the checkout_id resource
-- I've added is_checked_out to this table to ensure that all
checkouts (checkout_id) have
-- is_checked_out set to true (via the CHECK constraint). This could
also be done with a
-- trigger.

-- And a couple quick functions to handle the process of checking in
and checking out.
-- The SELECT ... FOR UPDATE in checkout should ensure that
concurrent transactions
-- aren't grabbing the same checkout_id.

CREATE FUNCTION checkout (p_uid INTEGER)
RETURNS INTEGER -- checkout_id
LANGUAGE plpgsql AS $body$
DECLARE
     v_checkout_id INTEGER;
BEGIN
     SELECT INTO v_checkout_id
         checkout_id
     FROM checkout_ids
     WHERE NOT is_checked_out
     LIMIT 1
     FOR UPDATE;

     UPDATE checkout_ids
     SET is_checked_out = TRUE
     WHERE checkout_id = v_checkout_id;

     INSERT INTO checkouts (checkout_id, uid)
     VALUES (v_checkout_id, p_uid);
     RETURN v_checkout_id;
END;
$body$;

CREATE FUNCTION checkin (p_checkout_id INTEGER)
RETURNS VOID
LANGUAGE plpgsql AS $body$
BEGIN

     DELETE FROM checkouts
     WHERE checkout_id = p_checkout_id;

     UPDATE checkout_ids
     SET is_checked_out = FALSE
     WHERE checkout_id = p_checkout_id;

     RETURN;
END;
$body$;

Hope this helps.

Michael Glaesemann
grzm seespotcode net



pgsql-general by date:

Previous
From: "Alexander Staubo"
Date:
Subject: Re: clustering failover... ala Oracle Parallel server
Next
From: "Simon Riggs"
Date:
Subject: Re: truncate transaction log