Libpq support to connect to standby server as priority
Hi Hackers,
This is a proposal that let libpq support 'prefer-read' option in
target_session_attrs in pg_conn. The 'prefer-read' means the libpq will try
to connect to a 'read-only' server firstly from the multiple server
addresses. If failed to connect to the 'read-only' server then it will try
to connect to the 'read-write' server.
By providing this feature the application can have the opportunity to
connect to the standby server firstly if failed then connect to master
server without caring the sequence of the server addresses provided to the
libpq.
The 'read-only' server means Standby Server
The 'read-write' server means Master Server support to 'read-write'
--
Regards,
Jing Wang
Fujitsu Australia
From: Jing Wang [mailto:jingwangian@gmail.com]
This is a proposal that let libpq support 'prefer-read' option in
target_session_attrs in pg_conn. The 'prefer-read' means the libpq will
try to connect to a 'read-only' server firstly from the multiple server
addresses. If failed to connect to the 'read-only' server then it will try
to connect to the 'read-write' server.
There's a pending patch I started. I'd be happy if you could continue this.
https://commitfest.postgresql.org/15/1148/
Regards
Takayuki Tsunakawa
Hi,
Enclosed please find the patch that the libpq support 'prefer-read'
feature.
If the *target_session_attrs* is set to 'prefer-read', the patch will
connect to server and send 'SHOW transaction_read_only' query to check the
server being 'read-only' or not. If server is 'read-write' then it will try
next server address. If all connections for 'read-only' get failed it will
try to connect to the master server.
--
Regards,
Jing Wang
Fujitsu Australia
Attachments:
libpq_support_perfer-read_001.patchapplication/octet-stream; name=libpq_support_perfer-read_001.patchDownload
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8d54333..6d37428 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -327,7 +327,7 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
{"target_session_attrs", "PGTARGETSESSIONATTRS",
DefaultTargetSessionAttrs, NULL,
- "Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
+ "Target-Session-Attrs", "", 12, /* sizeof("prefer-read") = 12 */
offsetof(struct pg_conn, target_session_attrs)},
/* Terminating entry --- MUST BE LAST */
@@ -1184,7 +1184,8 @@ connectOptions2(PGconn *conn)
if (conn->target_session_attrs)
{
if (strcmp(conn->target_session_attrs, "any") != 0
- && strcmp(conn->target_session_attrs, "read-write") != 0)
+ && strcmp(conn->target_session_attrs, "read-write") != 0
+ && strcmp(conn->target_session_attrs, "prefer-read") != 0)
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
@@ -2086,8 +2087,22 @@ keep_going: /* We will come back to here until there is
{
if (++conn->whichhost >= conn->nconnhost)
{
- conn->whichhost = 0;
- break;
+ if (conn->primary_host_index > 0 &&
+ strcmp(conn->target_session_attrs,"prefer-read") ==0 )
+ {
+ /*
+ * Go to here means failed to connect to read-only servers
+ * and now try connect to read-write server again.
+ * Only under the 'prefer-read' scenario will go to here.
+ */
+ conn->addr_cur = conn->connhost[conn->primary_host_index].addrlist;
+ conn->whichhost = conn->primary_host_index;
+ }
+ else
+ {
+ conn->whichhost = 0;
+ break;
+ }
}
conn->addr_cur =
conn->connhost[conn->whichhost].addrlist;
@@ -2341,6 +2356,14 @@ keep_going: /* We will come back to here until there is
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
+ else if (conn->primary_host_index >= 0 &&
+ strcmp(conn->target_session_attrs, "prefer-read") == 0)
+ {
+ conn->addr_cur = conn->connhost[conn->primary_host_index].addrlist;
+ conn->whichhost = conn->primary_host_index;
+ conn->status = CONNECTION_NEEDED;
+ goto keep_going;
+ }
goto error_return;
}
@@ -2978,10 +3001,12 @@ keep_going: /* We will come back to here until there is
}
/*
- * If a read-write connection is required, see if we have one.
+ * If a read-write or prefer-read connection is required,
+ * see if we have one.
*/
if (conn->target_session_attrs != NULL &&
- strcmp(conn->target_session_attrs, "read-write") == 0)
+ (strcmp(conn->target_session_attrs, "read-write") == 0 ||
+ strcmp(conn->target_session_attrs, "prefer-read") == 0))
{
/*
* We are yet to make a connection. Save all existing
@@ -3042,10 +3067,12 @@ keep_going: /* We will come back to here until there is
}
/*
- * If a read-write connection is requested check for same.
+ * If a read-write or prefer-read connection is requested
+ * check for same.
*/
if (conn->target_session_attrs != NULL &&
- strcmp(conn->target_session_attrs, "read-write") == 0)
+ (strcmp(conn->target_session_attrs, "read-write") == 0 ||
+ strcmp(conn->target_session_attrs, "prefer-read") == 0))
{
if (!saveErrorMessage(conn, &savedMessage))
goto error_return;
@@ -3124,57 +3151,130 @@ keep_going: /* We will come back to here until there is
PQntuples(res) == 1)
{
char *val;
+ bool readonly_server = false;
val = PQgetvalue(res, 0, 0);
if (strncmp(val, "on", 2) == 0)
+ readonly_server = true;
+
+ if (strcmp(conn->target_session_attrs, "read-write") == 0)
{
- const char *displayed_host;
- const char *displayed_port;
+ if(readonly_server)
+ {
+ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+ displayed_host = conn->connhost[conn->whichhost].hostaddr;
+ else
+ displayed_host = conn->connhost[conn->whichhost].host;
+ displayed_port = conn->connhost[conn->whichhost].port;
+ if (displayed_port == NULL || displayed_port[0] == '\0')
+ displayed_port = DEF_PGPORT_STR;
- if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
- displayed_host = conn->connhost[conn->whichhost].hostaddr;
- else
- displayed_host = conn->connhost[conn->whichhost].host;
- displayed_port = conn->connhost[conn->whichhost].port;
- if (displayed_port == NULL || displayed_port[0] == '\0')
- displayed_port = DEF_PGPORT_STR;
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
- PQclear(res);
- restoreErrorMessage(conn, &savedMessage);
+ /* Not writable; close connection. */
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not make a writable "
+ "connection to server "
+ "\"%s:%s\"\n"),
+ displayed_host, displayed_port);
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+ pqDropConnection(conn, true);
- /* Not writable; close connection. */
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not make a writable "
- "connection to server "
- "\"%s:%s\"\n"),
- displayed_host, displayed_port);
- conn->status = CONNECTION_OK;
- sendTerminateConn(conn);
- pqDropConnection(conn, true);
+ /* Skip any remaining addresses for this host. */
+ conn->addr_cur = NULL;
+ if (conn->whichhost + 1 < conn->nconnhost)
+ {
+ conn->status = CONNECTION_NEEDED;
+ goto keep_going;
+ }
- /* Skip any remaining addresses for this host. */
- conn->addr_cur = NULL;
- if (conn->whichhost + 1 < conn->nconnhost)
+ /* No more addresses to try. So we fail. */
+ goto error_return;
+ }
+ else /* server support read-write */
{
- conn->status = CONNECTION_NEEDED;
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
+
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
+
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
goto keep_going;
}
-
- /* No more addresses to try. So we fail. */
- goto error_return;
}
- PQclear(res);
- termPQExpBuffer(&savedMessage);
+ else /* conn->target_session_attrs is prefer-read */
+ {
+ if(readonly_server)
+ {
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
- /* We can release the address lists now. */
- release_all_addrinfo(conn);
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
- /*
- * Finish reading any remaining messages before being
- * considered as ready.
- */
- conn->status = CONNECTION_CONSUME;
- goto keep_going;
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+ else /* server support read-write */
+ {
+ if ((conn->primary_host_index < 0) && (conn->whichhost + 1 < conn->nconnhost))
+ {
+ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+ displayed_host = conn->connhost[conn->whichhost].hostaddr;
+ else
+ displayed_host = conn->connhost[conn->whichhost].host;
+ displayed_port = conn->connhost[conn->whichhost].port;
+ if (displayed_port == NULL || displayed_port[0] == '\0')
+ displayed_port = DEF_PGPORT_STR;
+
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /*
+ * Connecting to a writable server, close it
+ * and try to connect to another one.
+ */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+ pqDropConnection(conn, true);
+
+ /* Skip any remaining addresses for this host. */
+ conn->addr_cur = NULL;
+
+ conn->status = CONNECTION_NEEDED;
+
+ /* Record primary host index */
+ conn->primary_host_index = conn->whichhost;
+ goto keep_going;
+ }
+ else /* No more host to connect, keep this connection */
+ {
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
+
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
+
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+ }
+ }
}
/*
@@ -3393,6 +3493,8 @@ makeEmptyPGconn(void)
conn = NULL;
}
+ conn->primary_host_index = -1;
+
return conn;
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 516039e..3241246 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -362,7 +362,7 @@ struct pg_conn
char *krbsrvname; /* Kerberos service name */
#endif
- /* Type of connection to make. Possible values: any, read-write. */
+ /* Type of connection to make. Possible values: any, read-write, perfer-read. */
char *target_session_attrs;
/* Optional file to write trace info to */
@@ -396,6 +396,7 @@ struct pg_conn
int nconnhost; /* # of possible hosts */
int whichhost; /* host we're currently considering */
pg_conn_host *connhost; /* details about each possible host */
+ int primary_host_index; /* index for primary host in connhost */
/* Connection data */
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
Hi Takayuki,
Thanks your reminder.
I will have a look your patch and the review comments and update the patch
soon.
Please leave my current submitted patch now.
--
Regards,
Jing Wang
Fujitsu Australia
2018-01-04 17:40 GMT+11:00 Tsunakawa, Takayuki <
tsunakawa.takay@jp.fujitsu.com>:
From: Jing Wang [mailto:jingwangian@gmail.com]
This is a proposal that let libpq support 'prefer-read' option in
target_session_attrs in pg_conn. The 'prefer-read' means the libpq will
try to connect to a 'read-only' server firstly from the multiple server
addresses. If failed to connect to the 'read-only' server then it willtry
to connect to the 'read-write' server.
There's a pending patch I started. I'd be happy if you could continue
this.https://commitfest.postgresql.org/15/1148/
Regards
Takayuki Tsunakawa
--
Kind regards
Jing