> On Oct 17, 2014, at 7:59 AM, [email protected] wrote:
>
> Repository: trafficserver
> Updated Branches:
> refs/heads/master 609443dbe -> dee36e716
>
>
> [TS-2503]: Dynamic TLS Record Sizing for better page load latencies.
> The strategy used is essentially to use small TLS records that fit into a
> single TCP segment for the first ~1 MB of data, increase record size to 16 KB
> after that to optimize throughput, and then reset record size back to a single
> segment after ~1 second of inactivity—lather, rinse, repeat.
>
>
> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/dee36e71
> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/dee36e71
> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/dee36e71
>
> Branch: refs/heads/master
> Commit: dee36e7163373c1d61bc6b97e529269dcde134c5
> Parents: 609443d
> Author: Sudheer Vinukonda <[email protected]>
> Authored: Fri Oct 17 14:57:15 2014 +0000
> Committer: Sudheer Vinukonda <[email protected]>
> Committed: Fri Oct 17 14:57:15 2014 +0000
>
> ----------------------------------------------------------------------
> .../configuration/records.config.en.rst | 7 ++++
> iocore/net/P_SSLNetVConnection.h | 13 ++++++
> iocore/net/P_SSLUtils.h | 2 +
> iocore/net/SSLNetVConnection.cc | 43 +++++++++++++++++++-
> iocore/net/SSLUtils.cc | 4 ++
> 5 files changed, 68 insertions(+), 1 deletion(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/dee36e71/doc/reference/configuration/records.config.en.rst
> ----------------------------------------------------------------------
> diff --git a/doc/reference/configuration/records.config.en.rst
> b/doc/reference/configuration/records.config.en.rst
> index 07244b7..ca30055 100644
> --- a/doc/reference/configuration/records.config.en.rst
> +++ b/doc/reference/configuration/records.config.en.rst
> @@ -2168,6 +2168,13 @@ SSL Termination
> buffering at the SSL layer. The default of ``0`` means to always
> write all available data into a single SSL record.
>
> + A value of ``-1`` means TLS record size is dynamically determined. The
> + strategy employed is to use small TLS records that fit into a single
> + TCP segment for the first ~1 MB of data, but, increase the record size to
> + 16 KB after that to optimize throughput. The record size is reset back to
> + a single segment after ~1 second of inactivity and the record size ramping
> + mechanism is repeated again.
> +
> .. ts:cv:: CONFIG proxy.config.ssl.session_cache INT 2
>
> Enables the SSL Session Cache:
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/dee36e71/iocore/net/P_SSLNetVConnection.h
> ----------------------------------------------------------------------
> diff --git a/iocore/net/P_SSLNetVConnection.h
> b/iocore/net/P_SSLNetVConnection.h
> index 51b7391..4a6d7c8 100644
> --- a/iocore/net/P_SSLNetVConnection.h
> +++ b/iocore/net/P_SSLNetVConnection.h
> @@ -52,6 +52,17 @@
> #define SSL_TLSEXT_ERR_NOACK 3
> #endif
>
> +// TS-2503: dynamic TLS record sizing
> +// For smaller records, we should also reserve space for various TCP options
> +// (timestamps, SACKs.. up to 40 bytes [1]), and account for TLS record
> overhead
> +// (another 20-60 bytes on average, depending on the negotiated ciphersuite
> [2]).
> +// All in all: 1500 - 40 (IP) - 20 (TCP) - 40 (TCP options) - TLS overhead
> (60-100)
> +// For larger records, the size is determined by TLS protocol record size
> +#define SSL_DEF_TLS_RECORD_SIZE 1300 // 1500 - 40 (IP) - 20
> (TCP) - 40 (TCP options) - TLS overhead (60-100)
> +#define SSL_MAX_TLS_RECORD_SIZE 16383 // 2^14 - 1
> +#define SSL_DEF_TLS_RECORD_BYTE_THRESHOLD 1000000
> +#define SSL_DEF_TLS_RECORD_MSEC_THRESHOLD 1000
> +
> class SSLNextProtocolSet;
> struct SSLCertLookup;
>
> @@ -105,6 +116,8 @@ public:
>
> SSL *ssl;
> ink_hrtime sslHandshakeBeginTime;
> + ink_hrtime sslLastWriteTime;
> + int64_t sslTotalBytesSent;
>
> static int advertise_next_protocol(SSL * ssl, const unsigned char ** out,
> unsigned * outlen, void *);
> static int select_next_protocol(SSL * ssl, const unsigned char ** out,
> unsigned char * outlen, const unsigned char * in, unsigned inlen, void *);
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/dee36e71/iocore/net/P_SSLUtils.h
> ----------------------------------------------------------------------
> diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
> index ec7b0b9..3b77e7d 100644
> --- a/iocore/net/P_SSLUtils.h
> +++ b/iocore/net/P_SSLUtils.h
> @@ -70,6 +70,8 @@ enum SSL_Stats
> ssl_total_tickets_verified_stat,
> ssl_total_tickets_not_found_stat,
> ssl_total_tickets_renewed_stat,
> + ssl_total_dyn_def_tls_record_count,
> + ssl_total_dyn_max_tls_record_count,
> ssl_session_cache_hit,
> ssl_session_cache_miss,
> ssl_session_cache_eviction,
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/dee36e71/iocore/net/SSLNetVConnection.cc
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
> index ba6f435..01d9eae 100644
> --- a/iocore/net/SSLNetVConnection.cc
> +++ b/iocore/net/SSLNetVConnection.cc
> @@ -617,12 +617,26 @@ SSLNetVConnection::load_buffer_and_write(int64_t
> towrite, int64_t &wattempted, i
> ProxyMutex *mutex = this_ethread()->mutex;
> int64_t r = 0;
> int64_t l = 0;
> + uint32_t dynamic_tls_record_size = 0;
> ssl_error_t err = SSL_ERROR_NONE;
>
> // XXX Rather than dealing with the block directly, we should use the
> IOBufferReader API.
> int64_t offset = buf.reader()->start_offset;
> IOBufferBlock *b = buf.reader()->block;
>
> + // Dynamic TLS record sizing
> + ink_hrtime now = 0;
> + if (SSLConfigParams::ssl_maxrecord == -1) {
> + now = ink_get_hrtime_internal();
> + int msec_since_last_write = ink_hrtime_to_msec(now - sslLastWriteTime);
ink_hrtime_diff_msec
> +
> + if (msec_since_last_write > SSL_DEF_TLS_RECORD_MSEC_THRESHOLD) {
> + // reset sslTotalBytesSent upon inactivity for
> SSL_DEF_TLS_RECORD_MSEC_THRESHOLD
> + sslTotalBytesSent = 0;
> + }
> + Debug("ssl", "SSLNetVConnection::loadBufferAndCallWrite, now %" PRId64
> ",lastwrite %" PRId64 " ,msec_since_last_write %d", now, sslLastWriteTime,
> msec_since_last_write);
> + }
> +
> if (HttpProxyPort::TRANSPORT_BLIND_TUNNEL == this->attributes) {
> return this->super::load_buffer_and_write(towrite, wattempted,
> total_wrote, buf, needs);
This is not your change, but s/total_wrote/total_written/ ;)
> }
> @@ -648,7 +662,25 @@ SSLNetVConnection::load_buffer_and_write(int64_t
> towrite, int64_t &wattempted, i
> // operations.
> int64_t orig_l = l;
> if (SSLConfigParams::ssl_maxrecord > 0 && l >
> SSLConfigParams::ssl_maxrecord) {
> - l = SSLConfigParams::ssl_maxrecord;
> + l = SSLConfigParams::ssl_maxrecord;
> +
> + // if ssl_maxrecord is configured higher than SSL_MAX_TLS_RECORD_SIZE
> + // round off the SSL_write at multiples of SSL_MAX_TLS_RECORD_SIZE to
> + // ensure openSSL can flush at TLS record boundary
> + if (l > SSL_MAX_TLS_RECORD_SIZE) {
> + l -= (l % SSL_MAX_TLS_RECORD_SIZE);
> + }
AFAICT, this is not necessary. The SSL transport will fragment at record
boundaries. This code just confuses the reader.
> + } else if (SSLConfigParams::ssl_maxrecord == -1) {
> + if (sslTotalBytesSent < SSL_DEF_TLS_RECORD_BYTE_THRESHOLD) {
> + dynamic_tls_record_size = SSL_DEF_TLS_RECORD_SIZE;
> + SSL_INCREMENT_DYN_STAT(ssl_total_dyn_def_tls_record_count);
> + } else {
> + dynamic_tls_record_size = SSL_MAX_TLS_RECORD_SIZE;
> + SSL_INCREMENT_DYN_STAT(ssl_total_dyn_max_tls_record_count);
> + }
> + if (l > dynamic_tls_record_size) {
> + l = dynamic_tls_record_size;
> + }
> }
>
> if (!l) {
> @@ -660,6 +692,7 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite,
> int64_t &wattempted, i
> Debug("ssl", "SSLNetVConnection::loadBufferAndCallWrite, before
> SSLWriteBuffer, l=%" PRId64", towrite=%" PRId64", b=%p",
> l, towrite, b);
> err = SSLWriteBuffer(ssl, b->start() + offset, l, r);
> +
> if (r == l) {
> wattempted = total_wrote;
> }
> @@ -676,6 +709,8 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite,
> int64_t &wattempted, i
> } while (r == l && total_wrote < towrite && b);
>
> if (r > 0) {
> + sslLastWriteTime = now;
> + sslTotalBytesSent += total_wrote;
> if (total_wrote != wattempted) {
> Debug("ssl", "SSLNetVConnection::loadBufferAndCallWrite, wrote some
> bytes, but not all requested.");
> // I'm not sure how this could happen. We should have tried and hit an
> EAGAIN.
> @@ -744,6 +779,8 @@ SSLNetVConnection::SSLNetVConnection():
> npnSet(NULL),
> npnEndpoint(NULL)
> {
> + sslLastWriteTime = 0;
> + sslTotalBytesSent = 0;
Why isn't this in the initializer list?
You also should reset these in SSLNetVConnection::free. I expect the handshake
stat is wrong too, since that is not reset either.
> }
>
> void
> @@ -936,6 +973,8 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
> }
>
> sslHandShakeComplete = true;
> + sslLastWriteTime = 0;
> + sslTotalBytesSent = 0;
This is weird, are you compensating for the missing code in free()?
>
> if (sslHandshakeBeginTime) {
> const ink_hrtime ssl_handshake_time = ink_get_hrtime() -
> sslHandshakeBeginTime;
> @@ -1058,6 +1097,8 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err)
> }
>
> sslHandShakeComplete = true;
> + sslLastWriteTime = 0;
> + sslTotalBytesSent = 0;
This too
> return EVENT_DONE;
>
> case SSL_ERROR_WANT_WRITE:
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/dee36e71/iocore/net/SSLUtils.cc
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
> index 27e0c19..286ba69 100644
> --- a/iocore/net/SSLUtils.cc
> +++ b/iocore/net/SSLUtils.cc
> @@ -1794,6 +1794,10 @@ SSLWriteBuffer(SSL * ssl, const void * buf, int64_t
> nbytes, int64_t& nwritten)
> int ret = SSL_write(ssl, buf, (int)nbytes);
> if (ret > 0) {
> nwritten = ret;
> + BIO *bio = SSL_get_wbio(ssl);
> + if (bio != NULL) {
> + BIO_flush(bio);
> + }
> return SSL_ERROR_NONE;
> }
>
>