Thread: dblink: could not send query: another command is already in progress

dblink: could not send query: another command is already in progress

From
Thiemo Kellner
Date:
Hi all

I try to use dblink to create a asynchronous logging facility. I have 
the following code

             -- open the dblink if it does not yet exist
             V_DBLINK_CONNECTION_NAME :=
               GET_PROPERTY_VALUE_STRING(
                   I_PROPERTY_NAME => 'DBLINK_CONNECTION_NAME'
               );
             select dblink_get_connections() into V_DBLINK_CONNECTION_NAMES;
             if (
                 V_DBLINK_CONNECTION_NAMES is null
              or V_DBLINK_CONNECTION_NAME != any(V_DBLINK_CONNECTION_NAMES)
             ) then
                 V_DBLINK_CONNECT_STRING :=
                   GET_PROPERTY_VALUE_STRING(
                       I_PROPERTY_NAME => 'DBLINK_CONNECT_STRING'
                   );
                 -- better to use dblink_connect_u with password file?
                 perform dblink_connect(
                     V_DBLINK_CONNECTION_NAME,
                     V_DBLINK_CONNECT_STRING
                 );
             end if;

             -- send query asynchronously
             -- Use literal (%L) as it returns the value null as the 
unquoted
             -- string NULL.
             V_QUERY := format(
                 $s$select true $s$ || C_LB ||
                 $s$  from %I( $s$ || C_LB ||
                 $s$           I_FUNCTION => %L, $s$ || C_LB ||
                 $s$           I_MESSAGE => %L, $s$ || C_LB ||
                 $s$           I_LEVEL => %L, $s$ || C_LB ||
                 $s$           I_PRESENT_USER => %L, $s$ || C_LB ||
                 $s$           I_SESSION_USER => %L, $s$ || C_LB ||
                 $s$           I_TRANSACTION_TIMESTAMP => $s$ ||
                 $s$             %L::timestamp, $s$ || C_LB ||
                 $s$           I_TRANSACTION_ID => $s$ ||
                 $s$             %L::bigint, $s$ || C_LB ||
                 $s$           I_SERVER_PID => $s$ ||
                 $s$             %L::bigint, $s$ || C_LB ||
                 $s$           I_REMOTE_ADDRESS => $s$ ||
                 $s$             %L::inet, $s$ || C_LB ||
                 $s$           I_REMOTE_PORT => $s$ ||
                 $s$             %L::bigint $s$ || C_LB ||
                 $s$       ); $s$ || C_LB ||
                 $s$commit $s$,
                 'WRITE_MESSAGE_TO_TABLE',
                 C_CALLER_FUNCTION,
                 I_MESSAGE,
                 I_LEVEL,
                 C_PRESENT_USER,
                 C_SESSION_USER,
                 C_TRANSACTION_TIMESTAMP,
                 C_TRANSACTION_ID,
                 C_SERVER_PID,
                 C_REMOTE_ADDRESS,
                 C_REMOTE_PORT
             );
             -- send query when connection is ready
             V_WAIT_FOR :=
               GET_PROPERTY_VALUE_INTERVAL(
                   I_PROPERTY_NAME => 'BUSY_WAIT_INTERVAL'
               ); -- to avoid continuous re-querying, already queried here
             -- surprisingly, dblink_is_busy does not return boolean, 
but 0 for
             -- false
             while dblink_is_busy(V_DBLINK_CONNECTION_NAME) != 0 loop
                 perform pg_sleep_for(V_WAIT_FOR);
             end loop;
             perform dblink_send_query(
                         V_DBLINK_CONNECTION_NAME,
                         V_QUERY
                     );
raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
raise notice 'Last error: %', 
dblink_error_message(V_DBLINK_CONNECTION_NAME);
raise notice 'Cancel query: %', 
dblink_cancel_query(V_DBLINK_CONNECTION_NAME);
             -- ??? commit needed?
raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
             while dblink_is_busy(V_DBLINK_CONNECTION_NAME) != 0 loop
                 perform pg_sleep_for(V_WAIT_FOR);
raise notice 'Waited for commit for % seconds', V_WAIT_FOR;
raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
             end loop;
             perform dblink_send_query(
                         V_DBLINK_CONNECTION_NAME,
                         'commit'
                     );

I get the following output.
psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 1 

psql:testing/test.pg_sql:41: NOTICE:  Last error: OK 

psql:testing/test.pg_sql:41: NOTICE:  Cancel query: OK 

psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 0 

psql:testing/test.pg_sql:41: NOTICE:  could not send query: another 
command is already in progress

I did all the raise notice and dblink querying and cancelling to get 
some information on what is going on but I am no wiser than before as 
without that the connection was not busy either. But it was still 
blocking I had the second call even though the commit did not seem to 
work and I was trying to send it for good. Btw, there is no entry in the 
logging table which is being done when the same function is called 
without using dblink.

Maybe I am wrong but I tried the solution with dblink_connect 
dblink_send_query instead of simply dblink believing that dblink 
function would open and close a connection at every call. I wanted to 
avoid this overhead.

Has anyone an idea?

-- 
SIP/iptel.org: thiemo.kellner
Öffentlicher PGP-Schlüssel:
http://pgp.mit.edu/pks/lookup?op=get&search=0xCA167FB0E717AFFC

Attachment

Re: dblink: could not send query: another command is already inprogress

From
Laurenz Albe
Date:
Thiemo Kellner wrote:
> I try to use dblink to create a asynchronous logging facility. I have 
> the following code
> 
> [...]
>              perform dblink_send_query(
>                          V_DBLINK_CONNECTION_NAME,
>                          V_QUERY
>                      );
> raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
> raise notice 'Last error: %', 
> dblink_error_message(V_DBLINK_CONNECTION_NAME);
> raise notice 'Cancel query: %', 
> dblink_cancel_query(V_DBLINK_CONNECTION_NAME);
>              -- ??? commit needed?
> raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
>              while dblink_is_busy(V_DBLINK_CONNECTION_NAME) != 0 loop
>                  perform pg_sleep_for(V_WAIT_FOR);
> raise notice 'Waited for commit for % seconds', V_WAIT_FOR;
> raise notice 'Connection busy: %', dblink_is_busy(V_DBLINK_CONNECTION_NAME);
>              end loop;
>              perform dblink_send_query(
>                          V_DBLINK_CONNECTION_NAME,
>                          'commit'
>                      );
> 
> I get the following output.
> psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 1 
> 
> psql:testing/test.pg_sql:41: NOTICE:  Last error: OK 
> 
> psql:testing/test.pg_sql:41: NOTICE:  Cancel query: OK 
> 
> psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 0 
> 
> psql:testing/test.pg_sql:41: NOTICE:  could not send query: another 
> command is already in progress

> Has anyone an idea?

The cause of the error message is clear; as the documentation says:

  dblink_cancel_query attempts to cancel any query that is in progress on the named
  connection. Note that this is not certain to succeed (since, for example, the remote
  query might already have finished). A cancel request simply improves the odds that
  the query will fail soon. You must still complete the normal query protocol,
  for example by calling dblink_get_result.

Not sure if that answers all your questions.

Yours,
Laurenz Albe
-- 
Cybertec | https://www.cybertec-postgresql.com


Re: dblink: could not send query: another command is already inprogress

From
Thiemo Kellner
Date:
On 03/30/18 07:39, Laurenz Albe wrote:
>> psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 1
>>
>> psql:testing/test.pg_sql:41: NOTICE:  Last error: OK
>>
>> psql:testing/test.pg_sql:41: NOTICE:  Cancel query: OK
>>
>> psql:testing/test.pg_sql:41: NOTICE:  Connection busy: 0
>>
>> psql:testing/test.pg_sql:41: NOTICE:  could not send query: another
>> command is already in progress
> 
>> Has anyone an idea?
> 
> The cause of the error message is clear; as the documentation says:

Not to me. As mentioned in my first post, originally I did no cancelling 
and error checking and had the same error. Trying to cancelling did not 
alter behaviour. It boils down to: Why do I get the "another command is 
already in progress" when dblink_is_busy acknowledges the dblink to not 
be busy by returning 0?

>    the query will fail soon. You must still complete the normal query protocol,
>    for example by calling dblink_get_result.

Ah, maybe this is the underlying problem. If dblink requires that 
results get retrieved by dblink_get_result before the dblink is actually 
ready to receive another query, it would explain the error I get. 
However, I feel than the result of dblink_is_busy is faulty, 
counter-intuitive or just useless in that context. Or I just 
misinterpreted documentation: "checks if connection is busy with an 
async query"

My understand there is that the actual query is still being processed, 
the gathering of the results. I did not count the keeping of the result 
as part of the query.

I hope I can check on that today and get back with my findings.

-- 
SIP/iptel.org: thiemo.kellner
Öffentlicher PGP-Schlüssel:
http://pgp.mit.edu/pks/lookup?op=get&search=0xCA167FB0E717AFFC

Attachment

Re: dblink: could not send query: another command is already inprogress

From
Laurenz Albe
Date:
Thiemo Kellner wrote:
> The cause of the error message is clear; as the documentation says:
> 
> >    the query will fail soon. You must still complete the normal query protocol,
> >    for example by calling dblink_get_result.
> 
> Ah, maybe this is the underlying problem. If dblink requires that 
> results get retrieved by dblink_get_result before the dblink is actually 
> ready to receive another query, it would explain the error I get. 
> However, I feel than the result of dblink_is_busy is faulty, 
> counter-intuitive or just useless in that context. Or I just 
> misinterpreted documentation: "checks if connection is busy with an 
> async query"
> 
> My understand there is that the actual query is still being processed, 
> the gathering of the results. I did not count the keeping of the result 
> as part of the query.

That is a misunderstanding.

If the connection is "busy", that means that the asynchronous query
is still busy receiving a result from the server.

Once it is no longer busy, a result has arrived and is ready to be
consumed.

You have to consume the result before you can send the next query.

Yours,
Laurenz Albe
-- 
Cybertec | https://www.cybertec-postgresql.com


Re: dblink: could not send query: another command is already inprogress

From
Thiemo Kellner
Date:
On 03/30/18 11:14, Laurenz Albe wrote:
> You have to consume the result before you can send the next query.

I changed implementation but still get the same error but now different 
context. I tried to retrieve the result but I failed

I committed the last code to its project repository at SourceForge 
https://sourceforge.net/p/pglogger/code/HEAD/tree/

and put the code to pastebin.com
Test calls: https://pastebin.com/xfUp9NAL
function WRITE_MESSAGE: https://pastebin.com/LDjE0Czx
function WRITE_MESSAGE_TO_TABLE: https://pastebin.com/vkBkehZF

Output
psql:testing/test.pg_sql:41: NOTICE:  2018-03-30 18:41:18.245592
    - Calling function:
    - Current user: act
    - Session user: act
    - Transaction timestamp: 2018-03-30 18:41:18.240580
    - Transaction ID:
    - Server process ID: 22401
    - Address of the remote connection:
    - Port of the remote connection:
    - Message: Test WRITE_MESSAGE
  write_message
---------------

(1 row)

psql:testing/test.pg_sql:42: NOTICE:  2018-03-30 18:41:18.257750
    - Calling function:
    - Current user: act
    - Session user: act
    - Transaction timestamp: 2018-03-30 18:41:18.240580
    - Transaction ID:
    - Server process ID: 22401
    - Address of the remote connection:
    - Port of the remote connection:
    - Message: Test WRITE_MESSAGE 2nd call
psql:testing/test.pg_sql:42: ERROR:  function 
WRITE_MESSAGE_TO_TABLE(i_function => text, i_message => text, i_level => 
text, i_present_user => name, i_session_user => name, 
i_transaction_timestamp => timestamp with time zone, i_transaction_id => 
bigint, i_server_pid => bigint, i_remote_address => inet, i_remote_port 
=> integer) does not exist
HINT:  No function matches the given name and argument types. You might 
need to add explicit type casts.
CONTEXT:  Error occurred on dblink connection named 
"PGLOGGER_CONNECTION": could not execute query.
SQL statement "SELECT * from 
dblink_get_result(V_DBLINK_CONNECTION_NAME) as t(ID bigint)"
PL/pgSQL function write_message(text,text) line 126 at PERFORM
psql:testing/test.pg_sql:42: STATEMENT:  select WRITE_MESSAGE('Test 
WRITE_MESSAGE 2nd call', 'NOTICE');

-- 
SIP/iptel.org: thiemo.kellner
Öffentlicher PGP-Schlüssel:
http://pgp.mit.edu/pks/lookup?op=get&search=0xCA167FB0E717AFFC

Attachment

Re: dblink: could not send query: another command is already inprogress

From
Laurenz Albe
Date:
Thiemo Kellner wrote:
> On 03/30/18 11:14, Laurenz Albe wrote:
> > You have to consume the result before you can send the next query.
> 
> I changed implementation but still get the same error but now different 
> context. I tried to retrieve the result but I failed
> 
> I committed the last code to its project repository at SourceForge 
> https://sourceforge.net/p/pglogger/code/HEAD/tree/
> 
> and put the code to pastebin.com
> Test calls: https://pastebin.com/xfUp9NAL
> function WRITE_MESSAGE: https://pastebin.com/LDjE0Czx
> function WRITE_MESSAGE_TO_TABLE: https://pastebin.com/vkBkehZF
> 
> [...]
> psql:testing/test.pg_sql:42: ERROR:  function 
> WRITE_MESSAGE_TO_TABLE(i_function => text, i_message => text, i_level => 
> text, i_present_user => name, i_session_user => name, 
> i_transaction_timestamp => timestamp with time zone, i_transaction_id => 
> bigint, i_server_pid => bigint, i_remote_address => inet, i_remote_port 
> => integer) does not exist
> HINT:  No function matches the given name and argument types. You might 
> need to add explicit type casts.
> CONTEXT:  Error occurred on dblink connection named 
> "PGLOGGER_CONNECTION": could not execute query.
> SQL statement "SELECT * from 
> dblink_get_result(V_DBLINK_CONNECTION_NAME) as t(ID bigint)"
> PL/pgSQL function write_message(text,text) line 126 at PERFORM
> psql:testing/test.pg_sql:42: STATEMENT:  select WRITE_MESSAGE('Test 
> WRITE_MESSAGE 2nd call', 'NOTICE');

I couldn't dig through all your code, but two things seemed suspicious:

> if (
>                 V_DBLINK_CONNECTION_NAMES is null
>              or V_DBLINK_CONNECTION_NAME != any(V_DBLINK_CONNECTION_NAMES)

I don't think you want "!=ANY" there.  Shouldn't that be "<>ALL" ?
"<>ANY" will *always* be true if there are at least two different entries.


The other thing is that you seem to call "dblink_get_result" on any existing
connection before use.  But you can only call the function if there is a
result outstanding.

One of your error messages above seems to suggest that this is your problem.

Yours,
Laurenz Albe
-- 
Cybertec | https://www.cybertec-postgresql.com


Re: dblink: could not send query: another command is already inprogress

From
Thiemo Kellner
Date:

On 04/03/18 11:28, Laurenz Albe wrote:
>> [...]
>> psql:testing/test.pg_sql:42: ERROR:  function
>> WRITE_MESSAGE_TO_TABLE(i_function => text, i_message => text, i_level =>
>> text, i_present_user => name, i_session_user => name,
>> i_transaction_timestamp => timestamp with time zone, i_transaction_id =>
>> bigint, i_server_pid => bigint, i_remote_address => inet, i_remote_port
>> => integer) does not exist
>> HINT:  No function matches the given name and argument types. You might
>> need to add explicit type casts.
>> CONTEXT:  Error occurred on dblink connection named
>> "PGLOGGER_CONNECTION": could not execute query.
>> SQL statement "SELECT * from
>> dblink_get_result(V_DBLINK_CONNECTION_NAME) as t(ID bigint)"
>> PL/pgSQL function write_message(text,text) line 126 at PERFORM
>> psql:testing/test.pg_sql:42: STATEMENT:  select WRITE_MESSAGE('Test
>> WRITE_MESSAGE 2nd call', 'NOTICE');
> 
> I couldn't dig through all your code, but two things seemed suspicious:

Thanks for looking into all the same.

>> if (
>>                  V_DBLINK_CONNECTION_NAMES is null
>>               or V_DBLINK_CONNECTION_NAME != any(V_DBLINK_CONNECTION_NAMES)
> 
> I don't think you want "!=ANY" there.  Shouldn't that be "<>ALL" ?
> "<>ANY" will *always* be true if there are at least two different entries.

In my simple test it works as expected with respect to that. But I did 
use it the wrong way as I thought that ! would invert the result of the 
any operation as a whole. I changed it to != all but the error persists.

> The other thing is that you seem to call "dblink_get_result" on any existing
> connection before use.  But you can only call the function if there is a
> result outstanding.

I call dblink_get_result only if I do not open a dblink connection, i. 
e. only on second and following function calls. I put more notice output 
into the code showing that dblink_send_query has been called once before 
the first call of dblink_get_result. I changed my query to reflect 
return bigint value of the called function write_message_to_table. Error 
persists.

I put a new pastebin https://pastebin.com/7R45R7qK and committed the 
changes.

-- 
+49 (0)1578-772 37 37
+41 (0)78 947 36 21
SIP/iptel.org: thiemo.kellner
Öffentlicher PGP-Schlüssel:
http://pgp.mit.edu/pks/lookup?op=get&search=0xCA167FB0E717AFFC

Attachment

Re: dblink: could not send query: another command is already inprogress

From
Laurenz Albe
Date:
Thiemo Kellner wrote:
> > The other thing is that you seem to call "dblink_get_result" on any existing
> > connection before use.  But you can only call the function if there is a
> > result outstanding.
> 
> I call dblink_get_result only if I do not open a dblink connection, i. 
> e. only on second and following function calls. I put more notice output 
> into the code showing that dblink_send_query has been called once before 
> the first call of dblink_get_result. I changed my query to reflect 
> return bigint value of the called function write_message_to_table. Error 
> persists.

That's not going to work.
You can only call dblink_get_result if there is an outstanding result.
You cannot call it on an idle connection, that will cause an error.

You should write your code so that whenever dblink_send_query has been
called, you also call dblink_get_result, regardless if the query has
been cancelled or not, so that you always leave the connection in
the "ready for query" state.
Then you don't have to clean up.

Of course you could also ignore the error you get on dblink_get_result,
but that is ugly.

Yours,
Laurenz Albe
-- 
Cybertec | https://www.cybertec-postgresql.com