05 сентября, 2014

О кавычках в VARCHAR и о том, как не надо делать в целом

На днях звонят мне наши веб-разработчики и говорят, мол MySQL тормозит адово, сил нет никаких терпеть, а при нескольких одновременных запросах и вовсе транзакции валятся по таймауту.

Привычный осмотр processlist'a показал вполне ожидаемую картину. В таблице чуть более 11 миллионов строк, и уже где-то полминуты висит простенький запрос типа UPDATE some_table SET act_status = 1 WHERE grp_id = 10885882802223, останавливаться он при этом не собирается, а в State красуется "Queried about 5120000 rows".

«Опять индекс не поставили!», - подумал Штирлиц и отправил разработчиков расставлять индексы, попросив попутно не отвлекать более подобными пустяками.

Однако, индекс был. Но всё равно фуллскан. Как так? А очень просто, как оказалось в итоге. В вышеуказанном запросе поле grp_id, если положиться на здравый смысл, должно иметь какой-нибудь целочисленный тип. Но здравый смысл - это для кого-то другого. Тот, кто делал эту прекрасную таблицу, решил что лучше будет, если это поле сделать VARCHAR.

Ну а вишенкой на торте стал самописный недо-ORM, который был написан или тем человеком, который сделал ту прекрасную таблицу, или его сообщником (сообщниками?), ну или сразу группой лиц с особой жестокостью.

Этот самый недо-ORM строил запросы следующим образом: если запрашиваемое значение включало только цифры, то кавычек в запросе не было. Ну а если цифры и буквы, то кавычки заботливо подставлялись.

В результате мы имели то, что имели (проблему с фуллсканом и адовые тормоза).

Смотрим внимательно. Что получалось и как быть не должно, если уж мы имеем дело с VARCHAR:


EXPLAIN select * from some_table where grp_id = 10885882802223 LIMIT 1;
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+
| id   | select_type | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+
|    1 | SIMPLE      | some_table | ALL  | grp           | NULL | NULL    | NULL | 7747809 | Using where |
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+


А вот как надо было:

EXPLAIN select * from some_table where grp_id = '10885882802223' LIMIT 1;
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+
| id   | select_type | table      | type | possible_keys | key   | key_len | ref   | rows | Extra       |
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+
|    1 | SIMPLE      | some_table | ref  | grp           | grp   | 99      | const |    1 | Using where |
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+


Ситуация на самом деле идиотская донельзя и руки бы отрывать за такие изыски. Какие-нибудь еще выводы оставляю право читателю сделать самостоятельно.

18 сентября, 2013

Plesk 11.5, Horde и Integrity check violation

После обновления Plesk до 11.5, веб-интерфейс почты Horde начал себя безобразно вести. То и дело при входе вываливалась ошибка такого вида:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry for key 'PRIMARY'

KB Parallels говорит, что в этом случае нужно установить (либо переустановить) MU #14. Но меня это по какой-то причине не спасло. Вылечить этот косяк удалось путем отключения кэширования Horde, на скорости работы (с почтой через веб-интерфейс работает до 100 пользователей одновременно) никак не сказалось. Возможно, есть более изящное решение, ну а пока в файле /usr/share/psa-horde/config/conf.php находим строку

$conf['cache']['driver'] = 'Sql';

и убираем оттуда Sql, оставив пустые кавычки. Т.е. в итоге должно получиться

$conf['cache']['driver'] = '';

Лично мне помогло, больше меня эти ошибки не тревожили

27 июня, 2013

Реанимируем M-Audio Audiophile USB

Начиналось все тривиально: понадобился мне недавно дополнительный аудиоинтерфейс. Решено было достать с полки второй M-Audio Audiophile USB, который лежал и ждал своего часа, и пустить-таки его в дело. Интерфейс был оперативно найден, не менее оперативно был найден родной блок питания, который выдает не самые стандартные 9 вольт переменного тока на выходе, все это было вместе соединено и подключено к компьютеру иии...

Ничего. Вообще ничего не происходит. На передней панели светится индикатор питания, но компьютер никак не реагирует на подключенный девайс, а ведь когда я его убирал на полку, все было ок. После того, как я перепробовал несколько usb-портов и кабелей, а также попробовал воткнуть карточку в компьютер с Windows, а потом и с Linux, стало ясно, что карточка работать не желает и на контакт ни в какую не идет.

В интернете ничего толком об этом нет, разве что попадаются сообщения о том, что карточки эти иногда вот так "без объявления войны" подыхают. На сайте производителя лишь идиотский FAQ, который заканчивается в моем случае пунктом "Несите туда, где купили", а купил я эту железяку довольно давно, еще и в другой стране, в общем, вариант не подходит.

Сдергиваем кожух и начинаем тыкать в пациента веточкой тестером. Напряжения в норме, +15, -15, +5, +3.3, предохранители целы, даже кварц заводится, но по USB никаких событий не обнаруживается.

Однако, мое внимание привлекла небольшая микросхема в "кроватке". Собственно, это EEPROM данной железяки. Благодаря удачному стечению обстоятельств, у меня под рукой был еще один, заведомо рабочий, Audiophile USB. Переставляю микросхему EEPROM из рабочей карточки в нерабочую, и последняя внезапно оживает.

Разработчики карты не стали извращаться и применили вполне стандартную микруху 24C64, за что им спасибо, т.к. она замечательно шьется с программатора типа PICLIGHT-2, который аналогично моей звуковушке лежал у моего друга и тоже ждал того момента, когда он будет так нужен.


Дальше все было просто - скачали прошивку с рабочего экземпляра, залили в нерабочий, и все заработало сразу. Использовали программу IC-Prog 1.06, чтение и запись прошивки удались с первого раза без каких-либо трудностей.

Знаю, что не у каждого, кто окажется в подобной ситуации, будет под рукой еще один интерфейс, с которого можно стянуть прошивку, поэтому заботливо выкладываю ее здесь:

M-Audio USB Audiophile EEPROM dump (23053 bytes)
md5: 77df950625700cf7980e0beb911d5311

21 апреля, 2013

ControllerMate, ремап регулировки громкости

Уже не первый год в качестве звуковой карты я использую уже не самый интересный по нынешним меркам интерфейс Audiophile USB от M-Audio. Он меня более, чем устраивает по всем статьям, поэтому при переходе на Mac я и не думал расставаться с этой железкой.

Однако, меня поджидал неприятный сюрприз: драйверы для этой карточки под OS X не используют системный микшер, следовательно громкость соответствующими клавишами на клавиатуре не регулируется, мол, в вашей карточке нечего крутить. На сайте M-Audio об этом указано, а в качестве решения предлагают крутить либо громкость в приложении, либо регулятор на передней панели аудиоинтерфейса.

Крутить регулятор не хотелось, т.к. тянуться каждый раз за этим откровенно лень. А вот регулировать громкость плеера при помощи штатных мультимедиа-клавиш - вполне адекватное решение.

И хотя я уже знал про существование такого чудо-приложения, как ControllerMate, провернуть описанный выше фокус удалось не сразу. Возможно, сказывается то, что с OS X я до этого толком не работал, а возможно - тот факт, что редко кто не из музыкантов и им сочувствующих пользуется внешней звуковой картой, а уж тем более с маком.

Итак, открываем ControllerMate.

Для начала я убрал действия для клавиш регулировки громкости, чтобы больше не выводилось уведомление о том, что регулировать нечего. На функционал не влияет, сделано сугубо в эстетических соображениях.

Нажимаем "Create Driver Configuration", выбираем нашу клавиатуру (в моем случае используется клавиатура от Logitech)". Хотя в моем случае почему-то выбрать надо было мышь. Это не совсем очевидно, согласен.


Находим пункты "Volume Increment" и "Volume Decrement", меняем поведение с Default на None.

После этого создаем новую группу для нашей клавиатуры (или пишем новые правила в существующую, если у вас уже есть пресеты в ControllerMate).

В качестве плеера я использую Swinsian,  и у него нет шорткатов для регулировки громкости. Конечно, их можно создать через System Preferences, но зачем плодить сущности? При нажатии на клавишу эмулировать нажатие других клавиш, чтобы отрегулировать громкость - это как-то громоздко. Правда, команд "сделать громче/тише" для AppleScript у него тоже нет, только возможность задания значений от 0 до 100. Возиться со счетчиками не хотелось, поэтому почему бы не эмулировать нажатие соответствующего пункта в меню плеера?

Создаем внутри группы пресетов для нашей клавиатуры еще одну группу, я назвал ее аналогично названию плеера,  указывем, что группа будет активна, когда запущен плеер, при этом не забываем справа от названия плеера поменять параметр на "Foreground or Background", иначе ничего не будет регулироваться, если окно плеера не является активным.


Внутри группы создаем новую страницу, добавляем новый блок:

В окне инспектора выбираем нужную нам клавишу (точнее, достаточно один раз на нее нажать и она выберется сама). После этого добавляем новый блок, на этот раз выбираем "AppleScript - AppleScript". Чтобы не плодить файлы, выбираем, что скрипт храниться будет тут же, текст скрипта для Swinsian будет таким:

tell application "System Events" to tell process "Swinsian"
         click menu item "Increase Volume" of menu "Controls" of menu bar 1
end tell

После этого связываем созданные блоки, проделываем аналогичную процедуру для кнопки увеличения громкости, заменив Decrease на Increase соответственно.

Кстати, на кнопки "следующий/предыдущий трек" у меня плеер тоже не реагировал, поэтому пришлось создать еще правила для них, но там все проще, благо у плеера есть команды смены трека.

Надеюсь, объяснение получилось не очень путаным.
Готовый файл конфигурации для ControllerMate можно скачать здесь.

А вообще, всем пользователям OS X, еще не знакомым с AppleScript и Automator рекомендую обратить внимание на два этих прекрасных инструмента, позволяющих автоматизировать, наверное, даже самые нетривиальные процедуры в системе.

28 марта, 2013

plesk, spamassasin и нестандартная директория для почты

Этот plesk у меня никогда не закончится.

Когда я устанавливал панель (предварительно ознакомившись с документацией), из этой самой документации я не совсем правильно понял фразу
if you want to use a separate partition for Panel, mount the partition to /opt/psa/
Понадеялся, что все, что относится к плеску, будет установлено туда. Разметку диска производил соответственно. Но оказалось, что, например, почту он все равно собирается хранить в привычной директории /var/qmail/mailnames (даже если при установке мы выбрали postfix и никакого qmail'a у нас нет). Переустанавливать и переразмечать все это хозяйство очень не хотелось, т.к. это требовало еще и поездки в дата-центр (сервер не был подключен к IP-KVM, а IMM у него был без лицензии на эту фичу). Путем недолгого гугления нашлась вот эта статья на сайте Parallels. Немного смутило то, что она для 9 и 10 версий, но про 11 ничего найти не удалось.

Выполнил все, как было написано в статье, после чего смигрировал аккаунты на этот сервер. Все хорошо, почта лежит там, где я это указал в /etc/psa/psa.conf и ничего не предвещает беды.

Но когда дело дошло до spamassasin, все пошло не так. Суть в том, что вроде он и работает, но явный спам проскакивает во входящие, так к нему еще и почему-то не добавляются заголовки от SA. При этом заголовки вполне себе присутствуют в легитимных письмах.

В логах нашлось следующее:
spam_hook[32577]: Exiting with exit code: 75
spam_hook[32577]: Unable to delivery message into Spam folder

Как это? Почему нельзя? Непонятно. Директория есть, права есть, все вроде хорошо.
На директиву save в user_prefs, кстати, spamassasin не реагировал.

Тут-то я и вспомнил о перемещении Maildir'ов, о котором писал в начале.
В результате проблему удалось решить одной короткой командой:
ranzhe@plesk:~$ sudo ln -s /opt/psa/mail/mailnames /var/qmail/mailnames

Т.е. где-то в хуке для spamassasin'a безобразно захардкодили путь, из-за чего оно и не работало.
Вот и верь после этого документации.

26 марта, 2013

И снова plesk, теперь file (fail?) sharing

В общем-то, я наверное даже готов поверить в то, что все мои проблемы с плеском - следствие миграции с 9.5.4 на 11.0.9, но на то были свои причины и выбора особо не было.

Через некоторое время после миграции начала вылезать неприятная (и непонятная, впрочем, тоже) ошибка: при попытке сменить пароль на каком-либо из смигрированных почтовых аккаунтов, панель ругалась примерно так:

unable to execute file-sharing : empty error message from utility

Но вот ведь какое дело: на старом сервере не было никакого файл-шаринга, да и не нужен он и на новом, но попытки отключить его путем снятия соответствующих галок в настройках панели (Server - File Sharing - Enable Public Files) заканчивались тем, что галки эти после перезагрузки страницы опять возвращались на место.

Попытки же просто открыть одноименный раздел в панели заканчивались сообщениями с ошибкой, мол, не хватает у меня прав для подобных дел (учетка, естественно, администее некуда).

Да и даже на саму ошибку можно было бы не обращать внимания, если бы при этом пароль иногда не изменялся-таки на новый, что действительно уже стало неприятным.

Решилась проблема следующим образом:

ranzhe@plesk:~$ sudo /opt/psa/admin/sbin/file-sharing --sync

Точнее, не совсем. При попытке выполнить этот самый синк (а делается это php-скриптом), php отваливался из-за превышения лимита памяти, мол 128 мегабайт не хватает. Но чего не сделаешь ради хорошо дела? Вот только оказывается, что выполняется это все не просто через php-cli, конфиг которого в дебиане/убунте лежит в привычном /etc/php5/cli/php.ini, а через sw-engine-cgi, у которого свой php.ini, и путь к нему такой:

/opt/psa/admin/conf/php.ini

После увеличения лимита памяти sync отработал без ошибок, и file sharing перестал ругаться на то, что я "недостаточно прав", и вообще позволил наконец-таки отключить себя. Больше он меня не беспокоил.

17 марта, 2013

Легкий способ сделать SRS в postfix

В наше время все чаще встречаются почтовые серверы, проверяющие SPF очень строго. С учетом количества спама, гуляющего по сети, администраторов этих серверов можно понять. Но  можно столкнуться с такой ситуацией: письмо поступает с mail1@example.com на mail2@example2.com, проходит все проверки, и все хорошо. Но на сервере example2.com стоит правило "форварднуть письмо на mail3@domain3.com". Сервер example2.com начинает пересылку, но сервер domain3.com видит, что письмо от mail1@example.com пришло от сервера example2.com, который, по результатам проверки SPF, не имеет никакого права слать такую почту.

Для исправления ситуации существует SRS (Sender Rewrite Scheme). И мой любимый exim умеет это из коробки, а вот postfix - нет. Но иногда эксим прикрутить нельзя (например, у нас панель управления, которая работает только с постфиксом). Можно, конечно, написать rewrite map, и переписывать FROM на сервере example2.com, но если у нас тысяча с лишним ящиков, что делать?

Реализаций SRS для postfix существует великое множество, но многие из них требуют патча исходников либо достаточно сложны в прикручивании к уже имеющейся системе.

Но есть простое решение, появившееся не так давно. Называется postsrsd.

Тем, у кого используется ubuntu server, достаточно сделать следующее:

add-apt-repository ppa:roehling/stable
aptitude update
aptitude install postsrsd

Остальные могут сходить на github и забрать исходники для самостоятельной сборки

Дальейшая настройка предельно проста. При установке генерируется ключ, нужный для подписания писем, которые мы будем форвардить. Остальные настройки есть в /etc/default/postsrsd. Мне понадобилось сменить порты, которые слушает демон, т.к. дефолтные уже были заняты.

В конфиг постфикса пишем следующее:

sender_canonical_maps = tcp:127.0.0.1:10001
sender_canonical_classes = envelope_sender
recipient_canonical_maps = tcp:127.0.0.1:10002
recipient_canonical_classes= envelope_recipient

После этого убеждаемся, что postsrsd запущен и релоадим postfix. Готово.