shoorick: (Рыжий)
После нескольких неудачных попыток прилогиниться к сайту, сделанному на друпале 7.15, пользователь натыкается на сообщение:
Sorry, there have been more than 5 failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.
Насколько позже стóит повторить попытку — непонятно.

Полез в БД — в таблице `users` никакой информации об активности пользователей. А вот в таблице `flood` — есть: в поле `event` — название события, `identifier` — идентификатор: IP либо строка вида UID-IP для попыток залогиниться, если логин существует, `timestamp` и `expiration` — время попытки входа и истечения бана (в секундах с начала эпохи). Продолжительность бана — 3600 секунд (час) для случаев, когда UID не указан и 21600 секунд (6 часов) — для пары UID-IP.

Если удалить из таблицы `flood` лишние записи — вход открывается.

P. S. Узнать текущее время в секундах с начала эпохи в юниксоподобных операционных системах можно командой
date +%s
в MySQL для этого есть функция UNIX_TIMESTAMP.
shoorick: (Default)
Настроил себе доступ к серверам с помощию SSH-ключей — жить стало легче.

Можно не только делать дампы баз, как описано в статье, но и проводить обратную операцию: заливать дампы в базу:
zcat some-dump.sql.gz | ssh hostname 'mysql -pPa$$w0rd database_name'
shoorick: (Default)
Странности творятся. Почитав мануал, обращаюсь к друпаловой БД, ищу дубликаты адресов в таблице с 7500 строками:
SELECT `alias`
FROM `PREFIX_url_alias`
GROUP BY `language`, `alias`
HAVING COUNT(*)>1;
MySQL достаточно резво выдаёт ответ из почти семи десятков строк. Делаю запрос с подзапросом, чтоб выбрать номера узлов с найденными на предыдущем шаге дублями:
SELECT `source`
FROM `PREFIX_url_alias`
WHERE `alias` IN (
    SELECT `alias`
    FROM `PREFIX_url_alias`
    GROUP BY `language`, `alias`
    HAVING COUNT(*)>1
);
В итоге MySQL тяжко задумывается — вот уж 20 минут непонятно, что делает. Посмотрел SHOW CREATE TABLE — по полю `alias` строится индекс. Чё ему ещё надо-то?

P. S. Понятно, что я могу найденное на первом шаге тупо перебрать в цикле, написав какой-нибудь перлоскрипт, однако хочется сделать как-то изящнее.
shoorick: (Default)
Похоже, в друпале придётся применять тот же способ, как и в одном из предшествующих сайтов — отказаться от всяческих абстракций в пользу чистого SQL. Если, конечно, он предусмотрен.

Поглядев статистику потраченного друпалом времени и количества запросов к СУБД, офигел весь был неприятно удивлён: на формирование страницы со списком подмножества опубликованных статей друпал потратил от 100 до 11 тысяч запросов, что заняло от 2 секунд до более чем полутора минут. Это как так?

Я, конечно, понимаю, что время программиста сейчас ценится выше машинного времени плюс друпалу на отрисовку страницы ещё нужны кое-какие ресурсы, но это не может служить оправданием того безобразия, что я наблюдаю: сидя дома, не имея под рукой эпического полотна со структурой хотя бы части БД, я минут за 10 написал и отладил SQL-запрос, который формирует тот же самый список статей и при этом выполнение запроса укладывается в десятки миллисекунд. На то, чтобы понять, как работают Views, и поэкспериментировать с ними, я потратил часы и дни. И всё равно толком не понял.
shoorick: (Default)
Видимо, я ничё не понимаю в ORM вообще и DBIx::Class в частности.
Элементарная для чистого SQL задача: вывести отсортированный по алфавиту список людей может решаться, например, таким запросом:
SELECT CONCAT_WS(' ', lname, fname, sname) AS name
FROM user
ORDER BY name;
Попытки перевести этот запрос на DBIx::Class — сплошь и рядом безуспешные, не помогает ни бубен, ни чтение мануала. Точнее, чтение мануала помогло лишь в одном, понять, что атрибут as ничего не делает. Гугление тоже не особо помогает... По каким принципам в DBIx::Class используются всякие \, [] и {} — тоже не ясно, складывается ощущение, что как попало.

upd/16:50: Из всех методов один даёт почти такой запрос, какой надо. Но как странно это выглядит!
$c->model('Dbase::User')->search(
    {},
    {
        'select'   => [ { '' => \q{CONCAT_WS(' ', lname, fname, sname)}, -as => 'name' } ],
        'order_by' => [ 'name' ],
    }
)
upd/вечернее: Атрибут as всё-таки нужен. Но не для влияния на SQL (он на него не влияет), а для явного указания дбикс-классу имени нового поля.
'select' => [ 'id', { '' => \q{CONCAT_WS(' ', lname, fname, sname)}, -as => 'name' } ],
'as'     => [qw( id name )],
Если его не указать, то появляются сообщение об ошибке: «Метод name не существует».
shoorick: (Default)
Ускоряем каталистовый сайт, переписываем слишком медленные запросы с DBIx::Class на обычный DBI. Попутно допилил напильником каталистовую модель, чтобы она поддерживала UTF-8 в MySQL, о чём и написал в [livejournal.com profile] ru_catalyst. Кому надо — пользуйтесь.
shoorick: (Default)
Обнаружил, что MySQL совершенно спокойно относится к тому, что у него не хватает полей в служебных таблицах mysql.db и mysql.user для некоторых сравнительно новых действий (в мануале список гораздо обширнее фактического). Ну нет — и нет, чё юзера пугать? Видимо, авторы MySQL считают, что сказать юзеру обычное Query OK, 0 rows affected (0.00 sec), когда фактически нужное действие не выполнено — это норма. Плохо.

Конечно, можно привести структуру к требуемой операторами типа
ALTER TABLE db
ADD `Create_view_priv` enum('N','Y') NOT NULL default 'N'
AFTER `Grant_priv`;
но это как-то неправильно — должна же существовать удобная процедура миграции наверное, мы mysql_upgrade забыли выполнить, когда переезжали с 3.23 на 5.0.

upd/17:50: Действительно, забыли когда-то выполнить mysql_upgrade — после его выполнения, сопровождающегося перезапуском сервера и перезагрузкой таблиц привилегий (mysqladmin reload) всё заработало.
shoorick: (Default)
Перенос проекта из трака в редмайн 0.9.* работал. Хоть и не в полном объёме, конечно же, но Redmine после такой операции спокойно мог работать с новыми данными. Сейчас, в версии 1.0.2, такой фокус не проходит: редмайн ругается при попытке открыть импортированную задачу или список, где такие задачи присутствуют. При этом другие задачи (как и списки из соседних проектов) отображаются нормально.

Пошерстил форум на сайте разработчика, нашёл лишь рекомендации запускать скрипты миграции и перезапускать сервер. Но это относилось всё-таки к процедуре обновления редмайна, а не импорта данных в него. Написал о проблеме, а потом сам же нашёл причину: скрипт миграции не заполнял поля lft и rgt в таблице issues. Заполнил поля вручную — заработало.

Ой

Dec. 7th, 2009 05:45 pm
shoorick: (Default)
Прочитал про отладку в DBIx::Class, включил вывод запросов в лог — ужасаюсь: на отображение одной странички ушло 140 (сто сорок!) запросов (среди них — немало повторяющихся). Даже если СУБД и выдаёт результаты из кэша — это всё равно слишком много. Я, конечно, понимаю, что использование ORM может подразумевать некоторое увеличение использования машинных ресурсов в обмен на экономию времени программиста. Но не до такой же степени!
shoorick: (Default)
DBDesigner теперь переименовался в MySQL Workbench, он бесплатный и кроссплатформенный, но самостоятельно соединяться с БД через SSH-туннель пока не научился. Туннель можно прорыть вручную:
ssh -f -L 3307:database.server:3306 gateway.user@gateway.server sleep 60
После чего MySQL Workbench, если в параметрах соединения с БД ему указать Hostname: localhost и Port: 3307, успешно соединяется с базой.

Но вот консольный клиент соединяться не желает:
mysql -P 3307 -u database.user -p database.name
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
upd/12:51:Если создать сокет вручную:
perl -MIO::Socket -e 'IO::Socket::UNIX->new(Local=>q{/tmp/mysql.3307.sock})'
и указать клиенту флагом -S, что хотим соединятьс именно через этот сокет, он всё равно не соединяется. Меняется лишь код ошибки — (61) вместо (2)

upd/13:17: Если клиенту указать адрес хоста как -h 127.0.0.1 — он успешно соединяется.
shoorick: (Default)
Меняю структуру одной из таблиц. MySQL ругается:
ERROR 1005 (HY000) at line 1: Can't create table './name/#sql-540_37a.frm' (errno: 121)
Оказалось, что причина — в попытке создать индекс с именем уже существующего — в MySQL у индексов имена должны быть уникальными в пределах БД.
shoorick: (Default)
Разгребал старые тикеты от разрабатываемого на Catalyst + DBIx::Class проекта, наткнулся на один из: в БД не вставляются файлы, размером больше, чем @@max_allowed_packet (а, наверное, и ещё меньше; в моём случае максимальный размер — около мегабайта), а @@max_allowed_packet на ходу меняться не хочет. Гуглил методы обхода или, например, готовые решения вставки по кускам (ибо самому писать слегка лениво) — в итоге напоролся на DBIx::Class::InflateColumn::File: хранение файлов в файловой системе с доступом к ним через DBIx::Class. Удивительно...
shoorick: (Default)
  1. У компании EMS, разработчика клиентского софта для ковыряний во всяких БД вообще и MySQL в частности, грамотный и внятный саппорт (кстати, они поддерживают и пользователей триальных версий своего софта, на время действия испытательного срока), за что им всем зур рахмат, респект и уважуха. Саппорт, вроде, многоязычен, но мне, естественно, нужен был только русский, что я и получил: в одном городе живём...
  2. Чтоб не забыть: кодировку данных в каталисте можно переключить, например, так:
    __PACKAGE__->config(
        schema_class => Dbase,
        connect_info => [
            dbi:mysql:db:host,
            user,
            password,
            { AutoCommit => 1 },
            {
                on_connect_do => [
                    SET NAMES utf8,
                ],
            }
        ],
    );
shoorick: (Default)
Ковыряться в тонне однотипных исходников, пытаясь в редакторе найти нужный кусок и заменить на что-то новое — не наш метод. Наш метод:
sed -e "s/Page/Teacher/g" -e "s/page/teacher/g" -e "s/Subject/Dept/g" -e "s/subject/dept/g" PageSubject.pm > TeacherDept.pm
Хотя подозреваю, что и это — не самый оптимальный из возможных путей.

upd:Таки да, есть ещё более весёлый метод: запустить helper, который сразу генерит всю толпу модулей. Например, так:
script/app_create.pl model Dbase DBIC::Schema Dbase create=static dbi:mysql:БД[:хост] логин пароль
shoorick: (Default)
Чтоб самому потом не забыть:
SELECT *,(d2>=d3 AND d1<=d4) AS cmp FROM `test_ival`
Возвращает 1 в случае пересечения диапазонов [d1; d2] c [d3; d4] и 0 в противном случае.
Если вместо >= и <= использовать соответственно > и <, получится проверка пересечения (d1; d2) c (d3; d4).
shoorick: (Default)
Кэшировать сложный SQL-запрос путём засовывания результата в очередную таблицу, откуда выдирать его путём простого запроса нифига не выгодно — скорость чтения обычного файла средствами PHP примерно на два порядка выше скорости выполнения простенького селекта типа
SELECT * FROM `ej_cache` WHERE idissue=1 AND idlang=1
Абыдна!
shoorick: (Default)
Скормил SQL-ному серваку не тот файл.
Два часа работы псу под хвост :-(

upd: по-стахановски восстановил всё убитое за 20 минут.
shoorick: (Default)
Читал доку по MySQL, опять напоролся на описание полнотекстовой индексации и одноимённого поиска.
Попробовал. Получилось.

Давно пора было...

Profile

shoorick: (Default)
shoorick

December 2016

S M T W T F S
    1 23
45678910
11121314151617
18 19 2021222324
25262728293031

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 13th, 2025 11:36 pm
Powered by Dreamwidth Studios