Bug #119585 In extreme cases, sync_array_get_and_reserve_cell fails to obtain a cell even have space

Submitted: 23 Dec 2025 3:17
Reporter: yuxiang jiang Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:8.0.30/9.4.0/8.4.6 OS:Any
Assigned to: CPU Architecture:Any

[23 Dec 2025 3:17] yuxiang jiang
Description:
In extreme cases, there may be free cells in the sync_wait_array, but the function sync_array_get_and_reserve_cell fails to acquire one, leading to an abnormal crash.

The function sync_array_get_and_reserve_cell calls sync_array_get to obtain an array from the sync_wait_array.

The following code attempts to acquire elements from the sync_wait_array through a loop. However, it cannot guarantee that all elements will be accessed within the loop—some may be accessed multiple times, and in extreme cases, the same element may be retrieved every time:

for (ulint i = 0; i < sync_array_size && *cell == nullptr; ++i) {
    /* Although the sync_array is get in a random way currently,                                      
    we still try at most sync_array_size times, in case any                                           
    of the sync_array we get is full */
    sync_arr = sync_array_get();
    *cell = sync_array_reserve_cell(sync_arr, object, type, location);
}

If the cells of this particular element are exhausted, it will ultimately lead to a crash upon exiting the loop.

How to repeat:
Modify the following function so that default_indexer_t<>::get_rnd_index() always returns a fixed value to simulate the extreme cases.

static inline sync_array_t *sync_array_get() {
  if (sync_array_size <= 1) {
    return (sync_wait_array[0]);
  }

  return (
      sync_wait_array[default_indexer_t<>::get_rnd_index() % sync_array_size]);
}

Suggested fix:
use a random positon and visit all the sync_wait_array element in a loop from this position
[23 Dec 2025 5:43] Knut Anders Hatlen
Thank you for the bug report. The functions you refer to seem to be in the InnoDB code, so I'm changing the bug category to InnoDB.
[13 Jul 23:49] Marcos Albe
Just a bit more detailed explanation:

sync_array_get uses get_rnd_index, which is based on my_timer_cycles, which in turn is based on RDTSC instruction; In older CPU microarchitectures RDTSC would return values that increment by 1 (per-core/per-cycle), but in more modern CPUs it will return (for example) multiples of 32.  This is a problem because when you use modulo on these values, they will split into a limited number of "buckets", which in the case of sync_array_get results in MUCH higher probability of hitting https://bugs.mysql.com/bug.php?id=119567

As I was doing a bit of research I found this interesting reference table for rdtsc increments in different AMD microarchitectures (quoting from https://inria.hal.science/hal-02866777v1/document):
CPU                 μ-arch.                     Increment
=================== =========================== =========
AMD Athlon 64       X2 3800+ K8                         1
AMD Turion II       Neo N40L K10                        1
AMD Phenom II       X6 1055T K10                        1
AMD E-450           Bobcat                              1
AMD Athlon          5350 Jaguar                         1
AMD FX-4100         Bulldozer                           1
AMD FX-8350         Piledriver                          1
AMD A10-7870K       Steamroller                         1
AMD Ryzen           Threadripper 1920X Zen             35
AMD Ryzen           Threadripper 1950X Zen             34
AMD Ryzen           Threadripper 1700X Zen             34
AMD Ryzen           Threadripper 2970WX Zen+           30
AMD Ryzen 7 3700X   Zen 2                              36
AMD EPYC 7401p      Zen                                20
AMD EPYC 7571       Zen                                22

So you can see that the my_timer_cycles will be tied to the CPU architecture.

To "repeat" (or rather to verify the problem is there):
========================================================

Set innodb_sync_array_size=1024 and run some simple by contentious load; For example:

~$ sysbench $auth --db-driver=mysql  --time=0 --rand-type=uniform  --table-size=2 --tables=1 --threads=512 --report-interval=5 --create_secondary=on /usr/share/sysbench/oltp_read_write.lua run;

Then observe the sync arrays via first parameter to sync_array_reserve_cell:
~$  sudo bpftrace -e 'uprobe:/home/kolita/opt/percona/8.4.7/bin/mysqld:_Z23sync_array_reserve_cellP12sync_array_tPvmN2ut8LocationE { printf("arr: %u (%u)\n",arg0,arg0%32); }'
Attaching 1 probe...
arr: 3875288064 (0)
arr: 3869525952 (0)
arr: 3867788288 (0)
arr: 3871258304 (0)
arr: 3868232576 (0)
arr: 3871685504 (0)
arr: 3868664672 (0)
arr: 3874429664 (0)
arr: 3873562496 (0)
arr: 3869959648 (0)
arr: 3867789248 (0)
arr: 3871254784 (0)
arr: 3871255424 (0)
arr: 3869962368 (0)
arr: 3867363424 (0)
arr: 3867364064 (0)
arr: 3873561856 (0)
arr: 3874431744 (0)

Notice how modulo 32 always returns zero, which means only 32 of the 1024 sync array entries will be used.

Fix (?):
=======
Use a different source for get_rnd_index (https://github.com/mysql/mysql-server/blob/845d525d49c8027a4d0cdcc43372c96ba295c857/storag...) so that it doesn't depend on architecture implementations.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.050 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.