Client-Side Optimization

Simplified Acknowledgment of Message Delivery

An outgoing message may be considered sent once the server has assigned it an identifier. Normally, a client would learn of this from the result of the messages.sendMessage method. The MTProto server provides a mechanism for “quick acknowledgments". Upon receiving such an acknowledgment, the client may be certain that the call to the send message method has at least been fully received by the server and placed in a processing queue, and can inform the user that the delivery was successful. It is possible that the server's actual response will never be received by the client (an interrupted connection; or the app restarts at exactly the wrong time). To correctly handle these situations, you can use a special type of notification generated by the server when updates.getDifference is called: updateMessageID. When processing this notification, the client can use the random_id identifier to associate the previously transmitted message with the one delivered to the server. If such a notification is not issued when updates.getDifference is called for one of the previously sent messages, the message must be marked as undelivered.

Server Salt

Server salt is a 64-bit number added to every outgoing and incoming message. At present, a single salt's lifespan is 1 hour, following which it is considered invalid and the server will return an error for all the messages that contain it. The error message will contain the correct salt, which may be immediately used for sending. Given this approach, there will always be a period of waiting before the client receives a new salt if it connects to the server less frequently than once an hour. For improved performance, there is a special get_future_salts method, which fetches in advance a list of the salts that will be valid during the course of a specified period of time following the call (1 day, for example). A start time and an end time are specified for each salt. The salts overlap one another by half an hour. We recommend always using the record with the longest remaining lifespan.

Downloading Files and Uploading Data to the Server

We recommend that separate connections and sessions be created for these tasks. Remember that the extra sessions must be deleted when no longer needed. It makes sense to download files over several connections (optimally to have a pool). When uploading data to a server one connection is enough to achieve the best results.

The file handling API is designed to perform data operations in parts. In its simplest implementation, the process of uploading files to a server looks like this: send a query, wait for a response, send the next query, etc. This approach does not optimize the use of network resources and the ping time has a huge effect. The upload and download process is optimal when two or more queries are continuously being executed through one connection. In this arrangement, uploading to the server would look like this:

  1. Send Query 1
  2. Send Query 2
  3. Wait for a response to Query 1
  4. Send Query 3
  5. Wait for a response to Query 2
  6. Send Query 4
  7. etc.

This will help reduce the effect of ping latency and maximize the channel workload.

Sending Messages in Bulk

Sometimes a client needs to transmit several send message method calls to the server all at once in a single message or in several consecutive messages. However, the server may execute these requests out of order (queries are handled by different servers to improve performance, which introduces a degree of randomness to the process). This requires that dependencies be explicitly stated when processing queries by using the function

invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;

Actually, this means padding the beginning of the query with the 32-bit number 0xcb9f372d and the 64-bit message identifier of the query on which the current query is dependent.

Grouping Updates

Generating updates (notifications about various server events) and delivering them to the client form two different parts of the system (respectively, the messenger API and MTProto). By itself, MTProto cannot modify in any way the data transmitted to the client, and the server API cannot respond to client-MTProto connection events. Imagine the situation where a client loses its connection (or is intentionally disconnected from the network) for some time. If lots of different events occur before a new connection is established (contacts come online, typing event messages are sent), then when a connection is established the client will receive lots of data containing all of the intervening events, despite the fact that most of the data is obsolete. The grouping of messages has been introduced to optimize such situations. If new events occur and the client has not managed to “collect” the previously generated updates, then the server API can combine them into a single package.

A client is able to control when the MTProto server begins to consider that the connection has been lost and grouping can begin (the earlier this occurs when there is no connection, the better for the client). This functionality is implemented through a special type of Ping message, ping_delay_disconnect, which specifies a time delay following which the server will close the current connection and start grouping messages.

It makes sense to combine the transmission of ping_delay_disconnect with that of other recurring tasks, such as updating the user status (account.updateStatus).

Setting the Typing Status

If a contact is not online, there is no need to invoke messages.setTyping.