The bullet-proof solution should be setting twin-field primary key in db level, I believe. Hope somebody can show us how to do that in web2py db.define_table(...), if not using db.executesql('set up the primary key blah blah').
On the other hand, in a non-db level, inside web2py, perhaps Massimo will introduce a global ThreadLock so that developers can do: def invite(): with web2py_lock: form = ... if form.accepts(...): ... or maybe a syntactic sugar: @with_lock def invite(): all_the_original_code Wait and see what other may say. On May28, 1:44am, Carl <carl.ro...@gmail.com> wrote: > thanks Iceberg. > > your approach is pretty much what I have right now. I'm looking for a > bullet-proof approach. > otherwise I'm running code with "fingers crossed two users don't use > the system at the same time" > > and I want to encourage usage :) > > C > > On May 27, 6:09 pm, Iceberg <iceb...@21cn.com> wrote: > > > > > On May27, 11:32pm, Carl <carl.ro...@gmail.com> wrote: > > > > the flaw is in my architecture not web2py. can you help with a better > > > approach? Needs to be BigTable-friendly to boot. > > > > I have two user types: Agents and Candidates > > > Agents invite Candidates to Parties. > > > > I keep a Party table recording all such Party invites. > > > Candidates can be invited to the same Party by several Agents so I > > > have an Invite table recording which Agents invited which Candidates > > > to which Parties. > > > > 1. On a new Party invitation I check if the Candidate has already been > > > invited by looking in the Party table. > > > 2. If a party isn't found then I insert into the Party table > > > 3. I insert the Agent into Invite table has a value pointing to the > > > appropriate Party row. > > > > Here's the "race condition"... > > > > **Between** steps 1 and 2 above another party invite is sent and > > > checked for pre-existance. it's not found because step 2 by the 1st > > > agent's run through hasn't yet executed. > > > > Thus duplicate party invitations are inserted into the database. > > > > What's the better approach to employ? > > > Here is my attempt. Not a perfect one. > > > db.define_table('Agent', Field('name', unique=True)) > > db.define_table('Candidate', Field('name', unique=True)) > > db.define_table('Party', Field('name', unique=True)) > > db.define_table('Invitation', > > Field('agent', db.Agent), > > Field('candidate', db.Candidate), > > Field('party', db.Party), > > # Ideally, we should set up two fields, candidate and party, > > # as combinated primary key in db level. But I don't know how. > > ) > > > # Then, in controller > > def invite(): > > def no_duplicate(form): > > if db( (db.Invitation.candidate==form.vars.candidate) > > & (db.Invitation.party==form.vars.party) ).count(): > > form.errors.candidate = 'Already invited to this party' > > form = SQLFORM(db.Invitation) > > if form.accepts( ..., onvalidation=no_duplicate): > > response.flash = 'ok' > > # However, theoretically speaking there is still a slim time gap, > > # maybe 0.2 second, between the onvalidation and the later > > insertion. > > return {'':form}