Re: finding reusable ids

From: Michael Glaesemann <grzm(at)seespotcode(dot)net>
To: Kenji Morishige <kenjim(at)juniper(dot)net>
Cc: Nis Jørgensen <nis(at)superlativ(dot)dk>, pgsql-general(at)postgresql(dot)org
Subject: Re: finding reusable ids
Date: 2007-08-07 17:30:58
Message-ID: 0D417BF5-5523-4F54-BF12-0EF9EF4EB6FC@seespotcode.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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

In response to

Browse pgsql-general by date

  From Date Subject
Next Message Simon Riggs 2007-08-07 17:57:34 Re: truncate transaction log
Previous Message Alexander Staubo 2007-08-07 16:37:11 Re: clustering failover... ala Oracle Parallel server