[MDEV-14959] Control over memory allocated for SP/PS Created: 2018-01-16  Updated: 2024-01-10

Status: Stalled
Project: MariaDB Server
Component/s: Stored routines
Fix Version/s: 10.4, 10.5, 10.6, 10.11, 11.0, 11.1, 11.3

Type: Task Priority: Critical
Reporter: Oleksandr Byelkin Assignee: Dmitry Shulga
Resolution: Unresolved Votes: 3
Labels: gsoc18, gsoc19, gsoc20, gsoc21, triage

Issue Links:
Blocks
is blocked by MDEV-32369 Memory leak when executing PS for que... Closed
PartOf
includes MDEV-32466 Potential memory leak on execuing of ... Closed
includes MDEV-32517 Memory leak on running query in PS mo... Closed
includes MDEV-32733 Two JSON related tests running in PS ... Closed
Relates
relates to MDEV-30889 Memory leak issues with MariaDB 10.6.... Stalled
relates to MDEV-32223 Memory leak showed in MDEV-6146 test ... Closed
relates to MDEV-31127 Possible memory leak on running inser... Open

 Description   

SP/PS (Stored Procedures / Prepared Statements) allocates memory till the PS cache of SP will be destroyed. There is no way to see how many memory allocated and if it grows with each execution (first 2 execution can lead to new memory allocation but not more)

Task minimum:
Status variables which count the memory used/allocated for SP/PS by thread and/or for the server.

Other ideas:

  • Automatic stop allocation in debugging version after second execution and call exception on attempt.
  • Information schema by threads and SP/PS with information about allocated and used memory

Information can be collected in MEM_ROOTs of SP/PS. Storing info about status of mem_root before execution then checking after new allocated memory can be found.

MEM_ROOT can be changed to have debug mode which make it read only which can be switched on after second execution.



 Comments   
Comment by Zhangxian Xu [ 2021-02-18 ]

Hi, I'm Zhangxian Xu, a student pursuing master's degree in Computer Science. It's a nice issue and I want to complete it as my Gsoc 2021 project . Hope to get some guidence on this issue. Thanks!

Comment by Oleksandr Byelkin [ 2022-04-11 ]

a draft

diff --git a/include/my_alloc.h b/include/my_alloc.h
index 52c04fe1b7d..4e67f4a1ee3 100644
--- a/include/my_alloc.h
+++ b/include/my_alloc.h
@@ -49,6 +49,9 @@ typedef struct st_mem_root
      MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)
   */
   unsigned int first_block_usage;
+#ifndef DBUG_OFF
+  my_bool read_only;
+#endif
 
   void (*error_handler)(void);
 } MEM_ROOT;
diff --git a/include/mysql.h.pp b/include/mysql.h.pp
index 73b9f9f588b..4eea704165f 100644
--- a/include/mysql.h.pp
+++ b/include/mysql.h.pp
@@ -238,6 +238,7 @@ typedef struct st_mem_root
   size_t block_size;
   unsigned int block_num;
   unsigned int first_block_usage;
+  my_bool read_only;
   void (*error_handler)(void);
 } MEM_ROOT;
 typedef struct st_typelib {
diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c
index d67b8be9bb8..8c09e4bab68 100644
--- a/mysys/my_alloc.c
+++ b/mysys/my_alloc.c
@@ -71,6 +71,9 @@ void init_alloc_root(MEM_ROOT *mem_root, size_t block_size,
   mem_root->error_handler= 0;
   mem_root->block_num= 4;			/* We shift this with >>2 */
   mem_root->first_block_usage= 0;
+#ifndef DBUG_OFF
+  mem_root->read_only= 0;
+#endif
 
 #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
   if (pre_alloc_size)
@@ -177,6 +180,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
 
   DBUG_ASSERT(alloc_root_inited(mem_root));
 
+  DBUG_ASSERT(mem_root->read_only == 0);
+
   DBUG_EXECUTE_IF("simulate_out_of_memory",
                   {
                     if (mem_root->error_handler)
@@ -210,6 +215,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
   DBUG_PRINT("enter",("root: %p", mem_root));
   DBUG_ASSERT(alloc_root_inited(mem_root));
 
+  DBUG_ASSERT(mem_root->read_only == 0);
+
   DBUG_EXECUTE_IF("simulate_out_of_memory",
                   {
                     /* Avoid reusing an already allocated block */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 64e4cd30561..726e1634904 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -167,6 +167,9 @@ class Prepared_statement: public Statement
   Server_side_cursor *cursor;
   uchar *packet;
   uchar *packet_end;
+#ifndef DBUG_OFF
+  ulong executed_counter;
+#endif
   uint param_count;
   uint last_errno;
   uint flags;
@@ -3996,6 +3999,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
   cursor(0),
   packet(0),
   packet_end(0),
+#ifndef DBUG_OFF
+  executed_counter(0),
+#endif
   param_count(0),
   last_errno(0),
   flags((uint) IS_IN_USE),
@@ -4069,8 +4075,8 @@ void Prepared_statement::setup_set_params()
 Prepared_statement::~Prepared_statement()
 {
   DBUG_ENTER("Prepared_statement::~Prepared_statement");
-  DBUG_PRINT("enter",("stmt: %p  cursor: %p",
-                      this, cursor));
+  DBUG_PRINT("enter",("stmt: %p  cursor: %p, executed: %lu",
+                      this, cursor, executed_counter));
   delete cursor;
   /*
     We have to call free on the items even if cleanup is called as some items,
@@ -4228,6 +4234,9 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
     init_param_array(this);
 
   lex->set_trg_event_type_for_tables();
+#ifndef DBUG_OFF
+  executed_counter= 0;
+#endif
 
   /*
     While doing context analysis of the query (in check_prepared_statement)
@@ -4525,6 +4534,16 @@ Prepared_statement::execute_loop(String *expanded_query,
       goto reexecute;
   }
   reset_stmt_params(this);
+#ifndef DBUG_OFF
+  if (!error)
+  {
+    if (++executed_counter >= 2)
+    {
+      mem_root->read_only= 1;
+    }
+    DBUG_PRINT("XXX", ("execute counter: %lu", executed_counter));
+  }
+#endif
 
   return error;
 }

Generated at Thu Feb 08 08:17:38 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.