Fix memory leak in "batch" mode in extended query.
authorTatsuo Ishii <ishii@sraoss.co.jp>
Thu, 28 Mar 2019 04:58:27 +0000 (13:58 +0900)
committerTatsuo Ishii <ishii@sraoss.co.jp>
Fri, 29 Mar 2019 07:14:14 +0000 (16:14 +0900)
In "batch" mode, not for every execute message, a sync message is
followed.  Unfortunately Pgpool-II only discard memory of query
context for the last execute message while processing the ready for
query message. For example if 3 execute messages are sent before the
sync message, 2 of query context memory will not be freed and this
leads to serious memory leak.

To fix the problem, now the query context memory is possibly discarded
when a command complete message is returned from backend if the query
context is not referenced either by sent messages or pending messages.
If it is not referenced at all, we can discard the query context.

Also even if it is referenced, it is ok to discard the query context
if it is either an unnamed statement or an unnamed portal because it
will be discarded anyway when next unnamed statement or portal is
created.

Per bug 468.

src/context/pool_session_context.c
src/include/context/pool_session_context.h
src/protocol/CommandComplete.c

index 0e0185858fe6ae18f77cbbb63e4573cadb8e3aa6..7b4b99e595619ee144a12238417df33dfd9c4980 100644 (file)
@@ -460,6 +460,30 @@ void pool_clear_sent_message_list(void)
        }
 }
 
+/*
+ * Zap query context info in sent messages to indicate that the query context
+ * has been already removed.
+ */
+void
+pool_zap_query_context_in_sent_messages(POOL_QUERY_CONTEXT *query_context)
+{
+       int                     i;
+       POOL_SENT_MESSAGE_LIST *msglist;
+
+       msglist = &pool_get_session_context(false)->message_list;
+
+       for (i = 0; i < msglist->size; i++)
+       {
+               elog(LOG, "checking zapping sent message: %p query_context: %p",
+                        &msglist->sent_messages[i], msglist->sent_messages[i]->query_context);
+               if (msglist->sent_messages[i]->query_context == query_context)
+               {
+                       msglist->sent_messages[i]->query_context = NULL;
+                       elog(LOG, "Zap sent message: %p", &msglist->sent_messages[i]);
+               }
+       }
+}
+
 static void dump_sent_message(char *caller, POOL_SENT_MESSAGE *m)
 {
        ereport(DEBUG1,
@@ -569,7 +593,7 @@ void pool_add_sent_message(POOL_SENT_MESSAGE *message)
 }
 
 /*
- * Get a sent message
+ * Find a sent message by kind and name.
  */
 POOL_SENT_MESSAGE *pool_get_sent_message(char kind, const char *name, POOL_SENT_MESSAGE_STATE state)
 {
@@ -592,6 +616,29 @@ POOL_SENT_MESSAGE *pool_get_sent_message(char kind, const char *name, POOL_SENT_
        return NULL;
 }
 
+/*
+ * Find a sent message by query context.
+ */
+POOL_SENT_MESSAGE *
+pool_get_sent_message_by_query_context(POOL_QUERY_CONTEXT * query_context)
+{
+       int                     i;
+       POOL_SENT_MESSAGE_LIST *msglist;
+
+       msglist = &pool_get_session_context(false)->message_list;
+
+       if (query_context == NULL)
+               return NULL;
+
+       for (i = 0; i < msglist->size; i++)
+       {
+               if (msglist->sent_messages[i]->query_context == query_context)
+                       return msglist->sent_messages[i];
+       }
+
+       return NULL;
+}
+
 /*
  * Set message state to POOL_SENT_MESSAGE_STATE to POOL_SENT_MESSAGE_CLOSED.
  */
index 18c5f72f6b8e8dc63910817cd00bfcd71a7429eb..0c2bde6cd5f376be01a38e4a16db32b4783fb40f 100644 (file)
@@ -271,6 +271,8 @@ extern void pool_clear_sent_message_list(void);
 extern void pool_sent_message_destroy(POOL_SENT_MESSAGE *message);
 extern POOL_SENT_MESSAGE *pool_get_sent_message(char kind, const char *name, POOL_SENT_MESSAGE_STATE state);
 extern void pool_set_sent_message_state(POOL_SENT_MESSAGE *message);
+extern void pool_zap_query_context_in_sent_messages(POOL_QUERY_CONTEXT *query_context);
+extern POOL_SENT_MESSAGE * pool_get_sent_message_by_query_context(POOL_QUERY_CONTEXT * query_context);
 extern void pool_unset_writing_transaction(void);
 extern void pool_set_writing_transaction(void);
 extern bool pool_is_writing_transaction(void);
index 9191859bfaa16fa30f8fb2a86d2b9046fa8e12e8..66c864204af23f22cda52fe2458c6edd03a22248 100644 (file)
@@ -201,6 +201,31 @@ POOL_STATUS CommandComplete(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *bac
                pool_at_command_success(frontend, backend);
                pool_unset_query_in_progress();
                pool_pending_message_reset_previous_message();
+
+               if (session_context->query_context == NULL)
+               {
+                       elog(WARNING, "At command complete there's no query context");
+               }
+               else
+               {
+                       /*
+                        * Destroy query context if it is not referenced from sent
+                        * messages and pending messages except bound to named statements
+                        * or portals.  Named statements and portals should remain until
+                        * they are explicitly closed.
+                        */
+                       if (can_query_context_destroy(session_context->query_context))
+
+                       {
+                               POOL_SENT_MESSAGE * msg = pool_get_sent_message_by_query_context(session_context->query_context);
+
+                               if (!msg || (msg && *msg->name == '\0'))
+                               {
+                                       pool_zap_query_context_in_sent_messages(session_context->query_context);
+                                       pool_query_context_destroy(session_context->query_context);
+                               }
+                       }
+               }
        }
 
        return POOL_CONTINUE;