This feature allows to specify the behavior when a write query issued.
Except Japanese documents, all done.
works with Master Slave mode (<xref linkend="runtime-config-master-slave-mode">)
and Replication mode (<xref linkend="runtime-config-replication-mode">). When enabled
<productname>Pgpool-II</productname> sends the writing queries to the
- <acronym>primay node</acronym> in Master Slave mode, all of the backend nodes
- in Replication mode, and other queries get load balanced among all backend nodes.
- To which node
- the load balancing mechanism sends read queries is decided at the
- session start time and will not be changed until the session
- ends. The only exception is by writing special SQL comments. See
- below for more details.
+ <acronym>primay node</acronym> in Master Slave mode, all of the
+ backend nodes in Replication mode, and other queries get load
+ balanced among all backend nodes. To which node the load
+ balancing mechanism sends read queries is decided at the session
+ start time and will not be changed until the session ends. However
+ there are some exceptions. See below for more details.
</para>
<note>
<para>
</sect2>
+ <sect2 id="runtime-config-writing-queries-may-affect-load-balancing">
+
+ <title>Writing queries may affect Load Balancing</title>
+ <para>
+ In general, read queries are load balanced if certain conditions
+ are met. However, writing queries may affect the load
+ balancing. Here "writing queries" mean all the queries except
+ below:
+ </para>
+
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ SELECT/WITH without FOR UPDATE/SHARE
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ COPY TO STODUT
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ EXPLAIN
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ EXPLAIN ANALYZE and the query is SELECT not including writing functions
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ SHOW
+ </para>
+ </listitem>
+
+ </itemizedlist>
+ </para>
+
+ <para>
+ If writing queires appear, succeeding read queries may not be
+ load balanced. i.e. sent to primary node (in streaming
+ replication mode) or master node (in other mode) depending on the
+ setting of <xref linkend="guc-disable-load-balance-on-write">.
+ </para>
+ </sect2>
+
<sect2 id="runtime-config-load-balancing-in-streaming-raplication">
<title>Load Balancing in Streaming Replication</title>
SELECT not listed above
</para>
</listitem>
+
<listitem>
<para>
- COPY TO
+ COPY TO STDOUT
</para>
</listitem>
+
+ <listitem>
+ <para>
+ EXPLAIN
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ EXPLAIN ANALYZE and the query is SELECT not including writing functions
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ SHOW
+ </para>
+ </listitem>
+
</itemizedlist>
</para>
</listitem>
</listitem>
</varlistentry>
+ <varlistentry id="guc-disable-load-balance-on-write" xreflabel="disable_load_balance_on_write">
+ <term><varname>disable_load_balance_on_write</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>disable_load_balance_on_write</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Specify load balance behavior after write queries appear.
+ This parameter is especially useful in streaming replication
+ mode. When write queries are sent to primary server, the
+ changes are applied to standby servers but there's a time
+ lag. So if a client read the same row right after the write
+ query, the client may not see the latest value of the
+ row. If that's the problem, clients should always read data
+ from the primary server. However this effectively disables
+ load balancing, which leads to lesser performance. This
+ parameter allows a fine tuning for the trade off between
+ not-clustering-aware applications compatibility and
+ performance.
+ </para>
+ <para>
+ If this parameter is set to <varname>off</varname>, read
+ queries are load balanced even if write queries appear. This
+ gives the best load balance performance but clients may see
+ older data. This is useful for an environment where
+ PostgreSQL parameter synchronous_commit = 'remote_apply', or
+ in the native replication mode, since there's no replication
+ delay in such environments.
+ </para>
+ <para>
+ If this parameter is set to <varname>transaction</varname>
+ and write queries appear in an explicit transaction,
+ subsequent read queries are not load balanced until the
+ transaction ends. Please note that read queries not in an
+ explicit transaction are not affected by the parameter. This
+ setting gives the best balance in most cases and you should
+ start from this. This is the default and same behavior in
+ <productname>Pgpool-II 3.7</productname> or before.
+ </para>
+ <para>
+ If this parameter is set
+ to <varname>trans_transaction</varname> and write queries
+ appear in an explicit transaction, subsequent read queries
+ are not load balanced in the transaction and subsequent
+ explicit transaction until the session ends. So this
+ parameter is safer for older applications but give lesser
+ performance than <varname>transaction</varname>. Please note
+ that read queries not in an explicit transaction are not
+ affected by the parameter.
+ </para>
+
+ <para>
+ If this parameter is set to <varname>always</varname> and
+ write queries appear, subsequent read queries are not load
+ balanced until the session ends regardless they are in
+ explicit transactions or not. This gives the highest
+ compatibility with not-clustering-aware applications and the
+ lowest performance.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
</sect1>
{
pool_setall_node_to_be_sent(query_context);
}
+ else if (pool_is_writing_transaction() &&
+ pool_config->disable_load_balance_on_write == DLBOW_ALWAYS)
+ {
+ pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
+ }
/*
* Ok, we might be able to load balance the SELECT query.
/* -*-pgsql-c-*- */
/*
- *
- * $Header$
*
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
*
- * Copyright (c) 2003-2017 PgPool Global Development Group
+ * Copyright (c) 2003-2018 PgPool Global Development Group
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
*/
void pool_unset_writing_transaction(void)
{
- ereport(DEBUG1,
- (errmsg("session context: clearing writing transaction. DONE")));
-
- pool_get_session_context(false)->writing_transaction = false;
+ /*
+ * If disable_transaction_on_write is 'always', then never turn off
+ * writing transaction flag.
+ */
+ if (pool_config->disable_load_balance_on_write != DLBOW_ALWAYS)
+ {
+ pool_get_session_context(false)->writing_transaction = false;
+ ereport(DEBUG1,
+ (errmsg("session context: clearing writing transaction. DONE")));
+ }
}
/*
*/
void pool_set_writing_transaction(void)
{
- ereport(DEBUG1,
+ /*
+ * If disable_transaction_on_write is 'off', then never turn on writing
+ * transaction flag.
+ */
+ if (pool_config->disable_load_balance_on_write != DLBOW_OFF)
+ {
+ pool_get_session_context(false)->writing_transaction = true;
+ ereport(DEBUG1,
(errmsg("session context: setting writing transaction. DONE")));
- pool_get_session_context(false)->writing_transaction = true;
+ }
}
/*
* explicit transaction, remember that we had a write
* query in this transaction.
*/
- if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) == 'T')
+ if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) == 'T' ||
+ pool_config->disable_load_balance_on_write == DLBOW_ALWAYS)
{
pool_set_writing_transaction();
}
else if (!is_select_query(node, query))
{
/*
- * If the query was not READ SELECT, and we are in an
- * explicit transaction, remember that we had a write
- * query in this transaction.
+ * If the query was not READ SELECT, and we are in an explicit
+ * transaction or disable_load_balance_on_write is 'ALWAYS', remember
+ * that we had a write query in this transaction.
*/
- if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) == 'T')
+ if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) == 'T' ||
+ pool_config->disable_load_balance_on_write == DLBOW_ALWAYS)
{
/* However, if the query is "SET TRANSACTION READ ONLY" or its variant,
* don't set it.
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="INSERT INTO pgproto_test1 VALUES(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(INSERT 0 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 1")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="INSERT INTO pgproto_test1 VALUES(1)")
+<= BE CommandComplete(INSERT 0 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 2")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="SELECT 3")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 4")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 1")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="INSERT INTO pgproto_test1 VALUES(1)")
+<= BE CommandComplete(INSERT 0 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 2")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="SELECT 3")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 4")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="INSERT INTO pgproto_test1 VALUES(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(INSERT 0 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="INSERT INTO pgproto_test1 VALUES(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(INSERT 0 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 1")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="INSERT INTO pgproto_test1 VALUES(1)")
+<= BE CommandComplete(INSERT 0 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 2")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="SELECT 3")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 4")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 1")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="INSERT INTO pgproto_test1 VALUES(1)")
+<= BE CommandComplete(INSERT 0 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 2")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="SELECT 3")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Query(query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query(query="SELECT 4")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query(query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query(query="DROP TABLE IF EXISTS pgproto_test1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M table "pgproto_test1" does not exist, skipping F tablecmds.c L 914 R DropErrorMsgNonExistent )
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Query(query="CREATE TABLE pgproto_test1(i INT)")
+<= BE CommandComplete(CREATE TABLE)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="INSERT INTO pgproto_test1 VALUES(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(INSERT 0 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query(query="DROP TABLE pgproto_test1")
+<= BE CommandComplete(DROP TABLE)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 0 Execute: 0 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# SELECT: 0 SELECT: 0 SELECT: 0 SELECT: 0
+set 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: SELECT: 1 SELECT: 0 SELECT: 1 SELECT: 1
+set 1 0 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 1 Execute: 1 Parse: 1 Execute: 1 Parse: 1 Execute: 1
+set 1 0 0 0 0 1 1 1 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Parse: 1 Parse: 1 Execute: 1 Parse: 1 Execute: 1
+# Parse: 1 Execute: 1 Parse: 1 Execute: 1 Parse: 1 Execute: 1
+# SELECT: 1 SELECT: 1 SELECT: 1 SELECT: 1
+set 1 1 1 1 1 1 1 1 1 1 1 1 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: SELECT: 1 SELECT: 0 SELECT: 1 SELECT: 0
+set 1 0 1 0
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 1 Execute: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+set 1 0 0 0 0 1 1 0 0 0 0
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+# Test for disable_load_balance_on_write feature (always).
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in current transaction only.
+# (default behavior)
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Issue INSERT
+'P' "" "INSERT INTO pgproto_test1 VALUES(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to primay node.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node because
+# disable_load_balance_on_write = 'always.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+# Test for disable_load_balance_on_write = always feature
+# using simple queries.
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue Parse SELECT. This should be sent to node 0.
+'Q' "SELECT 1"
+'Y'
+
+# Issue INSERT
+'Q' "INSERT INTO pgproto_test1 VALUES(1)"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 2"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 3"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 4"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write feature
+# using simple queries.
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in current transaction only.
+# (default behavior)
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue Parse SELECT. This should be sent to node 1.
+'Q' "SELECT 1"
+'Y'
+
+# Issue INSERT
+'Q' "INSERT INTO pgproto_test1 VALUES(1)"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 2"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'Q' "SELECT 3"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 4"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write feature.
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in current transaction only.
+# (default behavior)
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Issue INSERT
+'P' "" "INSERT INTO pgproto_test1 VALUES(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to node 1 because
+# disable_load_balance_on_write = 'transaction'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to node 1.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write feature (off).
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in current transaction only.
+# (default behavior)
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Issue INSERT
+'P' "" "INSERT INTO pgproto_test1 VALUES(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to standby node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to standby node because
+# disable_load_balance_on_write = 'off'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+# Test for disable_load_balance_on_write = off feature
+# using simple queries.
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue Parse SELECT. This should be sent to node 1.
+'Q' "SELECT 1"
+'Y'
+
+# Issue INSERT
+'Q' "INSERT INTO pgproto_test1 VALUES(1)"
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'Q' "SELECT 2"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'Q' "SELECT 3"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'Q' "SELECT 4"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write feature
+# using simple queries.
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in next transaction.
+##disable_load_balance_on_write = 'trans_transaction'
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue Parse SELECT. This should be sent to node 1.
+'Q' "SELECT 1"
+'Y'
+
+# Issue INSERT
+'Q' "INSERT INTO pgproto_test1 VALUES(1)"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 2"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'Q' "SELECT 3"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 4"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write feature.
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in next transaction.
+##disable_load_balance_on_write = 'trans_transaction'
+
+# Create test table
+'Q' "DROP TABLE IF EXISTS pgproto_test1"
+'Y'
+'Q' "CREATE TABLE pgproto_test1(i INT)"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Issue INSERT
+'P' "" "INSERT INTO pgproto_test1 VALUES(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node because
+# disable_load_balance_on_write = 'trans_transaction'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+# DROP test table
+'Q' "DROP TABLE pgproto_test1"
+'Y'
+
+'X'