Received: from localhost (mx1.hub.org [200.46.208.251]) by postgresql.org (Postfix) with ESMTP id 0FCBE9FA646 for ; Thu, 2 Nov 2006 18:12:14 -0400 (AST) Received: from postgresql.org ([200.46.204.71]) by localhost (mx1.hub.org [200.46.208.251]) (amavisd-new, port 10024) with ESMTP id 83962-01 for ; Thu, 2 Nov 2006 18:12:08 -0400 (AST) X-Greylist: from auto-whitelisted by SQLgrey- Received: from floppy.pyrenet.fr (floppy.pyrenet.fr [194.116.145.2]) by postgresql.org (Postfix) with ESMTP id D27229FA3B1 for ; Thu, 2 Nov 2006 18:12:06 -0400 (AST) Received: by floppy.pyrenet.fr (Postfix, from userid 106) id 7BCDC308F7; Thu, 2 Nov 2006 23:12:05 +0100 (MET) From: "Bobus" X-Newsgroups: pgsql.sql Subject: Re: fetching unique pins in a high-transaction environment... Date: 29 Oct 2006 22:13:22 -0800 Organization: http://groups.google.com Lines: 64 Message-ID: <1162188802.731436.198130@h48g2000cwc.googlegroups.com> References: <1162139808.031657.23340@f16g2000cwb.googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" X-Complaints-To: groups-abuse@google.com User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0,gzip(gfe),gzip(gfe) Complaints-To: groups-abuse@google.com Injection-Info: h48g2000cwc.googlegroups.com; posting-host=202.57.110.170; posting-account=OzfgeQ0AAAA5-_8F6HU0QheK-BteRp5f To: pgsql-sql@postgresql.org X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=0.371 tagged_above=0 required=5 tests=AWL, BAYES_20, DATE_IN_PAST_48_96 X-Spam-Level: X-Archive-Number: 200611/68 X-Sequence-Number: 26720 I think we've figured out a way to implement the equivalent of a READPAST hint in a function. The basic idea is to loop until we find the next available unlocked row, using the lock_not_available exception to determine if the record is locked or not. Our early testing seems to indicate that this solution will work, but we would love to hear about simpler and more efficient ways to accomplish this. Here's a simplified version of the function which illustrates the principle: CREATE OR REPLACE FUNCTION "getpin"() RETURNS varchar as $$ DECLARE v_id integer := 0; v_pin varchar; BEGIN LOOP BEGIN -- Find the first available PIN. -- Note: we cannot lock down the row here since we need to be -- able to store the ID of the pin to implement the READPAST. select id into v_id from pins where id > v_id and status = 0 order by id limit 1; -- Exit if there are no PINs available. IF NOT FOUND THEN RAISE EXCEPTION 'no pins available'; END IF; -- Lock down the PIN. If another transaction beat us to it, we -- trap the error (see below) and loop looking for the next -- available pin. If another transaction already updated the -- status to 1 in between this select and the previous, then we -- loop (see ELSE statement). select pin into v_pin from pins where id = v_id and status = 0 for update nowait; IF FOUND THEN -- Update the PIN. The status = 0 check is unnecessary, -- but better safe than sorry. update pins set status = 1 where id = v_id and status = 0; -- I don't think this should ever happen. IF NOT FOUND THEN RAISE EXCEPTION 'this should never happen'; END IF; RETURN v_pin; ELSE -- Somebody snuck in and updated/grabbed the pin. Loop. END IF; EXCEPTION WHEN lock_not_available THEN -- Loop looking for the next available unlocked pin. END; END LOOP; END; $$ language plpgsql; Thanks...