Projects tigase _server server-core Issues #9
Change the TLS buffer to a very small size (#9)
Artur Hefczyc opened 1 decade ago
Due Date
2016-04-16

Currently TLS buffers are resized down to the default TLS buffer capacity. However, for clients connections, which are idle for most of the time this leads to wasting lots of memory in buffers which are not used anyway. Resizing them down to even unusable size might be a better approach, as in XMPP c2s connection is not used for most of the time, so it does not really matter if we sometimes have to resize them back to a bigger size when data comes.

Artur Hefczyc commented 1 decade ago

Partially done, some improvements are still possible though.

Andrzej Wójcik (Tigase) commented 9 years ago

I reviewed current implementation and I think that current size of input buffer (which reads data from socket and passes to parser or TLS engine for unwrapping) which is set to 2k and may be increased/decreased as needed is OK and should not be changed as smaller size may require frequent resizes which may create additional work for garbage collector.

I also checked buffer used by TLSIO class which is kept in tlsInput field and I decided to modify this buffer as by default in typical use case (even for idle client connection) it uses 16k of memory for every connection - far more that buffer used for reading data from socket!. Moreover, it could be resized if needed but it was always resized to bigger size and it's size was never reduced - due to that I was able to allocate over 32k in this buffer for single connection by sending VCard to server.

I changed way in which this buffer is resized, so it will be increased if needed but if all data will be read from connection (no data waiting) then it will be resized down to default value.

I also changed default size for this buffer. Now it will be equal to minimal from network socket buffer (2k for client connections and 64k for HT connections) and getApplicationBufferSize() of SSLSession (which is highest possible size of single TLS packet - usually 16k). This way this buffer should keep it's size big enough for HT connections and properly reduce for ST connections (ie. client connections).

Due to above changes we should reduce memory usage by tlsInput buffer from 16k to 2k for typical XMPP client with TLS/SSL and possible we can get even bigger reduction if client sent to server big packets as this could increate tlsInput buffer which was never resized to smaller value.

Artur Hefczyc commented 9 years ago

Thank you for looking into it.

I was aware that the current data buffer size is 2k and default TLS/SSL buffer size is 16k. In fact the standard TLS/SSL buffer size across different systems is 16k and sometimes is bigger. Normally it is not even possible to correctly read and decipher TLS/SSL data form a stream with smaller than 16k buffers.

However, normally for the XMPP systems a connection is idle for most of the time, therefore, 16k for a buffer which is not used looks like a waste of memory. Especially if we have a system with 200k+ connections, most of them idle most of the time and only occasionally sending some data. It's like over 3GB of RAM wasted. And for large chunks of data the TLS buffer size easily gets much larger.

So my intention for the task was to do the following:

  1. Allow for the TLS/SSL buffer to be of whatever required minimal size is (let's say 16k) when there is some transmission. But shrink it down to something like 2k when the connection is idle

  2. Make sure the data input buffer is also not larger than 2k when the connection is idle

From your description it looks like you made it work exactly like this. Please confirm if this is the case and close the ticket.

Andrzej Wójcik (Tigase) commented 9 years ago

Normally it is not even possible to correctly read and decipher TLS/SSL data form a stream with smaller than 16k buffers.

I tried with Psi+ and Tigase XMPP Server with buffer set to 2k and data was correctly read and deciphered by TLS using this small buffer - needed to resize it only if there were more data in single TCP/TLS frame. So I think it is possible.

Yes, I did what you intended to be done in this task.

Now If connection is idle TLS buffer and input buffer will be resized to 2k, and resized to bigger size only when needed.

However before I close this task I wonder if we should not change algorithm responsible for resizing buffers to bigger size - as now size is increased by 2k every time.

I think that now, when we reduce size as well we could (and should) change it to use new algorithm by increasing buffer size by half of existing buffer size. It could use more memory than needed but in fact it could allocate less memory in process.

Current algorithm:

while (isTooSmall(buf)) {
   buf = ByteBuffer.allocate(buf.capacity() + 2048);
}

Suggested new algorithm:

while (isTooSmall(buf)) {
   buf = ByteBuffer.allocate(buf.capacity() * 1.5);
}

In case of required buffer of size 31k (during VCard publication I got similar size) we would have following results:

IterationBuffer size (old size/old algorithm)Buffer size (new size/old algorithm)Buffer size (new size/new algorithm)
11622
21843
32064.5
42287.75
5241011.625
6261217.44
7281426.16
8301639.24
93218-
10-20-
11-22-
12-24-
13-26-
14-28-
15-30-
16-32-
Total allocated memory216272111.72
Used buffers9168
After increase but when idle3222

From that you can see that use of ratio based resizing of input buffer than constant based could reduce number of memory allocations and could reduce number of objects for GC, but may result in temporary increase of memory usage as buffer size should be reduced just after connection will become idle.

I think that we should use ratio based resizing with ratio set to 1.5 or maybe bigger, however bigger ration will be responsible for faster increase of size of buffer and may lead to wasting memory in this buffer as not whole buffer will be used (but only for a short time).

%kobit What do you think?

Artur Hefczyc commented 9 years ago

Andrzej Wójcik wrote:

I think that we should use ratio based resizing with ratio set to 1.5 or maybe bigger, however bigger ration will be responsible for faster increase of size of buffer and may lead to wasting memory in this buffer as not whole buffer will be used (but only for a short time).

%kobit What do you think?

I think this is a very good idea. I definitely like using the new algorithm. However, I think a better ration would be 2 instead of 1.5. In your example above it would allocate 32kB faster and would waste less memory. So higher ratio does not always mean more wasted memory. Moreover, more iterations to allocate buffer to required size mean actually more wasted memory because there is more objects for GC to deal with. So I think bigger ratio gives us more benefits than disadvantages.

It kind of surprises me what you say about 2k buffer being enough for TLS. I believe you of course, I just remember that I could not get below 16kB which appeared to be minimal possible and also all TLS buffer size increases were multiplication of 16. So, if 16kB was too small, I had to resize it to 32kB, then to 48kB, and so on... Maybe it also depends what kind of software works on the other side of the TLS stream.... Also my tests were run quite a few years ago, different OS, different Java version.

Andrzej Wójcik (Tigase) commented 9 years ago

%kobit Ok, will use 2 as ratio for resizing buffers - for that I created separate task #4111

As for TLS required buffer size it depends on implementations of TLS on both sides, stream flushing, used ciphers and used TLS protocol - now we have TLSv1.1 and TLSv1.2, while not so long ago there was only TLSv1.0 and SSLv3. However even if it would occur that it needs to be resized for some reason every time this new solution will handle this and work fine.

issue 1 of 1
Type
Task
Priority
Minor
Assignee
RedmineID
14
Version
tigase-server-8.0.0
Estimation
16h
Spent time
33h
Issue Votes (0)
Watchers (0)
Reference
tigase/_server/server-core#9
Please wait...
Page is in error, reload to recover