Bug #112035 Materializing performance_schema.data_locks can lead to excessive mem usage/OOM
Submitted: 10 Aug 19:33 Modified: 21 Nov 16:06
Reporter: Marc Reilly Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Performance Schema Severity:S2 (Serious)
Version:8.0.34 OS:Any
Assigned to: CPU Architecture:Any
Tags: data_locks, OOM, performance schema, server crash

[10 Aug 19:33] Marc Reilly
Description:
Issue 1: When materializing performance_schema.data_locks when there are a lot of locks held on the server can lead to a server exit due to OOM. This is quite simple to reproduce using ro queries on a server. 

Issue 2: The memory allocated here during materialization does not seem to be tracked in the performance schema memory instrumentation, making it difficult to troubleshoot where memory is being allocated on a live server. 

Here I'm using a 10000000 row table to demonstrate on a r5.large ec2 instance (2 vcpu, 16GiB memory ), to repro you can adjust the number of rows/locks taken. 
Steps:
# generate sysbench data and enable P_S instruments
# Based on my above specs I'm using 10 tables with 10000000 rows. 
sysbench /usr/local/share/sysbench/oltp_write_only.lua --db-driver=mysql --mysql-user=msandbox --mysql-password=msandbox --mysql-db=test --mysql-socket=/tmp/mysql_sandbox8034.sock --mysql-port=8034 --threads=10 --rand-type=uniform --db-ps-mode=disable --tables=10 --forced-shutdown --table-size=10000000 --create_secondary=0 prepare
update performance_schema.setup_consumers set enabled=1;
update performance_schema.setup_instruments set enabled=1;

Open 3 sessions:
# Execute 1st and leave open
Session 1:
PAGER tail
begin;
select * from sbtest1 for update;
<do the same for sbtest2,3 etc for more rows>

# Run after session 1
Session 2: 
SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'test' LIMIT 100;

# Observe system memory usage while session 2 is running
Session 3:
while sleep 5; do sandboxes/msb_8_0_34/use -e "select * from sys.memory_global_by_current_bytes limit 10";sudo dmesg | grep "Out of memory"; free -m;sleep 3;done

How to repeat:
Session 1: Run a query which generates a lot of row locks. Here I'm demonstrating using sysbench with select for update
PAGER tail
begin;
select * from sbtest1 for update;
<do the same for sbtest2,3 etc for more rows>

Session 2: Once session 1 is finished gathering the locks, run a query against performance_schema.data_locks in session 2 to get info on locks against the test schema. This will fail and cause server to OOM. 
mysql [localhost:8034] {msandbox} (test) > SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'test' LIMIT 100;
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql_sandbox8034.sock' (111)
ERROR:
Can't connect to the server

Session 3: Here, once session 2 starts executing, I'm checking OS level stats and performance schema. Note how free -m is reporting the drop in mem until we OOM, performance schema is not changing at all. 
 while sleep 5; do sandboxes/msb_8_0_34/use -e "select * from sys.memory_global_by_current_bytes limit 10";sudo dmesg | grep "Out of memory"; free -m;sleep 3;done
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| event_name                                                                  | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| memory/innodb/buf_buf_pool                                                  |            80 | 10.22 GiB     | 130.88 MiB        |         80 | 10.22 GiB  | 130.88 MiB     |
| memory/performance_schema/events_statements_summary_by_digest               |             1 | 40.28 MiB     | 40.28 MiB         |          1 | 40.28 MiB  | 40.28 MiB      |
| memory/innodb/ut0link_buf                                                   |             2 | 24.00 MiB     | 12.00 MiB         |          2 | 24.00 MiB  | 12.00 MiB      |
| memory/innodb/log_buffer_memory                                             |             1 | 16.00 MiB     | 16.00 MiB         |          1 | 16.00 MiB  | 16.00 MiB      |
| memory/performance_schema/events_statements_history_long                    |             1 | 14.34 MiB     | 14.34 MiB         |          1 | 14.34 MiB  | 14.34 MiB      |
| memory/performance_schema/events_errors_summary_by_thread_by_error          |           257 | 12.81 MiB     | 51.04 KiB         |        257 | 12.81 MiB  | 51.04 KiB      |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name |             1 | 11.55 MiB     | 11.55 MiB         |          1 | 11.55 MiB  | 11.55 MiB      |
| memory/performance_schema/events_statements_summary_by_digest.digest_text   |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.digest_text        |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.sql_text           |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
               total        used        free      shared  buff/cache   available
Mem:           15779        5445        8019           0        2314       10074
Swap:              0           0           0
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| event_name                                                                  | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| memory/innodb/buf_buf_pool                                                  |            80 | 10.22 GiB     | 130.88 MiB        |         80 | 10.22 GiB  | 130.88 MiB     |
| memory/performance_schema/events_statements_summary_by_digest               |             1 | 40.28 MiB     | 40.28 MiB         |          1 | 40.28 MiB  | 40.28 MiB      |
| memory/innodb/ut0link_buf                                                   |             2 | 24.00 MiB     | 12.00 MiB         |          2 | 24.00 MiB  | 12.00 MiB      |
| memory/innodb/log_buffer_memory                                             |             1 | 16.00 MiB     | 16.00 MiB         |          1 | 16.00 MiB  | 16.00 MiB      |
| memory/performance_schema/events_statements_history_long                    |             1 | 14.34 MiB     | 14.34 MiB         |          1 | 14.34 MiB  | 14.34 MiB      |
| memory/performance_schema/events_errors_summary_by_thread_by_error          |           257 | 12.81 MiB     | 51.04 KiB         |        257 | 12.81 MiB  | 51.04 KiB      |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name |             1 | 11.55 MiB     | 11.55 MiB         |          1 | 11.55 MiB  | 11.55 MiB      |
| memory/performance_schema/events_statements_summary_by_digest.digest_text   |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.digest_text        |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.sql_text           |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
               total        used        free      shared  buff/cache   available
Mem:           15779        7267        6198           0        2314        8253
Swap:              0           0           0
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| event_name                                                                  | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
| memory/innodb/buf_buf_pool                                                  |            80 | 10.22 GiB     | 130.88 MiB        |         80 | 10.22 GiB  | 130.88 MiB     |
| memory/performance_schema/events_statements_summary_by_digest               |             1 | 40.28 MiB     | 40.28 MiB         |          1 | 40.28 MiB  | 40.28 MiB      |
| memory/innodb/ut0link_buf                                                   |             2 | 24.00 MiB     | 12.00 MiB         |          2 | 24.00 MiB  | 12.00 MiB      |
| memory/innodb/log_buffer_memory                                             |             1 | 16.00 MiB     | 16.00 MiB         |          1 | 16.00 MiB  | 16.00 MiB      |
| memory/performance_schema/events_statements_history_long                    |             1 | 14.34 MiB     | 14.34 MiB         |          1 | 14.34 MiB  | 14.34 MiB      |
| memory/performance_schema/events_errors_summary_by_thread_by_error          |           257 | 12.81 MiB     | 51.04 KiB         |        257 | 12.81 MiB  | 51.04 KiB      |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name |             1 | 11.55 MiB     | 11.55 MiB         |          1 | 11.55 MiB  | 11.55 MiB      |
| memory/performance_schema/events_statements_summary_by_digest.digest_text   |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.digest_text        |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
| memory/performance_schema/events_statements_history_long.sql_text           |             1 | 9.77 MiB      | 9.77 MiB          |          1 | 9.77 MiB   | 9.77 MiB       |
+-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+
               total        used        free      shared  buff/cache   available
Mem:           15779       10261        3203           0        2314        5258
Swap:              0           0           0

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql_sandbox8034.sock' (111)
[  954.783966] Out of memory: Killed process 4993 (mysqld) total-vm:34598780kB, anon-rss:15830440kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:31688kB oom_score_adj:0
               total        used        free      shared  buff/cache   available
Mem:           15779         192       15521           0          66       15390
Swap:              0           0           0

Suggested fix:
restrict the amount of memory the data_locks table can use during materialization. The query should fail or memory be reused/spill when it passes a certain threshold - it should not use unlimited memory by default which can lead to crashing the server. 

https://github.com/mysql/mysql-server/blob/8.0/storage/perfschema/pfs_data_lock.cc#L261
[16 Aug 9:00] MySQL Verification Team
Hello Marc Reilly,

Thank you for the report and test case.
Verified as described.

regards,
Umesh
[10 Oct 4:37] MySQL Verification Team
This is duplicate of internally reported Bug 35240825 - performance_schema.data_locks query increased RAM usage to OOM
[21 Nov 10:20] Edward Gilmore
Added the following note to the MySQL Server 8.3.0 release notes:
	
Executing SELECT * from performance_schema.data_locks on a server under heavy
load could cause MySQL to consume too much memory and close unexpectedly.
As of this release, memory used executing such a query is now instrumented with
memory/performance_schema/data_container, enabling you to observe memory consumption.
[21 Nov 16:06] Marc Reilly
Hi,
This bug also affects the 8.0 stream, will it be backported there too?
Thanks,
Marc