views:

143

answers:

2

I'm writing a C# program that monitors a dedicated Gmail account using POP3 for specialized command emails and reacts appropriately.

For maximum reliability, I will run this program on several computers throughout the country. I currently have a race condition where two instances of the program can read the same message before one of them deletes it, causing the message to be processed twice.

How can I make sure that each command is processed exactly once?

Gmail's POP3 access used to only serve each message once (making RETR and DELE a single atomic operation), but I can no longer reproduce this behavior.

The only method of communication between the computers is a SQL Server and an HTTP server (which I control).

A: 

One option I've thought of is to use POP3's UIDL command, and have a table in SQL Server with a unique column of UIDLs that were already processed.

Then, before downloading each message, the daemon would INSERT the UIDL into the table, and, if it got an error, skip the message. (I'm assuming that SQL Server's INSERT command is an atomic operation).

SLaks
All in all sounds like this is going to be the only feasible solution under the circumstances that you have to work with.
jerryjvl
A: 

First I must admit I do not know what commands POP3 supports, but... if you can do an explicit 'DELE' and get an error if the message no longer exists, then I'd say:

  • RETR the message
  • DELE the message
  • Process only if DELE succeeded

EDIT:

After reading RFC1939, this approach should work; from the RFC:

DELE msg

     Arguments:
         a message-number (required) which may NOT refer to a
         message marked as deleted

     Restrictions:
         may only be given in the TRANSACTION state

     Discussion:
         The POP3 server marks the message as deleted.  Any future
         reference to the message-number associated with the message
         in a POP3 command generates an error.  The POP3 server does
         not actually delete the message until the POP3 session
         enters the UPDATE state.

     Possible Responses:
         +OK message deleted
         -ERR no such message

     Examples:
         C: DELE 1
         S: +OK message 1 deleted
            ...
         C: DELE 2
         S: -ERR message 2 already deleted

This is ofcourse assuming that the Gmail implementation actually honours the RFC.

jerryjvl
The DELE command marks the message for deletion.The message is only actually deleted after sending QUIT. (See RFC1939)Therefore, if two daemons delete the same message, they will not receive any errors.
SLaks
Thanks for the RFC number there ;) ... shame that it won't help.
jerryjvl
Actually... reading the RFC now, indicates that using a message number in the DELE that has already been marked as deleted is an error. If Gmail implements RFC1939 faithfully my suggestion should work, in that you'd get an 'ERR' response, rather than an 'OK' response for a message that is already marked deleted.
jerryjvl
The "marked as deleted" state is per-session.Therefore, if two different sessions delete the same message, they will not get an error. (I tried this in telnet using stunnel)
SLaks
Damn... I guess that's a case where the RFC is too vague to condemn either implementation... but you gotta go with what you actually have then.
jerryjvl