Re: [Imap-protocol] QRESYNC and new arrivals which get deleted immediately through VANISHED

Alexey Melnikov <[email protected]>
Newsgroups gmane.mail.imap.general
Message-ID <[email protected]>
Hi Jan,

On 8 Jun 2012, at 17:02, Jan Kundrát <[email protected]> wrote:

> Hi,
> a big part of this mail is probably answered in RFC5162's errata, but
> I'd still like someone to explicitly confirm that how I understand this
> thing is correct.
> 
> I'm afraid I have a possible race condition in my code which keeps
> mailbox up to date with server's responses. Right now, my code supports
> CONDSTORE but not QRESYNC. The following bits describe how it behaves
> after the mailbox has been "synced", ie. when my idea about UID -> seq
> mapping matches with the state on server.
> 
> In this situation, whenever the program needs to perform something (like
> send the LIST command, issue a FETCH for some body part, store some
> flags through UID STORE etc), the command will be sent to the IMAP
> server almost immediately, and this can in turn trigger many interesting
> responses like EXISTS or EXPUNGE. That's why I'm ready to process them
> at any point, no matter if there's a command in progress or not. I
> believe that I'm handling them correctly now:
> 
> - Whenever I receive EXISTS >= current number of messages, I simply add
> the correct number of "empty messages" with UID zero to the end of my
> UID -> seq mapping and to some internal data structure which is in turn
> shown to the user who will see a few "Loading..." placeholders. I also
> immediately queue a UID FETCH previous_uidnext:* FLAGS command. The goal
> is that the UID number will eventually arrive and when they arrive, I
> update my on-disk cache with the obtained data.
> 
> - When EXPUNGE arrives, I check the number for correctness (1 <= number
> < mailbox_size) and remove the message which is at that particular
> index. If its UID was not zero, I also remove any data associated with
> that UID from my on-disk cache. If the number was zero, well, the idea
> is that this shouldn't matter at all as I couldn't possibly have any
> useful data for that message in my cache (message parts, envelopes and
> body structure are fetched only after the UID is known).
> 
> I'm not sure how to deal with it if I enable QRESYNC, though. The spec
> says that I should be ready for both EXPUNGE and VANISHED. I'd like to
> leave my handling of EXPUNGE unchanged if possible, which means that I
> really have to create and insert the fake UID-0 messages immediately in
> response to increased EXISTS.
> 
> The problem is what shall I do when I get VANISHED with a UID which is
> bigger than the highest UID I already know? (Like if a message arrived
> and before I had a chance to ask for its UID, it gets removed through
> VANISHED.) At this point it looks like I can just pick any of the still
> unknown messages in my in-memory state and delete one of them for each
> high UID received in the VANISHED response. I'm a bit nervous about that
> approach, though -- consider the following situation (which is partially
> answered by the QRESYNC errata):
> 
> (Mailbox contains one message with UID 5; UIDNEXT is 11, everything is
> synced.)
> 
> S: * 12 EXISTS
> C: x UID FETCH 11:* (FLAGS)
> 
> (At this point, server sends unsolicited data like FLAGS or even
> BODYSTRUCTURE for the new arrivals. I believe that it's allowed to do so
> per RFC3501.)
> 
> S: * 2 FETCH (FLAGS (a) BODYSTRUCTURE ...)
> S: * 3 FETCH (FLAGS (b) ENVELOPE ...)
> 
> (I'm on a slow network, so even before my UID FETCH arrives to the
> server, one of these messages is gone and the server sends back VANISHED:)
> 
> S: * VANISHED 11
> 
> (The programmer has flipped a coin and hence the code always deletes the
> very last message with UID=0. That means that the message flagged "b" is
> gone and "a" remains.)
> 
> S: * 2 FETCH (FLAGS (b) UID 12)
> 
> (At this point, client finally gets the UID for the only remaining
> message with UID=0, along with flags. The bad thing is that user has
> already seen a stub message flagged as "a" while in fact there was just
> the "b" one.)

Right, guessing which of the yet unknown to your client messages got expunged doesn't look right.
> 
> After reading the errata, I realize that servers are discouraged from
> not sending UIDs in FETCH responses when QRESYNC has been enabled. Would
> it be reasonable for my client to always ignore FETCH updates which do
> not contain UID when QRESYNC is in effect?

Yes.

> (I can easily change my code
> to never issue a FETCH, always UID FETCH with QRESYNC.)

Good idea.

> 
> My biggest concern is that my code perform potentially intrusive actions
> (like allocating complex structures for BODYSTRUCTURE and actually
> showing them to the user) upon seeing the FETCH responses for a given
> message for the first time. When I receive interleaved VANISHED and
> FETCH-without-UID responses, the result could be confusing to the user
> (and given that I already have an application where the messages are
> pushed into an external DB as soon as they arrive, I can imagine a use
> case where people would want to store only messages with a particular
> flag -- again, the code would get confused in the situation I described
> above.)

It looks like your code should get smarter if you don't want to just ignore FETCH responses with no UIDs. 

> 
> It seems to me that if the QRESYNC extension introduced new * ARRIVED
> <uid-sequence-set> response to replace EXISTS in a similar way that
> VANISHED replaces EXPUNGE, the window where client knows about new
> arrivals but doesn't know about the UIDs would cease to exist. Is there
> a particular reason why QRESYNC is not modeled in that way?

The idea didn't' occur at the time of writing the QRESYNC spec :).
> 
> Finally, my last question is why does QRESYNC require me to issue an
> ENABLE QRESYNC at all? I feel like the SELECT ... QRESYNC should be
> enough to tell the server that I'm really expecting the VANISHED
> responses. My biggest issue with ENABLE is that (at least according to
> the last errata by Alfred Hoenes from 2008-03-11 which is "held for
> document update") I'm supposed to wait for untagged ENABLED before
> issuing SELECT ... QRESYNC which introduces another roundtrip to the
> connection process. I'm ignoring this requirement for now, so I'll
> happily pipeline ENABLE QRESYNC with SELECT ... QRESYNC and hope that I
> won't get BAD in response from some pedantic server. I'd love to get
> some comments on this as well.

ENABLE QRESYNC failing should be a rare event, so doing the optimization you do seems to be Ok, as long as you can handle the BAD response.

Best Regards,
Alexey


_______________________________________________
Imap-protocol mailing list
[email protected]
http://mailman2.u.washington.edu/mailman/listinfo/imap-protocol
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.