Что же мне делать, что же мне делать, чтожемнеделать, через 10 часов самолёт, надо что-то решать :)
Вроде я себя чувствую получше после амоксициллина.

Отличные новости

Похоже, у меня нет метастаз в лёгкие, это просто пневмония :) </sarcasm>

День, когда WordPress подавился метадатой

Сегодняшняя история програмистская. Кому компьютеры скучны, можете смело пропускать.

Как все знают, у меня есть отдельный блог на boku.ru. Записи туда по большей части копируются отсюда – в виде исключения я пару раз выложил там скучные рассказы, чтобы включить их в архив, но не афишировать.

Блог сделан на WordPress. Копирование записей устроено так: специальный скрипт генерирует RSS из дневника на Diary, а плагин FeedWordPress забирает RSS и импортирует в WordPress.

Категории при этом сохраняются, внутренние ссылки мой собственный плагин заменяет на местные, а если пост на дайри изменился – изменяется и пост на boku.ru, так что всё устроено достаточно удобно.

Но есть проблема. (далее)

Код FeedWordPress выглядит примерно так:

function WhatToDo(post)
  localPost = FindLocalCopy(post);
  if localPost==null then
    //New post!
    AddPostMeta(localPost, 'syndication_item_hash', post.Hash);
    return doCreateNewPost;
  else
  if not FindMeta(localPost, 'syndication_item_hash', post.Hash) then
    //Post changed!
    AddPostMeta(localPost, 'syndication_item_hash', post.Hash);
    return doUpdatePost;
  else
    //No changes.
    return doNothing;

Если пост на дайри не менялся, FeedWordPress не будет заново его импортировать и не создаст новой ревизии местного поста. Это хорошо. Но перед тем, как записать пост в базу, WordPress прогоняет его через ряд плагинов, которые меняют его содержание:
Пост на дайри (из RSS) –> (замена ссылок на местные) —-> (исправление форматирования) –> Пост на boku.ru

Бывает, что какой-то из плагинов сбоит, и преобразует пост неправильно. Тогда я начинаю искать, в чём дело. Чтобы разобраться, мне нужно импортировать пост снова и снова, пока я не найду ошибку.

Но как это сделать? Ведь пост уже импортирован, и с точки зрения FeedWordPress, его содержимое не менялось (на дайри он остался тем же).

Для этой цели я влез в файлы FeedWordPress, и временно покромсал описанную выше процедуру. Она стала выглядеть так:

function WhatToDo(post)
  localPost = FindLocalCopy(post);
  if localPost==null then
    //New post!
    AddPostMeta(localPost, 'syndication_item_hash', post.Hash);
    return doCreateNewPost;
  else
  if not FindMeta(localPost, 'syndication_item_hash', post.Hash) then
    //Hack: Post is always changed!
    AddPostMeta(localPost, 'syndication_item_hash', post.Hash);
    return doUpdatePost;
    //TODO: Restore normal version.

Менялся пост или нет, мы всегда импортируем его заново. Конечно, при этом создаётся новая ревизия и захламляется база, но подумаешь, мне же ненадолго… А старые ревизии поста легко удалить.

Поправив таким образом FeedWordPress, я залил новые файлы на сервер и стал искать баг в своих плагинах. И нашёл. Исправил. Убедился, что теперь посты преобразуются правильно. Всё сохранил, применил, закрыл… а отключить хак забыл.

И ушёл.

Вторая половина этой истории началась через месяц, когда я зашёл на блог на boku.ru. Все страницы с последними постами не работали.

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

php error: maximum memory allocation exceeded

Какой-то из скриптов жрёт память? Но почему? Что я менял?

И тут я вспомнил, что забыл отключить хак.

Но постойте, а что такого? Проверки раз в полчаса – это 48 проверок в день, жалкие полторы тысячи ревизий за месяц. WordPress может обслуживать десятки тысяч постов, для MySQL лишние несколько тысяч ревизий – пустяк.

Если я напишу ещё полторы тысячи постов – вордпресс даже не поперхнётся. А полторы тысячи ревизий вывели его из строя?

Да ну! Не так он написан.

Тогда почему любая страница, которая обращается к последним постам – вылетает с переполнением памяти? База данных находится на диске – что вообще вордпресс грузит в память?

Метадату.

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

rows = exec_sql('SELECT * FROM post_metadata WHERE post_id=id');
while rows.MoveNext() do
  metadata[rows['name']]=rows['value']

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

Обычно таких записей 8-10, иногда до 15 – мелочи, в общем.
У последних записей в моём блоге их было по 60 000.

Ничего удивительного, что обращаясь к этим записям, вордпресс падал. Он не рассчитан на 60 000 записей метадаты у поста. Удивительно, откуда эти записи взялись.

Я открыл таблицу phpMyAdmin-ом, и увидел, что все они – это копии параметра syndication_permalink. Тогда всё стало ясно.

Описанная выше функция WhatToDo решает, что делать с постом из RSS – добавить новый, обновить существующий или пропустить. При этом она регистрирует syndication_permalink, чтобы второй раз не обновлять одно и то же.

Да, импорт RSS происходит лишь раз в полчаса, 48 раз в сутки, и каждый пост импортируется лишь однажды – но функция проверки WhatToDo вызывается десятки раз за процедуру одного импорта. Только однажды её результат имеет значение, поэтому ревизий в базе действительно создано лишь полторы тысячи – но при каждом вызове она добавляет syndication_permalink, и этих пермалинков, совершенно одинаковых, у одного поста набираются десятки тысяч.

Ирония: вордпресс мог бы вынести десятки тысяч постов – но не десятки тысяч свойств поста.

Как всё это чинить?
Итак, испорчена таблица post_metadata: в ней для некоторых постов некоторые записи продублированы десятки тысяч раз. Нужно удалить дубли, но оставить по одной копии каждой записи.

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

CREATE TABLE `keep_ids` AS (
  SELECT MIN(`rowid`) AS `rowid` FROM `post_metadata` GROUP BY `postid`, `name`, `value`
)

Этим запросом мы находим все цепочки дублей (записей с одинаковыми данными в полях postid, name и value), и в каждой выбираем наименьший номер записи. Таким образом, мы получаем по одной копии каждой уникальной записи. Эти копии надо сохранить, а всё остальное удалить.

ALTER TABLE `keep_ids` ADD UNIQUE INDEX `rowid` (`rowid`)

Это чтобы операции с новой таблицей были быстрыми – сейчас понадобится.

DELETE FROM `post_metadata` WHERE `rowid` NOT IN (SELECT `rowid` FROM `keep_ids`)

Удаляем все записи из исходной таблицы, которые не вошли в наш “список на сохранение”. Если б в `keep_ids` не было индексов, мы бы тут завязли на несколько минут, а так – только секунд.

Ну и, наконец, удаляем временную таблицу:

DROP TABLE `keep_ids`

Победа! Число записей в post_metadata резко падает с сотен тысяч до 13 000 и блог снова работает нормально.

Названия таблиц и полей в примерах условны, и не соответствуют настоящим названиям в базе вордпресс. Код написан на условном языке, а код SQL может быть не совсем правильным, но передаёт общую мысль.

Ну вот, похоже, девятого числа я еду в Японию. Хотя чувствую себя по-прежнему нехорошо (после последней химии). Надеюсь, всё в ближайшие дни как-нибудь разрешится: либо мне резко станет лучше или хуже, либо придут результаты каких-нибудь анализов, которые любые путешествия исключат, и т.д. и т.п.. А то непонятно как-то.

В Токио я буду неделю, при этом попадаю на летний комикет (10-12) и, возможно, Обон (15 августа, но в Канто 15 июля, короче, я ничего не понял, разберусь ближе к делу).

Надо вспомнить все места, которые я хотел посетить. Подскажите что-нибудь интересное. Пока помню:
– Комикет
– Акибу
– Синдзюку
– Токийскую башню
– Сибую
– Это место где продают отоме-игры (забыл место)
– Посмотреть на Токийский залив
– Обон

Принимаю запросы на сувениры, также на покупки с комикета (поскольку заранее неясно, что будет на комикете, и что я из этого увижу, можно просить и в общих чертах, например, “додзи по ореимо манами х папа кёске”, и тогда я, скрепя сердце, постараюсь найти хоть что-нибудь в таком духе).
Разумеется, нужно уметь как-то у меня это потом забрать. Обычно я в Москве.

Ещё аниме пачками

Kokoro connect – ещё пять школьников открыли кружок времяпровождения.
Binbougami ga! – ещё один сверхъестественный кто-то поселился в доме у кого-то.
Jinrui wa suitai shimashita – что-то из ноитамины, очередной непонятный мир будущего, мало сюжета, все такие необычные девочки, феечки, какая-то соц. сатира (но в сто раз менее едкая, чем в зецубо сенсее), в общем, кому-то может понравиться… скорее, девушкам.
Sword Art Online – чуваки зашли в виртуальную реальность и не могут выйти. Капля интереса в завязке есть, но похоже, впереди просто 13 серий приключенчества.

Получил свою японскую визу

Выглядит как радужная наклейка во всю страницу загранпаспорта.
Заодно сравнил последнее фото с тем, что на загранке (circa 2010). Два года назад я был вежливый молодой человек, а сейчас бандит какой-то лысый :)

Я не уверен!

Попробуйте быстро ответить на вопрос, в ответе на который не уверены до конца:
– Ты дома свет выключил?
– По-моему, да.
Не годится! “По-моему” – это сокращение от “по моему мнению” (“По-моему, Пупкин – плохой президент”), а какое уж тут мнение? Либо выключил, либо нет.

– Кажется, да.
Не годится! “Казаться” могут предметы вдалеке (“Кто это там на эшафоте?” – “Ба! Кажется, Пупкин”), ну в крайнем случае, будущее (“Кажется, Пупкину кранты”), но никак не прошлое! Где это оно “кажется” (показывается)?

– Вроде бы да.
Не годится! “Вроде” значит “похожего рода, сорта” (“Чья это дача?” – “Вроде бы депутата какого-то”). Вы же со светом поступили вполне определённо, либо выключили, либо нет.

– Полагаю, да.
Не годится! Полагают верным то, на чём строят дальнейшие рассуждения (“Полагаю, здесь теперь не искупаться, так что надо искать новый пляж”). А тут речь о ваших сомнениях, а не предположениях.

– Наверное.
Не годится! Какое же это “на верное”, когда вы не уверены? Правильное употребление: “Почему стоим?” – “Наверное, депутата везут.”

– Возможно.
Не годится! А то мы не знали, что это возможно! Правильное употребление: “Возможно, будет революция”.

– Видимо.
Что это вам “видимо”, когда дом далеко-далеко? Правильное употребление: “Что солдаты там делают?” – “Видимо, охраняют мир и порядок.”.

– Скорее всего.
А что, было соревнование в скорости выключения света? Правильно – указать на событие, которое случится прежде любых других: “Скорее всего, президент выступит с заявлением.”.

– Да выключил я, выключил, отвяжись от меня!

К слову о масштабах

Все помнят милую но правдивую шуточку Watch out Diablo III, this game might outsell you?

К слову. Как-то раз Сергей Лукьяненко – тот самый Сергей Лукьяненко, наш самый известный и продаваемый фантаст – упоминал тиражи. В другой раз Борис Акунин – тот самый Борис Акунин, которого читал любой интеллигент – говорил об экспериментах с псевдонимами, и тоже сказал пару слов о продажах. Короче, 400-500 тысяч – это очень неплохой итоговый результат для знаменитого автора в нашей огромной стране.

К настоящему времени было продано 4 миллиона книжек Ореимо.

Остановитесь на секунду и задумайтесь. Это не книги звёзд государственного масштаба, таких, как Акунин или Лукьяненко. Это даже не Харухи, в которой хоть сюрприз фантастический был (Харухи продано 10 миллионов). Мы тут все знаем, что такое Ореимо. Простенькая детская книжка – ну да, тёплая и ламповая. Каждый том этой глупенькой ерунды, которую и за серьёзную литературу-то никто не считает, продавался так же, как очередной “Дозор” Лукьяненко или “Фандорин” Акунина. И так десять томов.

О договорах

Раньше я думал, что я один такой, но похоже, в головах очень многих людей сидит мысль, что договор на бумаге – это формальность, а главное договориться по-человечески. Заказываю у одной фирмы, скажем, покупку и посадку картошки. Посидели, поговорили, выдают мне договор на подписание. Читаю:

Исполнитель обязуется посадить картошку на поле заказчика.

И ни слова про то, откуда он её возьмёт.
Я спрашиваю:

– А почему не сказано про то, что картошку вы купите сами?
– Да-да, купим, мы же договорились.
– Но в договоре об этом не сказано…
– Купим, откуда ещё нам взять картошку?

Приставать к менеджеру стыдно – мы ведь договорились! Получается, я им не доверяю? Я крючкотвор, придираюсь к бумажке?
Но договор и существует на случай, если мы поссоримся. Никому не хочется ссориться, но если слепо верить в чужую порядочность, то договоры вообще не нужны, правильно? Не надо ничего писать – ни про покупку, ни про посадку. А раз уж начали, то дело следует довести до конца. Неправильный договор – хуже, чем вообще никакого.

– Давайте, всё-таки, укажем это в договоре. А то, если мы поссоримся, вы скажете – “кто говорил о покупке? Мы вашу картошку обещали посадить”.
– Хорошо, не проблема. Не будем ссориться.
(сделал) Раньше просто никто не просил такого.

У этой достаточно крупной фермерской фирмы были десятки клиентов. Ни один не прочёл договора и не попросил указать в нём всё правильно? Либо менеджер говорит неправду, либо эти люди – растяпы, такие же, как я (если бы не обдумал это заранее).

Памятка исправляющимся растяпам:
Читайте от начала и до конца каждый договор, который подписываете. Не стесняйтесь потратить чужое время или задержать очередь – в крайнем случае, отойдите в уголок. Всегда просите разъяснения непонятным местам, и если формулировка договора вам не нравится, или вам кажется, что в нём сказано неясно, не то или не всё, о чём договорились, просите договор исправить.
Ни один договор не написан в камне, это всё вордовские документы, и пусть вас не пугает, что слова уже распечатаны: изменить текст и распечатать заново – дело минуты. Не сдавайтесь при словах, что “такой договор подписывают все”: платить за ЭТОТ договор придётся вам, а не “всем”, вы и решаете, что в нём написано.

Если вы и ваш партнёр действительно договорились, нет никаких причин не изменить договор, чтобы прояснить обязанности сторон или описать их полнее.
Отказать вам могут тогда – и только тогда! – когда вас хотят обмануть.

Игра по Back to the Future

Конечно, это фанфик, но очень хороший фанфик. Настолько кинематографичный и похожий на оригинал, что сцены из игры в голове путаются со сценами из фильма. Даже сюжет, который местами выбивается из стилистики, по большому счёту всё-таки “настоящий” – приключенческий и интересный. Не перечесть всех моментов, когда думаешь “чёрт, а ведь это правда могло быть и в фильмах”.

А знакомая музыка и знакомые голоса Марти и Дока (трудно поверить, что их играли другие люди) сшивают любые разрывы и заставляют поверить, что это действительно “Назад в будущее”. Авторы могли ошибиться во стольких местах, но в большинстве не ошиблись.