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, я не вижу этой ошибки. Но это значительно медленнее, так что, может быть, просто пропустить этот крайний случай?
Если кто-то может высказать какие-либо пожелания или мысли по поводу любого из следующих действий, я был бы очень признателен:
- Как я могу найти, откуда это исходит в моем приложении?
- Будет ли выделенный Redis Server потенциально исправить это?
- Должен ли я отправить отчет об ошибке на https://github.com/krakjoe/pthreads?
- Должен ли я отправить отчет об ошибке на https://github.com/nrk/predis?
- Кто-нибудь знает почему
zend_mm_heap corrupted
Может ли вообще возникнуть в потоке "безопасная" среда (он же ZTS)? Или как мне стоит остерегаться этого??? - Стоит ли пытаться скомпилировать еще больше разных версий 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 - это явное подключение / выполнение команд / отключение в текущей локальной области.
Подводя итог, следующее приводит к повреждению памяти при высокой нагрузке:
- Попытка передать клиент phpiredis $ другому объекту по ссылке в поточном контексте
- Попытка передать $ phpiredis-клиент другому объекту по значению в поточном контексте
- Попытка создания экземпляра / использования клиента phpiredis $ в любом месте, кроме локальной области действия в многопоточном контексте (например, в другом классе, _Redis, когда мы в настоящее время находимся в Investor::save)
- Попытка статически повторно использовать соединение 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);