PHP + PThreads + Redis/Predis = zend_mm_heap повреждены?

Я боролся с загадочной ошибкой уже несколько дней. Я использую PHP 7.1.0RC3 (я перекомпилировал себя с включенным ZTS/pthreads). Недавно я работал над рефакторингом, который заменил MySQL на Redis, чтобы оптимизировать ввод-вывод данных, не пригодный для работы с дисками, в моем приложении.

У меня есть скрипт, который создает поток (A) для каждого рынка криптовалют. Нить (A) создает другую Нить (B) для каждой торговой стратегии. B Темы всегда синхронизируются перед тем.

Я продолжаю получать эту ошибку: zend_mm_heap corrupted, Это происходит на разных этапах выполнения каждый раз, когда я запускаю скрипт.

Я перепробовал все предложенные исправления, сотни страниц Google. Сборка мусора, конфигурация / компиляция PHP, все было подробно рассмотрено. Я не нашел ничего, связанного с "zend_mm_heap поврежден", PThreads и Redis/Predis, в то время как другие "исправления" не имели никакого эффекта...

Все данные считываются и сохраняются в Redis (через Predis). Сообщение об ошибке появляется только при использовании Predis в потоке A или в потоке B (изменяется каждый раз). Это происходит чаще, когда два вызова метода Predis происходят в быстрой последовательности (например, set, get).

Если я использую shell_exec() чтобы запускать команды Redis напрямую через CLI, я не вижу этой ошибки. Но это значительно медленнее, так что, может быть, просто пропустить этот крайний случай?

Если кто-то может высказать какие-либо пожелания или мысли по поводу любого из следующих действий, я был бы очень признателен:

  1. Как я могу найти, откуда это исходит в моем приложении?
  2. Будет ли выделенный Redis Server потенциально исправить это?
  3. Должен ли я отправить отчет об ошибке на https://github.com/krakjoe/pthreads?
  4. Должен ли я отправить отчет об ошибке на https://github.com/nrk/predis?
  5. Кто-нибудь знает почему zend_mm_heap corrupted Может ли вообще возникнуть в потоке "безопасная" среда (он же ZTS)? Или как мне стоит остерегаться этого???
  6. Стоит ли пытаться скомпилировать еще больше разных версий PHP 7+? https://github.com/php/php-src

У меня нет выбора, пожалуйста, помилуй. Я могу поделиться примерами кода, но вставлять их здесь было бы не очень полезно.

Изменить Я перекомпилировал PHP с ./configure --enable-maintainer-zts --enable-pthreads --with-pthreads-sanitize --with-mysqli --enable-embedded-mysqli и я увидел новую ошибку, которую я не видел раньше:

==2716== ERROR: AddressSanitizer: heap-use-after-free on address 0x600800001c15 at pc 0xe25a87 bp 0x7f7a049feae0 sp 0x7f7a049fead0
READ of size 1 at 0x600800001c15 thread T1632
    #0 0xe25a86 (/usr/local/bin/php+0xe25a86)
    #1 0xe953c8 (/usr/local/bin/php+0xe953c8)
    #2 0xe4935b (/usr/local/bin/php+0xe4935b)
    #3 0xcbede3 (/usr/local/bin/php+0xcbede3)
    #4 0x98cfa9 (/usr/local/bin/php+0x98cfa9)
    #5 0x99dc3c (/usr/local/bin/php+0x99dc3c)
    #6 0x7f7b57e74a97 (/usr/lib64/libasan.so.0.0.0+0x19a97)
    #7 0x7f7b5694edc4 (/usr/lib64/libpthread-2.17.so+0x7dc4)
    #8 0x7f7b5667d73c (/usr/lib64/libc-2.17.so+0xf773c)
0x600800001c15 is located 5 bytes inside of 48-byte region [0x600800001c10,0x600800001c40)
freed by thread T1623 here:
    #0 0x7f7b57e71009 (/usr/lib64/libasan.so.0.0.0+0x16009)
    #1 0xe259fb (/usr/local/bin/php+0xe259fb)
    #2 0xe953c8 (/usr/local/bin/php+0xe953c8)
    #3 0xe4935b (/usr/local/bin/php+0xe4935b)
    #4 0xcbede3 (/usr/local/bin/php+0xcbede3)
    #5 0x98cfa9 (/usr/local/bin/php+0x98cfa9)
    #6 0x99dc3c (/usr/local/bin/php+0x99dc3c)
    #7 0x7f7b57e74a97 (/usr/lib64/libasan.so.0.0.0+0x19a97)
previously allocated by thread T0 here:
    #0 0x7f7b57e71129 (/usr/lib64/libasan.so.0.0.0+0x16129)
    #1 0xdb0118 (/usr/local/bin/php+0xdb0118)
    #2 0xe64f6c (/usr/local/bin/php+0xe64f6c)
    #3 0xe68c5d (/usr/local/bin/php+0xe68c5d)
    #4 0xcc4dcc (/usr/local/bin/php+0xcc4dcc)
    #5 0xcc5972 (/usr/local/bin/php+0xcc5972)
    #6 0x111f82c (/usr/local/bin/php+0x111f82c)
    #7 0x44c781 (/usr/local/bin/php+0x44c781)
    #8 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34)
Thread T1632 created by T0 here:
    #0 0x7f7b57e65c3a (/usr/lib64/libasan.so.0.0.0+0xac3a)
    #1 0x99e4cb (/usr/local/bin/php+0x99e4cb)
    #2 0x975d2a (/usr/local/bin/php+0x975d2a)
    #3 0x11105c1 (/usr/local/bin/php+0x11105c1)
    #4 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #5 0x97b385 (/usr/local/bin/php+0x97b385)
    #6 0x11126bc (/usr/local/bin/php+0x11126bc)
    #7 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #8 0x97b385 (/usr/local/bin/php+0x97b385)
    #9 0x11126bc (/usr/local/bin/php+0x11126bc)
    #10 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #11 0x97b385 (/usr/local/bin/php+0x97b385)
    #12 0x11126bc (/usr/local/bin/php+0x11126bc)
    #13 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #14 0x97b385 (/usr/local/bin/php+0x97b385)
    #15 0x11126bc (/usr/local/bin/php+0x11126bc)
    #16 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #17 0x97b385 (/usr/local/bin/php+0x97b385)
    #18 0x1110b28 (/usr/local/bin/php+0x1110b28)
    #19 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #20 0x97b385 (/usr/local/bin/php+0x97b385)
    #21 0x11126bc (/usr/local/bin/php+0x11126bc)
    #22 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #23 0x97b385 (/usr/local/bin/php+0x97b385)
    #24 0x111c28a (/usr/local/bin/php+0x111c28a)
    #25 0xe52281 (/usr/local/bin/php+0xe52281)
    #26 0xcc766f (/usr/local/bin/php+0xcc766f)
    #27 0x1121d36 (/usr/local/bin/php+0x1121d36)
    #28 0x44d202 (/usr/local/bin/php+0x44d202)
    #29 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34)
Thread T1623 created by T0 here:
    #0 0x7f7b57e65c3a (/usr/lib64/libasan.so.0.0.0+0xac3a)
    #1 0x99e4cb (/usr/local/bin/php+0x99e4cb)
    #2 0x975d2a (/usr/local/bin/php+0x975d2a)
    #3 0x11105c1 (/usr/local/bin/php+0x11105c1)
    #4 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #5 0x97b385 (/usr/local/bin/php+0x97b385)
    #6 0x11126bc (/usr/local/bin/php+0x11126bc)
    #7 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #8 0x97b385 (/usr/local/bin/php+0x97b385)
    #9 0x11126bc (/usr/local/bin/php+0x11126bc)
    #10 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #11 0x97b385 (/usr/local/bin/php+0x97b385)
    #12 0x11126bc (/usr/local/bin/php+0x11126bc)
    #13 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #14 0x97b385 (/usr/local/bin/php+0x97b385)
    #15 0x11126bc (/usr/local/bin/php+0x11126bc)
    #16 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #17 0x97b385 (/usr/local/bin/php+0x97b385)
    #18 0x1110b28 (/usr/local/bin/php+0x1110b28)
    #19 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #20 0x97b385 (/usr/local/bin/php+0x97b385)
    #21 0x11126bc (/usr/local/bin/php+0x11126bc)
    #22 0xf71de2 (/usr/local/bin/php+0xf71de2)
    #23 0x97b385 (/usr/local/bin/php+0x97b385)
    #24 0x111c28a (/usr/local/bin/php+0x111c28a)
    #25 0xe52281 (/usr/local/bin/php+0xe52281)
    #26 0xcc766f (/usr/local/bin/php+0xcc766f)
    #27 0x1121d36 (/usr/local/bin/php+0x1121d36)
    #28 0x44d202 (/usr/local/bin/php+0x44d202)
    #29 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34)
Shadow bytes around the buggy address:
  0x0c017fff8330: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa
  0x0c017fff8340: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x0c017fff8350: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00
  0x0c017fff8360: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa
  0x0c017fff8370: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00
=>0x0c017fff8380: fa fa[fd]fd fd fd fd fd fa fa 00 00 00 00 00 00
  0x0c017fff8390: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c017fff83a0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c017fff83b0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c017fff83c0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa
  0x0c017fff83d0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==2716== ABORTING

1 ответ

Я обнаружил источник повреждения памяти, и "исправить" было очень просто. Я обнаружил, что единственный "нефатальный" способ использования клиента phpiredis $ в pthreads - это явное подключение / выполнение команд / отключение в текущей локальной области.

Подводя итог, следующее приводит к повреждению памяти при высокой нагрузке:

  1. Попытка передать клиент phpiredis $ другому объекту по ссылке в поточном контексте
  2. Попытка передать $ phpiredis-клиент другому объекту по значению в поточном контексте
  3. Попытка создания экземпляра / использования клиента phpiredis $ в любом месте, кроме локальной области действия в многопоточном контексте (например, в другом классе, _Redis, когда мы в настоящее время находимся в Investor::save)
  4. Попытка статически повторно использовать соединение phpiredis вообще в многопоточном контексте

Повреждение памяти не происходит при передаче клиента MySQLi $ другому объекту тем же способом / контекстом, хотя.

Я надеюсь, что это спасет кого-то еще от недели отчаяния, свидетелем которой я был. Вот как нужно использовать phpiredis в pthreads:

// Instantiate a local raw phpiredis $client and execute get/set commands using $client directly
// @Result: This WORKS :D
$client = phpiredis_connect('127.0.0.1', 6379);
$key = "table_name-_-$this->id";
foreach($args as $prop=>$val)
    if(isset($prop) && isset($val))
        phpiredis_command($client, "hset $key $prop $val");
$obj = phpiredis_command($client, "hgetall $key");
echo json_encode($obj);
phpiredis_disconnect($client);
Другие вопросы по тегам