Bug #35477 sort_union() leaks file descriptors whenever goes over sort_buffer
Submitted: 21 Mar 2008 11:10 Modified: 1 May 2008 1:52
Reporter: Domas Mituzas Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server: General Severity:S2 (Serious)
Version:5.0-bk, 5.1.24, 6.0 OS:Any
Assigned to: Assigned Account CPU Architecture:Any
Tags: fd leak, memory leak

[21 Mar 2008 11:10] Domas Mituzas
Description:
whenever sort_union() is used, and sort_buffer_size is reached, temporary files are leaked:

mysqld  3988 mysql  122u     REG      14,2      78654 16999463 /private/var/tmp/MY69MtDm
mysqld  3988 mysql  123u     REG      14,2      78654 16999469 /private/var/tmp/MYuUS218
mysqld  3988 mysql  124u     REG      14,2      78654 16999475 /private/var/tmp/MYXTIgLK
mysqld  3988 mysql  125u     REG      14,2      78654 16999481 /private/var/tmp/MYhnvrNz
mysqld  3988 mysql  126u     REG      14,2      78654 16999487 /private/var/tmp/MYXAYcoG
mysqld  3988 mysql  127u     REG      14,2      78654 16999493 /private/var/tmp/MYhOGRAZ
mysqld  3988 mysql  128u     REG      14,2      78654 16999499 /private/var/tmp/MY1RLE4Q
mysqld  3988 mysql  129u     REG      14,2      78654 16999505 /private/var/tmp/MYwXVR6D
mysqld  3988 mysql  130u     REG      14,2      78654 16999511 /private/var/tmp/MYh8SGBC

How to repeat:
CREATE TABLE `jtest` (
  `a` varchar(255) default NULL,
  `b` varchar(255) default NULL,
  `c` varchar(255) default NULL,
  KEY `a` (`a`),
  KEY `b` (`b`),
  KEY `c` (`c`)
) ENGINE=MyISAM 

fill with few megabytes of random data, so that hitting sort buffer problems would be easier

set sort_buffer_size=10000;
then loop: 
select * from jtest where a like '5%' or b like '5%' order by c limit 1;

mysql> explain select * from jtest where a like '5%' or b like '5%' order by c limit 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jtest
         type: index_merge
possible_keys: a,b
          key: a,b
      key_len: 258,258
          ref: NULL
         rows: 23746
        Extra: Using sort_union(a,b); Using where; Using filesort
1 row in set (0.00 sec)

Suggested fix:
close the files?
[21 Mar 2008 11:18] Domas Mituzas
example dump file

Attachment: jtest.sql.gz (application/x-gzip, text), 465.77 KiB.

[21 Mar 2008 11:22] Domas Mituzas
This bug got a brother at #35478 - it also returns invalid data
[26 Mar 2008 23:43] Domas Mituzas
My amateurish fix, seems to handle the situation without too much crashing, and doesn't leak FDs. Bug#35478 still there, though:
flute:~/Development/mysql-5.0/sql midom$ diff -U 7 filesort.cc filesort.cc.fixed 
--- filesort.cc 2008-03-27 01:41:17.000000000 +0200
+++ filesort.cc.fixed   2008-03-27 01:41:08.000000000 +0200
@@ -553,14 +553,15 @@
   if (quick_select)
   {
     /*
       index_merge quick select uses table->sort when retrieving rows, so free
       resoures it has allocated.
     */
     end_read_record(&read_record_info);
+    free_io_cache(select->quick->head);
   }
   else
   {
     (void) file->extra(HA_EXTRA_NO_CACHE);     /* End cacheing of records */
     if (!next_pos)
       file->ha_rnd_end();
   }
[27 Mar 2008 6:20] Domas Mituzas
patch seems to pass testsuite
[27 Mar 2008 9:34] Domas Mituzas
This bug also leaks memory:

==18833== 66,073 (510 direct, 65,563 indirect) bytes in 6 blocks are definitely lost in loss record 6 of 7
==18833==    at 0x4022765: malloc (vg_replace_malloc.c:149)
==18833==    by 0x831A874: my_malloc (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x8219CFF: Unique::get(st_table*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x824B166: QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x825B5AD: find_all_keys(st_sort_param*, SQL_SELECT*, unsigned char**, st_io_cache*, st_io_cache*, st_io_cache*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x825D7A8: filesort(THD*, st_table*, st_sort_field*, unsigned, SQL_SELECT*, unsigned long, unsigned long*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81F56A4: create_sort_index(THD*, JOIN*, st_order*, unsigned long, unsigned long) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x8201904: JOIN::exec() (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x8203AE7: mysql_select(THD*, Item***, TABLE_LIST*, unsigned, List<Item>&, Item*, unsigned, st_order*, st_order*, Item*, st_order*, unsigned long long, select_result*, st_select_lex_unit*, st_select_lex*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x820436E: handle_select(THD*, st_lex*, select_result*, unsigned long) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81AC028: mysql_execute_command(THD*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81B14BB: mysql_parse(THD*, char const*, unsigned, char const**) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833== 
==18833== 
==18833== 818,984 bytes in 4 blocks are possibly lost in loss record 7 of 7
==18833==    at 0x4022765: malloc (vg_replace_malloc.c:149)
==18833==    by 0x831A874: my_malloc (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x825AA8F: init_read_record(st_read_record*, THD*, st_table*, SQL_SELECT*, int, bool) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x824B1A9: QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x825B5AD: find_all_keys(st_sort_param*, SQL_SELECT*, unsigned char**, st_io_cache*, st_io_cache*, st_io_cache*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x825D7A8: filesort(THD*, st_table*, st_sort_field*, unsigned, SQL_SELECT*, unsigned long, unsigned long*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81F56A4: create_sort_index(THD*, JOIN*, st_order*, unsigned long, unsigned long) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x8201904: JOIN::exec() (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x8203AE7: mysql_select(THD*, Item***, TABLE_LIST*, unsigned, List<Item>&, Item*, unsigned, st_order*, st_order*, Item*, st_order*, unsigned long long, select_result*, st_select_lex_unit*, st_select_lex*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x820436E: handle_select(THD*, st_lex*, select_result*, unsigned long) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81AC028: mysql_execute_command(THD*) (in /usr/local/mysql-5.0/libexec/mysqld)
==18833==    by 0x81B14BB: mysql_parse(THD*, char const*, unsigned, char const**) (in /usr/local/mysql-5.0/libexec/mysqld)
[27 Mar 2008 9:39] Domas Mituzas
slightly better formatted valgrind output at:

http://p.defau.lt/?_ZxrNXcfnAPoKHSYrgqQSw
[27 Mar 2008 12:03] Domas Mituzas
Fix for memory leak:

--- opt_range.cc        2008-03-27 08:03:13.000000000 -0400
+++ opt_range.cc.new    2008-03-27 08:03:04.000000000 -0400
@@ -1035,14 +1035,16 @@
   QUICK_RANGE_SELECT* quick;
   DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT");
   quick_it.rewind();
   while ((quick= quick_it++))
     quick->file= NULL;
   quick_selects.delete_elements();
   delete pk_quick_select;
+  if (&read_record)
+    end_read_record(&read_record);
   free_root(&alloc,MYF(0));
   DBUG_VOID_RETURN;
 }
 
 
 QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
                                                        TABLE *table,
[4 Apr 2008 13:54] Georgi Kodinov
Domas,

I've tried with the latest 5.0-opt-bk and I'm not getting the effect (no leaked fds) even after repeating the query several times. But I'm probably missing something in recreating the problem.

Can we please get a real test script (for mysqltest) that uses e.g. INSERT INTO t1 ...  SELECT ... FROM t1 ... to generate data (as looking at your test data dump this seems doable) ?
[4 Apr 2008 14:35] MySQL Verification Team
Georgi, I haven't tested this, but you can try setting the lowest possible sort_buffer_size and see if it leaks then.
[10 Apr 2008 7:20] MySQL Verification Team
standalone testcase to show 20MB/s memory leak, and file handle leak
--------------------------------------------------------------------

delimiter ;
drop table if exists `bug35477`;
create table `bug35477` (`a` varchar(255),`b` varchar(255),`c` varchar(255),key(`a`),key(`b`),key(`c`)) engine=myisam;
insert into `bug35477`values ('1','2','3'),('4','5','6'),('7','8','9');
set @a=1,@b=1,@c=1;
insert into `bug35477` select @a:=@a+1,@b:=@b+2,@c:=@c+3 from `bug35477` a,`bug35477` b,`bug35477` c;
insert into `bug35477` select @a:=@a+1,@b:=@b+2,@c:=@c+3 from `bug35477` a,`bug35477` b,`bug35477` c;

drop procedure if exists `p_bug35477`;
delimiter //
create procedure `p_bug35477`(`numtimes` int)
begin

        declare `i` int default '0';
        repeat
                select * from `bug35477` where `a` like '5%' or `b` like '5%' order by `c` limit 1 into @a,@b,@c;
                if(`i` % 100 =0) then select `i` as repetition; end if;
                set `i`=`i`+1;
        until `i`>`numtimes` end repeat;

end//

delimiter ;

set session sort_buffer_size=1024;

call `p_bug35477`(50000);
[10 Apr 2008 12:11] MySQL Verification Team
Georgi, if my above testcase doesn't repeat the problem on your internal BK builds, i wonder if this bug was fixed in bug #27732 (Possible memory leak with index_merge) ?
[10 Apr 2008 14:17] Sergey Petrunya
Using 5.0-opt with tip cset:

ChangeSet@1.2601, 2008-03-27 20:05:51+04:00, gshchepa@host.loc +2 -0
  Merge host.loc:/home/uchum/work/mysql-5.0
  into  host.loc:/home/uchum/work/5.0-opt
  MERGE: 1.2599.1.2

(i.e. BUG#27732 is in the tree), I still can see the fd leaks.
[10 Apr 2008 14:42] Sergey Petrunya
Can observe leaks on the latest 6.0 opt also. tip cset:
ChangeSet@1.2604, 2008-04-07 13:21:26-06:00, malff@lambda.hsd1.co.comcast.net. +1 -0
  Merge malff@bk-internal.mysql.com:/home/bk/mysql-5.0-opt
  into  lambda.hsd1.co.comcast.net.:/home/malff/TREE/mysql-5.0-35658
  MERGE: 1.2598.5.1
[10 Apr 2008 14:43] Sergey Petrunya
The above two observations were made by using Domas's testcase
[21 Apr 2008 4:21] Sergey Petrunya
The fix is at http://lists.mysql.com/commits/45739
[1 May 2008 1:52] Sergey Petrunya
Fixed by fix for BUG#35478. Changing status to duplicate.
[22 Jul 2008 18:36] Bugs System
Pushed into 5.0.68
[28 Jul 2008 13:46] Bugs System
Pushed into 5.0.68  (revid:kpettersson@mysql.com-20080716141220-83aobe5fdgd9ye5l) (pib:2)
(Retry automatic marking, to ensure nothing is missed. cm01)
[28 Jul 2008 14:45] Bugs System
Pushed into 6.0.7-alpha  (revid:alik@mysql.com-20080725172155-fnc73o50e4tgl23k) (version source revid:alik@mysql.com-20080725172155-fnc73o50e4tgl23k) (pib:3)
[28 Jul 2008 16:44] Bugs System
Pushed into 5.1.28  (revid:davi.arnaut@sun.com-20080722182431-0i2f1yc4uocime9q) (version source revid:davi.arnaut@sun.com-20080722182431-0i2f1yc4uocime9q) (pib:3)
[14 Sep 2008 2:21] Bugs System
Pushed into 6.0.7-alpha  (revid:sergefp@mysql.com-20080715141321-2vlk93j5blqa7kxe) (version source revid:sven@mysql.com-20080818195835-r615g9zz6xphmkzg) (pib:3)