mirror of
http://github.com/valkey-io/valkey
synced 2024-11-22 00:52:38 +00:00
Improvements for TLS with I/O threads (#1271)
Main thread profiling revealed significant overhead in TLS operations, even with read/write offloaded to I/O threads: Perf results: **10.82%** 8.82% `valkey-server libssl.so.3 [.] SSL_pending` # Called by main thread after I/O completion **10.16%** 5.06% `valkey-server libcrypto.so.3 [.] ERR_clear_error` # Called for every event regardless of thread handling This commit further optimizes TLS operations by moving more work from the main thread to I/O threads: Improve TLS offloading to I/O threads with two main changes: 1. Move `ERR_clear_error()` calls closer to SSL operations - Currently, error queue is cleared for every TLS event - Now only clear before actual SSL function calls - This prevents unnecessary clearing in main thread when operations are handled by I/O threads 2. Optimize `SSL_pending()` checks - Add `TLS_CONN_FLAG_HAS_PENDING` flag to track pending data - Move pending check to follow read operations immediately - I/O thread sets flag when pending data exists - Main thread uses flag to update pending list Performance improvements: Testing setup based on https://valkey.io/blog/unlock-one-million-rps-part2/ Before: - SET: 896,047 ops/sec - GET: 875,794 ops/sec After: - SET: 985,784 ops/sec (+10% improvement) - GET: 1,066,171 ops/sec (+22% improvement) Signed-off-by: Uri Yagelnik <uriy@amazon.com>
This commit is contained in:
parent
aa2dd3ecb8
commit
94113fde7f
25
src/tls.c
25
src/tls.c
@ -446,6 +446,7 @@ typedef enum {
|
|||||||
#define TLS_CONN_FLAG_WRITE_WANT_READ (1 << 1)
|
#define TLS_CONN_FLAG_WRITE_WANT_READ (1 << 1)
|
||||||
#define TLS_CONN_FLAG_FD_SET (1 << 2)
|
#define TLS_CONN_FLAG_FD_SET (1 << 2)
|
||||||
#define TLS_CONN_FLAG_POSTPONE_UPDATE_STATE (1 << 3)
|
#define TLS_CONN_FLAG_POSTPONE_UPDATE_STATE (1 << 3)
|
||||||
|
#define TLS_CONN_FLAG_HAS_PENDING (1 << 4)
|
||||||
|
|
||||||
typedef struct tls_connection {
|
typedef struct tls_connection {
|
||||||
connection c;
|
connection c;
|
||||||
@ -614,7 +615,7 @@ static void updatePendingData(tls_connection *conn) {
|
|||||||
|
|
||||||
/* If SSL has pending data, already read from the socket, we're at risk of not calling the read handler again, make
|
/* If SSL has pending data, already read from the socket, we're at risk of not calling the read handler again, make
|
||||||
* sure to add it to a list of pending connection that should be handled anyway. */
|
* sure to add it to a list of pending connection that should be handled anyway. */
|
||||||
if (SSL_pending(conn->ssl) > 0) {
|
if (conn->flags & TLS_CONN_FLAG_HAS_PENDING) {
|
||||||
if (!conn->pending_list_node) {
|
if (!conn->pending_list_node) {
|
||||||
listAddNodeTail(pending_list, conn);
|
listAddNodeTail(pending_list, conn);
|
||||||
conn->pending_list_node = listLast(pending_list);
|
conn->pending_list_node = listLast(pending_list);
|
||||||
@ -625,6 +626,14 @@ static void updatePendingData(tls_connection *conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateSSLPendingFlag(tls_connection *conn) {
|
||||||
|
if (SSL_pending(conn->ssl) > 0) {
|
||||||
|
conn->flags |= TLS_CONN_FLAG_HAS_PENDING;
|
||||||
|
} else {
|
||||||
|
conn->flags &= ~TLS_CONN_FLAG_HAS_PENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void updateSSLEvent(tls_connection *conn) {
|
static void updateSSLEvent(tls_connection *conn) {
|
||||||
if (conn->flags & TLS_CONN_FLAG_POSTPONE_UPDATE_STATE) return;
|
if (conn->flags & TLS_CONN_FLAG_POSTPONE_UPDATE_STATE) return;
|
||||||
|
|
||||||
@ -653,8 +662,6 @@ static void tlsHandleEvent(tls_connection *conn, int mask) {
|
|||||||
TLSCONN_DEBUG("tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d", fd, conn->c.state, mask,
|
TLSCONN_DEBUG("tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d", fd, conn->c.state, mask,
|
||||||
conn->c.read_handler != NULL, conn->c.write_handler != NULL, conn->flags);
|
conn->c.read_handler != NULL, conn->c.write_handler != NULL, conn->flags);
|
||||||
|
|
||||||
ERR_clear_error();
|
|
||||||
|
|
||||||
switch (conn->c.state) {
|
switch (conn->c.state) {
|
||||||
case CONN_STATE_CONNECTING:
|
case CONN_STATE_CONNECTING:
|
||||||
conn_error = anetGetError(conn->c.fd);
|
conn_error = anetGetError(conn->c.fd);
|
||||||
@ -662,6 +669,7 @@ static void tlsHandleEvent(tls_connection *conn, int mask) {
|
|||||||
conn->c.last_errno = conn_error;
|
conn->c.last_errno = conn_error;
|
||||||
conn->c.state = CONN_STATE_ERROR;
|
conn->c.state = CONN_STATE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
|
ERR_clear_error();
|
||||||
if (!(conn->flags & TLS_CONN_FLAG_FD_SET)) {
|
if (!(conn->flags & TLS_CONN_FLAG_FD_SET)) {
|
||||||
SSL_set_fd(conn->ssl, conn->c.fd);
|
SSL_set_fd(conn->ssl, conn->c.fd);
|
||||||
conn->flags |= TLS_CONN_FLAG_FD_SET;
|
conn->flags |= TLS_CONN_FLAG_FD_SET;
|
||||||
@ -690,6 +698,7 @@ static void tlsHandleEvent(tls_connection *conn, int mask) {
|
|||||||
conn->c.conn_handler = NULL;
|
conn->c.conn_handler = NULL;
|
||||||
break;
|
break;
|
||||||
case CONN_STATE_ACCEPTING:
|
case CONN_STATE_ACCEPTING:
|
||||||
|
ERR_clear_error();
|
||||||
ret = SSL_accept(conn->ssl);
|
ret = SSL_accept(conn->ssl);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
WantIOType want = 0;
|
WantIOType want = 0;
|
||||||
@ -747,10 +756,7 @@ static void tlsHandleEvent(tls_connection *conn, int mask) {
|
|||||||
conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;
|
conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;
|
||||||
if (!callHandler((connection *)conn, conn->c.read_handler)) return;
|
if (!callHandler((connection *)conn, conn->c.read_handler)) return;
|
||||||
}
|
}
|
||||||
|
updatePendingData(conn);
|
||||||
if (mask & AE_READABLE) {
|
|
||||||
updatePendingData(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -941,6 +947,7 @@ static int connTLSRead(connection *conn_, void *buf, size_t buf_len) {
|
|||||||
if (conn->c.state != CONN_STATE_CONNECTED) return -1;
|
if (conn->c.state != CONN_STATE_CONNECTED) return -1;
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
ret = SSL_read(conn->ssl, buf, buf_len);
|
ret = SSL_read(conn->ssl, buf, buf_len);
|
||||||
|
updateSSLPendingFlag(conn);
|
||||||
return updateStateAfterSSLIO(conn, ret, 1);
|
return updateStateAfterSSLIO(conn, ret, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -992,7 +999,7 @@ static int connTLSBlockingConnect(connection *conn_, const char *addr, int port,
|
|||||||
* which means the specified timeout will not be enforced accurately. */
|
* which means the specified timeout will not be enforced accurately. */
|
||||||
SSL_set_fd(conn->ssl, conn->c.fd);
|
SSL_set_fd(conn->ssl, conn->c.fd);
|
||||||
setBlockingTimeout(conn, timeout);
|
setBlockingTimeout(conn, timeout);
|
||||||
|
ERR_clear_error();
|
||||||
if ((ret = SSL_connect(conn->ssl)) <= 0) {
|
if ((ret = SSL_connect(conn->ssl)) <= 0) {
|
||||||
conn->c.state = CONN_STATE_ERROR;
|
conn->c.state = CONN_STATE_ERROR;
|
||||||
return C_ERR;
|
return C_ERR;
|
||||||
@ -1023,6 +1030,7 @@ static ssize_t connTLSSyncRead(connection *conn_, char *ptr, ssize_t size, long
|
|||||||
setBlockingTimeout(conn, timeout);
|
setBlockingTimeout(conn, timeout);
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
int ret = SSL_read(conn->ssl, ptr, size);
|
int ret = SSL_read(conn->ssl, ptr, size);
|
||||||
|
updateSSLPendingFlag(conn);
|
||||||
ret = updateStateAfterSSLIO(conn, ret, 0);
|
ret = updateStateAfterSSLIO(conn, ret, 0);
|
||||||
unsetBlockingTimeout(conn);
|
unsetBlockingTimeout(conn);
|
||||||
|
|
||||||
@ -1041,6 +1049,7 @@ static ssize_t connTLSSyncReadLine(connection *conn_, char *ptr, ssize_t size, l
|
|||||||
|
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
int ret = SSL_read(conn->ssl, &c, 1);
|
int ret = SSL_read(conn->ssl, &c, 1);
|
||||||
|
updateSSLPendingFlag(conn);
|
||||||
ret = updateStateAfterSSLIO(conn, ret, 0);
|
ret = updateStateAfterSSLIO(conn, ret, 0);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
nread = -1;
|
nread = -1;
|
||||||
|
Loading…
Reference in New Issue
Block a user