Код импорта переписан, обмен стал в восемь раз быстрее. Остаётся вопрос, на который «проверил глазами» не отвечает: пишет ли новая версия в базу ровно то же, что старая? Код, который каждые несколько секунд обновляет сотни записей, ошибётся тихо и сразу везде. Вот схема, которой мы это доказываем.
Это четвёртая часть цикла «Массовый импорт в WordPress».
Схема на тестовой копии
- Держать рядом точную копию старой реализации — под другим именем, без единой правки.
- Снимок всей меты, которую код может изменить (у нас — списки совместимости у всех товаров).
- Прогон старой → снимок А → откат к исходному снимку.
- Прогон новой → снимок Б → откат.
- Сверка А и Б побайтово — сравнивать сериализованные значения, а не «на глаз».
- В конце убедиться, что база вернулась в исходное состояние. Иначе проверка сама стала поломкой.
function snapshot(array $ids): array {
$s = [];
foreach ($ids as $id) {
$s[$id] = get_post_meta($id, 'fitsProductUnique', true);
}
return array_map('serialize', $s);
}
function restore(array $snap): void {
foreach ($snap as $id => $v) {
update_post_meta($id, 'fitsProductUnique', unserialize($v));
}
}
$before = snapshot($ids);
old_fits_product($target); $a = snapshot($ids); restore($before);
new_fits_product($target); $b = snapshot($ids); restore($before);
$diff = array_keys(array_diff_assoc($a, $b));
echo count($diff) ? 'РАЗНИЦА: ' . implode(',', $diff) : 'совпадает', "\n";
echo snapshot($ids) === $before ? "база восстановлена\n" : "БАЗА НЕ ВОССТАНОВЛЕНА\n";
На ком гонять
- самые тяжёлые объекты — где код делает больше всего работы (у нас товар со 101 связью);
- случайная выборка — чтобы не подбирать удобные случаи;
- краевые — объекты, на которых код почти ничего не делает: товар, на который никто не ссылается, товар без поля сортировки;
- запасной путь отдельно. Если в коде есть ветка «при ничьей — по-старому» (третья часть), её условие надо создать искусственно, иначе она так и останется непроверенной.
Ловушка: две версии в одном процессе
Обе реализации мы гоняли в одном PHP-процессе — и старая выглядела втрое быстрее, чем есть: 7,86 секунды в первой итерации и 1,6 во всех следующих. Новая версия заполняла общую памятку, а старая ею пользовалась. Чуть не сделали вывод «выигрыша нет».
Перед каждым прогоном сбрасывайте общее состояние: статические переменные, wp_cache_flush(), свои памятки. И смотрите на первую итерацию отдельно: пока не доказано обратное, она единственная честная.
На рабочем сайте — без записи
На живых данных откатывать нельзя: между снимком и откатом может прийти настоящий обмен. Там та же идея без записи: вместо update_post_meta обе реализации считают в память то, что записали бы, и это сравнивается.
$writes = [];
$write = function (int $id, $value) use (&$writes) { $writes[$id] = serialize($value); };
old_fits_product($target, $write); $a = $writes; $writes = [];
new_fits_product($target, $write); $b = $writes;
Например, запись в обеих версиях выносится в параметр-функцию: в бою она пишет в базу, в проверке — в массив. Результаты:
- первый сайт: снимок меты сняли до выката, после выката прогнали новую реализацию и сверили с ним — 9 самых связных товаров, около 900 адресатов, расхождений ноль;
- второй сайт: 303 адресата — разница в составе групп ноль. Отличался только взаимный порядок пары с одинаковым
order, и это ровно то изменение, которое мы внесли сознательно.
Страница работает — это ещё не данные на месте
Обратный случай с того же проекта. Новую группировку вариантов товара проверяли по странице каталога: всё работало, замеры зелёные. А в базе нужных ключей уже не было. Разметка лежала в кэше фрагментов, снятая, когда данные были целы, а чужой код при сохранении любого товара молча срезал ключи. Нашлось это только прямым запросом к базе.
Правка меняет данные — проверяйте данные. Страница из кэша может показывать вчерашнюю правду сколько угодно долго, и сброс кэша этого не выявит: он лишь перерисует уже испорченные данные.
Переписываете импорт и боитесь сломать данные?
Пришлите код — соберу проверку, которая сравнивает старую и новую версию на ваших данных без записи в рабочую базу.
// загружаю комментарии…