MBOX-Line: From slusarz at curecanti.org  Wed Nov 21 17:36:06 2012
To: imap-protocol@u.washington.edu
From: Michael M Slusarz <slusarz@curecanti.org>
Date: Fri Jun  8 12:34:49 2018
Subject: [Imap-protocol] Re: Suspend/Restore feature proposal
In-Reply-To: <CABa8R6sJfnA=HYk-fmFfpXki91cne_LeqFyuhDebMGeDdsdj7g@mail.gmail.com>
References: <20121115215854.Horde.zz6B0O0tmt3ylHiEXzXhQQ4@bigworm.curecanti.org>
	<CABa8R6tHP2My0k2LqT1RzHoLQZA+X_jwUMU0cydm2sAwo8f4Fg@mail.gmail.com>
	<20121116143137.Horde.7Kb3aEW5DhM6CnuA2hAx4Q8@bigworm.curecanti.org>
	<e122fa25-9110-4b36-855d-0e7e273c5805@flaska.net>
	<20121121155417.Horde.ZeW7JqTPNxTAI-hTtrAT-Q9@bigworm.curecanti.org>
	<CABa8R6sJfnA=HYk-fmFfpXki91cne_LeqFyuhDebMGeDdsdj7g@mail.gmail.com>
Message-ID: <20121121183606.Horde.8DDdRoJ261AbQsAiMPfS4w1@bigworm.curecanti.org>

Quoting Brandon Long <blong@google.com>:

> On Wed, Nov 21, 2012 at 2:54 PM, Michael M Slusarz  
> <slusarz@curecanti.org>wrote:
>
>> Quoting Jan Kundr?t <jkt@flaska.net>:
>>
>>  Hi Michael,
>>> I've read your draft, it's an interesting extension. However, it seems to
>>> me that the whole point here is to save a few roundtrips by skipping the
>>> process of activating/configuring various optional features. I'll discuss
>>> each extension separately.
>>>
>>
>> I would strongly disagree with this statement.  As written, the draft is
>> only minimally concerned with saving on network round-trips.
>>
>
> Yet, that and bytes are what you quote from your example.

That would be a selective reading of my example.  I spent the entire  
previous paragraph before introducing the example stating that other  
methods of benchmarking would be desirable, but impractical/impossible  
at this time.

I agree that byte counting isn't particularly useful as a benchmark,  
although it does provide some context (for example: a protocol  
addition that ADDED net additional bytes on-the-wire would tend to  
indicate the design/theory is flawed).

And I also explicitly indicated the net amount of  
commands/round-trips/additional IMAP protocol items that were saved,  
which is a more useful benchmark.

>> Sidebar: I'm not a huge fan in general of pipelining as a performance
>> since it is not always a feasible option for clients.  For example, a
>> client may use an OO-library to connect to the IMAP server.  This library
>> may not provide a reasonable (or any) way of allowing multiple commands to
>> be sent at once via the API.  For example, to start compression, enable
>> QRESYNC, and set the language, it is more than reasonable to expect this
>> kind of pseudocode:
>>
>> $result = $imap->useCompression(true);
>> // Check for success
>> $imap->useQresync(true);
>> // Check for success
>> $imap->setLanguage([LANGUAGE])**;
>> // Check for success
>>
>> In any OO IMAP interface the order of IMAP commands to allow for efficient
>> pipelining, or the fact that pipelining even exists, should obviously not
>> be a part of the API.  Thus pipelining is fairly useless in the real world
>> as a way to guarantee an increase in performance.
>>
> Thats not a true statement.  Its useless if you're forced to use a bad
> client API that you aren't willing to work around?  Is that bad client API
> going to support adding a new command?

How do you suggest writing an IMAP client library API in which the  
user of the API doesn't need to know ANYTHING about IMAP?  Any API  
that requires the client author to know about pipelining or other IMAP  
protocol details is worthless.  In fact, I'd go so far to say that a  
useful mail client library API should allow interaction with both an  
IMAP and POP server using the same commands, albeit with the  
expectation that some of the more advanced commands - e.g. ACLs -  
would necessarily need to be null actions when using a POP3 backend.

SUSPEND is a general solution and easy to implement (IMHO) and would  
allow performance gains without understanding the more esoteric  
details of pipelining.  The simple fact that we are discussing which  
commands would be appropriate for pipelining highlights the latter.

>> There are other, more important reasons why a mechanism to restore
>> configuration is useful:
>>
>> - It prevents the need to re-parse the CAPABILITY list.  Note that parsing
>> the CAPABILITY list involves *MUCH* more than just the actual string
>> tokenization of the list, although this alone may not be a trivial task
>> (see below).
>>
>> A client may, depending on the capabilities returned, need to perform
>> various internal initialization tasks.  For example - if CONDSTORE/QRESYNC
>> is listed, a client may have to then parse a separate configuration file to
>> grab the details of the local cache where it is storing this information,
>> and then connect to this cache, etc.  Or if language is listed, a client
>> might have to parse a local list of language availability to determine if
>> it can/should change the language.
>>
>
> And... it would have had to do that anyways.  If its resuming the
> connection, it has to either do that initialization or it has to cache that
> initialization, both of which it can do just as easily on non-resume if
> we're talking about a new connection every 10s.

You've proven my point exactly.  For you, this kind of initialization  
may be trivial.  For another client, this isn't.  You can't make  
assumptions (incorrectly, in your case) about how a client does work  
or should work depending on how you do/would do things.

> Parse a local list of language availability?  So, you want to avoid reading
> a local config file ... on the hope that the client doesn't need to know
> that language information anyways just to display stuff to the user which
> doesn't come from the server?

More assumptions about client behavior.  I'll agree that my initial  
example may not be a tremendously useful/practical example, but it is  
a useful analogy for other initialization tasks that may occur.

>> And CAPABILITY parsing is more than just determining what capabilities are
>> listed.  It is also determining which capabilities SHOULD not be listed.
>>  Just today, Cyrus was fixed due to a bug that our code was triggering:
>> APPENDing binary data via a literal8 caused Cyrus to immediately terminate
>> the connection with a BYE response.  Our code is smart enough to catch this
>> broken behavior by removing BINARY appending from the list of available
>> capabilities.  But without a way to ensure that every subsequent connection
>> is a continuation of the current session, we have to do this detection
>> EVERY SINGLE TIME.  This is potentially a huge performance hit, since we
>> may be appending MBs of data to the server before the BYE response can be
>> returned (e.g. appending a sent-mail message containing attachments).
>>
>
> So, you worked around the cyrus bug by determining the connection exhibits
> the bug and then never using it for that server again?  Or, you could just
> not use BINARY ever, or issue an ID command to know if the remote server
> has the bug. You could even pipeline it!  Or you could store the  
> known bad server
> information somewhere in your app server.

* Without a way of determining if the IMAP server we connect to is the  
same IMAP server we previously connected to when we determined BINARY  
literal8's were broken, there's nothing we can do except try all over  
again.  You obviously can't assume that the IMAP server is the same.   
Several of my clients use IMAP load-balancing, and all backend IMAP  
servers may not be running the same IMAP software/version.

* Ack!  You didn't just say to use the ID command did you?  RFC 2971 [3]:

    Implementations MUST NOT make operational changes based on the data
    sent as part of the ID command or response.  The ID command is for
    human consumption only, and is not to be used in improving the
    performance of clients or servers.

    This includes, but is not limited to, the following:

       [...] Clients MUST NOT attempt to work
       around server bugs based on the ID response.

* The Cyrus break apparently only happened recently.  Hypothetically:  
even if using ID information (BAD!), how does that help all of the  
admins that have installed previous versions of our software where the  
ID sniff does not catch the issue?

* Not use BINARY ever?  How do you send null characters?  And because  
*1* version of *1* server is broken, EVERY other server that has ever,  
or will ever, support BINARY has to be ignored?  That's a bummer.

* (Getting a bit off topic...) If a server supports BINARY - or at  
least if it claims to support BINARY - it is a big time win to just  
send all literals as literal8's.  That way you don't have to scan the  
data stream for nulls, which is potentially an expensive operation  
when the APPENDed data is 10's of MBs in size (users love sending 10  
camera pics in outgoing e-mails for some reason...).  So sending  
literal8's is a significant performance improvement.  If a server  
reports that it supports BINARY, who are we to argue?

* For the record, Cyrus is not the only one that has a broken BINARY  
literal8 implementation (that I know of). For fun, try this on a  
UW-IMAP BINARY capable server:

A1 APPEND INBOX ~{1}
A1 BAD Missing literal in APPEND

>> - As mentioned above, sending an initialization command to the server may
>> take quite a bit of work on the client side to prepare.  It's not as easy
>> as hardcoding 'ENABLE QRESYNC' in client code - it may take quite a bit of
>> CPU cycles to get to that point in a given client.
>>
>
> But this doesn't change anything about the having to do that.  Regardless
> of whether you're resuming a session or not, you still have to do that work.

Back to a client assumption.  It is *much* cheaper for us to resume  
our session than to reinitialize.  It may not be true with your  
implementation but that's irrelevant.

>> Another example: a client keeps all of its imap initialization code in a
>> separate dynamically-loadable module.  If the session is successfully
>> resumed, this module does not need to be loaded/interpreted/run.
>>
>
> What kind of clients are we talking about?  I'm just completely failing to
> think this is an issue.

Ours.  We keep initialization code in a completely separate class  
(PHP).  That class is never loaded if we don't need to re-initialize  
(this currently happens when using the current XIMAPPROXY feature  
discussed in the original thread e-mail).

>> - From the server side, it may be much more expensive to initiate an IMAP
>> session as compared with resuming one.  This draft allows the server to
>> optimize if possible.  I believe Timo's post indicates that resuming in
>> Dovecot is more efficient than creating a new session.
>>
>
> Resuming into the middle of a selected folder seems cheap.  Resuming the
> status of N commands seems  optimizing in the small.  I would think it
> would cost more in terms of either caching the data in memory or stashing
> the data to disk/reading it back than the overhead of parsing a couple
> commands and maybe allocating a data structure.

I am not a server author so I can't speak to this - one of the reasons  
I started this thread was to get feedback on just this issue.

However, I believe Timo has indicated that it is potentially a  
performance win (although he is discussing in a slightly different  
context):

http://markmail.org/message/qp45yod5ukqf3jfn

>> - Even when pipelining commands, they still need to be sent, the incoming
>> command needs to be tokenized (server), the command is performed (server),
>> the response sent back, any untagged responses are tokenized (client), the
>> untagged responses are interpreted (client), the tagged response is
>> tokenized (client), and the tagged response is processed (client).  None of
>> this is "free".  Pipelining eliminates none of this.
>
>
> Tokenization is what clients and servers do, and this takes a trivial
> amount of time and cpu in reasonable languages.

Yes and no.  The higher-level language you get, the less of a chance  
you get to optimize this.  PHP, which I am stuck with, will not be as  
efficient as C at doing these kind of actions so I can only do so much  
to improve tokenization speed.  I'm not claiming that this is vastly  
going to improve performance, but you also can't argue that it can't  
hurt.

Not to mention that I have seen many poorly-written tokenizers doing  
things like using regexps to parse IMAP responses.  For these clients,  
any reduction in the number of commands processed is a much bigger win.

So for well-written IMAP clients containing highly tuned tokenizers, I  
would agree this advantage is of dubious value.  But well-written IMAP  
clients are probably in the minority.

>>  COMPRESS=DEFLATE
>
> And what does "resuming" a COMPRESS=DEFLATE do?  I assume we're not talking
> about trying to keep the old dictionary or anything like that, right?
>  We're actually talking about just the equivalent of starting it again.

I would suggest the proper behavior would be to re-start the  
compression behavior after the tagged command containing the resume  
response code.  Whether the dictionary should be retained from a  
previous section would be a decision entirely up to the implementer.

>  And finally, what IMAP servers support the LANGUAGE extension?
>>>
>>
>> Why does this matter?  RFC 5255 is a Standards Track extension.  A year
>> from now, every IMAP server and 200 new ones may support it.
>
>
> True.  But that also goes to my point that you need to specify which
> extensions and what information would need to be resumed, you can't just
> say "any which apply", what if the server author doesn't think the way you
> do and one of these doesn't get resumed?

You may have missed this in a previous e-mail response of mine to  
another commenter: I now agree that this appears to be an unfortunate  
necessity.

I was trying to keep the draft as lean as possible.  But discussion of  
how this command affects current extensions - see, e.g., Section 4 in  
the draft MOVE extension - is necessary to avoid ambiguities.

> But without an actual proof that this is useful for some combination of
> client/server, why would we adopt yet another extension that no one will
> implement?

Whatever I come up with here will be implemented by me in the  
imapproxy server, whether standardized or not.  Timo has indicated an  
interest to explore the idea further for implementation in Dovecot  
since he may be implementing something like this internally for other  
reasons.

Obviously our project would implement client side. I would assume the  
other large PHP-based open source webmail options would be interested.

As opposed to some other hella complicated extensions (I'm looking at  
you CONVERT), this proposal only adds two commands (one is really a  
simple extension of LOGOUT) and a response code.  Thus, this should be  
something that could be added without having to go in and modify too  
much existing code (It was an explicit design decision to not add the  
RESUME information to the authentication command; this would save a  
round-trip, but add significant complexity and implementation concerns.)

> Have you considered not re-establishing a connection every 10s?  This is a
> connected protocol, not http.

 From a webmail perspective: if you could tell me how to maintain a  
consistent IMAP connection using nothing more than current IMAP  
commands and an out-of-the box HTTP server, I would be ecstatic.   
That's what users demand our software works with, so that's what we  
need to code for.

I don't have the UI information in front of me right now, but a user  
initiating an action every 10 seconds, at least when managing messages  
in a mailbox (loading a message to read, deleting, copying/moving,  
reporting as spam), seems like a reasonable estimate for discussion  
purposes.  That's where I got the 10 second value from.

Thanks again for the additional input and review.

michael


