среда, 28 ноября 2012 г.

INSERT ... ON DUPLICATE KEY UPDATE deadlock

А вот какие грабли можно получить, если делать множественный INSERT (где перечисляются несколько рядов разом через VALUES) ON DUPLICATE KEY UPDATE.

Случай несколько специфичен. Имеется баннерная система, выдает баннеры, например по 2 шт, при выдаче увеличивает счетчики показов для каждого баннера. Не забывает перемешивать баннеры shuffle-ом перед выдачей (делает это ДО подсчета показов).

При мало-мальски большой нагрузке (10rps) уже проскакивали дедлоки, пара штук в час примерно.
Делаем запрос show engine innodb status; смотрим секцию "LATEST DETECTED DEADLOCK"
Видим там две поссорившиеся транзакции:
*** (1) TRANSACTION:
TRANSACTION 26A4ED2EF, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 5361085, OS thread handle 0x7f6f31c76700, query id 71605993 blabla
INSERT INTO stat (`banner_id`,`dt`,`views`) VALUES (1283, CURDATE(), 1), (1282, CURDATE(), 1) ON DUPLICATE KEY UPDATE views = views + 1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1079 page no 358 n bits 376 index `main_key` of table blabla trx id 26A4ED2EF lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 26A4ED2F0, ACTIVE 0 sec inserting, thread declared inside InnoDB 497
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 5360789, OS thread handle 0x7f6f0fc30700, query id 71605995 blabla
INSERT INTO stat (`banner_id`,`dt`,`views`) VALUES (1282, CURDATE(), 1), (1283, CURDATE(), 1) ON DUPLICATE KEY UPDATE views = views + 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1079 page no 358 n bits 376 index `main_key` of table bla trx id 26A4ED2F0 lock_mode X locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1079 page no 358 n bits 376 index `main_key` of table bla trx id 26A4ED2F0 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (2)
в итоге вторую mysql откатил.
вспоминаем, что banner_id и dt - уникальный ключ, обращаем внимание, что в обоих запросах обновляется по 2 ряда, причем в первом запросе сначала идет баннер 1283 потом 1282. Во втором же запросе сначала баннер 1282 потом 1283.
Вспоминаем про shuffle ДО подсчета показов. При желании материмся. Делаем выводы.

Выхода два: можно подсчитывать показы ДО shuffle, а можно делать отдельные запросы на подсчет для каждого баннера (по-моему так надежнее).

суббота, 20 октября 2012 г.

Обнаружение и разруливание коллизий в сети на протоколе UART

Я долго думал над проблемой объединения кучи микроконтроллеров (МК AVR) в общую сеть. Хотелось иметь простой протокол, в идеале - UART. Но он во-первых не умеет разруливать коллизии, и во-вторых вообще-то даже не предусматривает присутствие нескольких устройств на общей шине!
Но нас такими глупостями не испугаешь.
Поскольку UART мне был нужен также "для самых маленьких" МК (вплоть до tiny13), его пришлось программить самому. Ну а раз пишем сами, то и правила устанавливаем свои :)

Описать сеть я мог бы так: один ПК, много МК на общей шине, полный дуплекс, т.е. отдельные линии RxD и TxD, ПК рассылает на всех разом, МК могут инициировать связь с ПК (но не друг с другом). Схема будет под катом.

В итоге был реализован немного расширенный UART-протокол общения с ПК, который вообще-то полностью совместим с generic-UART, имеет обнаружение коллизий, режим пакетной передачи и позволяет иметь на линии сколь угодно много МК, каждый из которых совершенно свободно может передавать данные в сеть, как только пожелает.
Особенности: никаких арбитров, маркеров, "ответов-только-если-спросили" и прочих ограничений. Абсолютная демократия!
То есть посылаешь в сеть широковещательное "Кто тут?!" и получаешь в ответ аккуратные пакеты, содержащие ответ с адресом каждого МК. Никакой каши.
В виде бонуса, каждому МК можно поставить 3 светика, которые будут отражать соответствующие системные флаги "Линия занята", "Коллизия" и "FRAME ERROR".

Как же я этого добился?!

понедельник, 15 октября 2012 г.

Пишем приложение для Android в Appcelerator (Titanium Studio)

Небольшой tutorial "как писать приложения под Android" от человека, который под телефоны никогда ничего не писал, Java не знает и вообще PHP-разработчик.

PHP-разработчики не всегда знают Java, зато они все должны знать JavaScript. Есть такая штука, Titanium Studio, позволяет писать мобильные приложения на JavaScript. Она еще и бесплатная. Какая удача!

четверг, 11 октября 2012 г.

Не работает CURLOPT_CONNECTTIMEOUT_MS

Очередной квест при работе с curl.
Нужны были таймауты в миллисекундах, версия libcurl 7.21, версия php 5.3, т.е. условиям соответствует.

Но не работает..

тестовый код
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://192.168.0.1/");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 300);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
$res = curl_exec($ch);
var_dump($res, curl_error($ch));
выводит

* Closing connection #0
* Timeout was reached
bool(false)
string(19) "Timeout was reached"

Т.е. даже никаких тебе About to connect и Trying 192.168.0.1, сразу Closing connection, и хоть ты волком вой.
Пробовал переустанавливать libcurl, php5-curl, не помогло.
Полез в гугл по запросу "curlopt_connecttimeout_ms,  timeout was reached" и вышел на спасительный комментарий в мануале:
http://www.php.net/manual/ru/function.curl-setopt.php#104597
The problem is that on (Li|U)nix, when libcurl uses the standard name resolver, a SIGALRM is raised during name resolution which libcurl thinks is the timeout alarm.

т.е. если libcurl использует стандартный name resolver, в процессе резолвинга посылается сигнал SIGALRM, который libcurl принимает за таймаут!

Установка опции nosignal решила проблему.
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);

SOLVED

среда, 3 октября 2012 г.

Интересное поведение mkdir в php

Внезапно обнаружилось, что mkdir не работает с указанием прав так, как от нее ожидается. По крайней мере при запуске скриптов из крона.
Пытаешься указать ей права 770 - папка создается как 750.
Пытаешься 777 - создается как 755. Явно просматривается ограничение по umask.
Но что интересно, chmod() на 770 говорит ОК, следом тут же проверка fileperms() говорит 770, НО по факту в ФС - 755! Ну нихрена себе, мир рушится! Как я теперь могу доверять PHP?!?

В итоге было выяснено, что пых работает с umask 0022.
Вызов umask(0002) перед файловыми операциями привел права в норму, но поведение chmod+fileperms все равно удивляет.

Значит надо запускать php-fpm с явным заданием umask 0002 например.
Однако в дефолтном /etc/bashrc было задано 0002. WTF?!?

Оказалось, что крон просто не цепляет bashrc!

среда, 26 сентября 2012 г.

Сколько ест компьютер

Сегодня скуки ради взял мультиметр, включил его в разрыв шнура питания БП компа и замерил ток. Давно меня интересовала эта цифра.

В биосе график был ровный, в районе 0.35A, во время загрузки - скакало от 0.35 до 0.45 ампер, после загрузки цифра вернулась к значению 0.30-0.32A.

Запустил Red Alert 3, скоро кулеры взвыли, а ток вырос до 0.45-0.55A.

Замеры в ждущем режиме показали 0.03A.

ИТОГО: потребление системника в ждущем режиме - 6.5Вт, а при работе где то 60-120Вт.

Конфигурация:
* Intel SSD SA2M160G2GC
* Intel(R) Core(TM) i5 CPU 760 @ 2.80GHz
* 2x2Gb DDR3-1333
* Asus P7H55-M/USB3
* Простенькое Видео HD6670

Самое главное забыл, БП какой то RealPower CG-400W (400Вт).

UPD: померял ваттметром, 70..80..90..100 при загрузке, около 80 в режиме простоя, 135 в режиме игры, 5 в ждущем.
Ваттметр такой