Что тестировщикам (и не только им) важно знать о базах данных. Шпаргалка по популярным ошибкам

от автора

Нужно ли тестировщику разбираться в базах данных? Короткий ответ: да, как минимум на том уровне, чтобы можно было успешно выявлять и локализовывать ошибки в их работе. На практике же проблемы в базах данных зачастую фрустрируют даже опытных QA-инженеров. Что-то где-то пошло не так, но что именно и где?

Разумеется, БД — вовсе не черный ящик с магией внутри, а такой же набор взаимодействующих по определенным правилам компонентов, как и все остальное, с чем ежедневно приходится иметь дело QA-инженерам (и разработчикам, на самом деле, тоже, но они обычно больше погружены в контекст). Понимание того, что там под капотом, помогает эффективно проводить тест-дизайн, локализовывать баги, общаться с разработкой.

Под катом — наша шпаргалка по распространённым багам в работе баз данных. Разбили их по категориями, снабдили примерами и объяснили первопричины появления. Надеемся, будет полезно не только QA-специалистам, но и бэкенд-разработчикам начального уровня, а также всем, кто хочет углубить свои познания в области взаимодействия с БД.


Привет, Хабр! Меня зовут Анна, я QA lead mobile в компании Сравни. У меня четыре с половиной года опыта в обеспечении качества, год лидства и нежная любовь к тестированию бэкенда.

В одном из туториалов я как-то услышала фразу типа «что мы в базу данных записали, то нам из нее и вернётся». В самоуверенности автора этого видео чувствовался подвох: моя QA-чуйка подсказывала, что такой важный и сложный узел взаимодействия разных частей ПО не может не преподнести однажды сюрприз. Поразмыслив над вопросом, я набросала список случаев, когда БД может подвести — так зародилась первая версия этой статьи.

Далее были несколько месяцев собеседований middle+/senior QA в разные команды. Ребята с парой-тройкой лет коммерческого опыта, бодро отвечавшие на вопросы о процессах и мобильном тестировании, начинали «сыпаться» на вопросах вроде «назови, пожалуйста, возможные причины, по которым БД не возвращает нам ожидаемые данные». Это был второй повод написать статью. 

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

Распространённые ошибки в работе БД

Ниже — список возможных проблем с примерами, разбитый на категории. Финально устранить их помогут разработчики или девопсы, в то время как ключевая задача QA: выявить ошибку, локализовать, чётко определить её суть и причины возникновения. Чтобы затем прийти к коллегам не с абстрактным запросом в духе «там что-то не работает», а с описанием конкретной технической проблемы. 

1. Проблемы с транзакциями

  • Неправильное начало или завершение транзакции

Предположим, пользователь переводит деньги со своего счета (Account A) на другой счет (Account B). Процесс состоит из двух шагов:

  1. уменьшить баланс счета A

  2. увеличить баланс счета B

Если в транзакции возникает ошибка, например, на этапе увеличения баланса счета B, а система не откатывает транзакцию, то мы получим ситуацию, когда деньги списаны с счета A, но не зачислены на счет B. Это нарушает целостность данных.

  • Проблемы с изоляцией транзакций, приводящие к таким явлениям, как грязное чтение, неповторяющееся чтение и фантомное чтение

Все три явления возникают при недостаточной изоляции транзакций и приводят к некорректному чтению данных в многопользовательской среде.

Грязное чтение происходит, когда одна транзакция читает данные, которые были изменены, но ещё не зафиксированы (не коммитнуты) в другой транзакции. Если вторая транзакция откатится, первая останется с данными, которых больше нет в базе данных.

Пример:

  • транзакция A: обновляет баланс пользователя в таблице accounts и еще не коммитнула изменения

  • транзакция B: читает баланс из accounts, пока транзакция A ещё активна

В этом случае транзакция B увидела баланс, увеличенный на 500, хотя изменение было откатано, и этих 500 в итоге нет в базе.

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

Пример:

  • транзакция A: читает баланс пользователя из таблицы accounts

  • транзакция B: обновляет баланс пользователя и коммитит изменения

Транзакция A в первом чтении видит баланс 1000, а во втором — 800, хотя ожидала увидеть один и тот же результат.

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

Пример:

  • транзакция A: считает количество пользователей с балансом более 1000

  • транзакция B: вставляет нового пользователя с балансом 1500 и коммитит изменения

Транзакция A в первом запросе видела 5 пользователей с балансом более 1000, а при повторном запросе видит уже 6, хотя сама не добавляла новых пользователей.

  • Конкурентный доступ

Состояние гонки: возникает, когда два или более процесса (транзакции) пытаются одновременно изменить одни и те же данные, и результат зависит от порядка выполнения этих процессов.

Пример: предположим, два клиента одновременно пытаются купить последние 10 билетов на мероприятие:

  • транзакция A и транзакция B одновременно читают количество доступных билетов (10) и решают купить 5 билетов каждый

  • обе транзакции обновляют количество билетов, уменьшая его на 5, и записывают обратно

В результате продажи остались некорректными, и количество билетов неверно — должно было остаться 0 билетов, но получилось 5.

Еще транзакции могут оказаться взаимно заблокированными. Например:

  • транзакция A заблокировала строку 1 в таблице accounts и пытается заблокировать строку 2

  • транзакция B в это время заблокировала строку 2 и пытается заблокировать строку 1

В этом случае обе транзакции ждут освобождения ресурсов друг от друга, и ни одна не может завершиться, пока другая не освободит блокировку.

2. Нарушение целостности данных

  • Отсутствие или некорректная реализация ограничений целостности (первичные и внешние ключи, уникальные ограничения)

    Предположим, у нас есть таблица employees, где каждый сотрудник должен иметь уникальный номер телефона. В противном случае дублирование приводит к нарушению целостности. Это может создать проблемы в идентификации сотрудника по номеру телефона, а значит, в создании или поиске других сущностей, которые привязаны к конкретному сотруднику.

  • Нарушение ссылочной целостности, например, когда удаляются записи, на которые ссылаются другие записи. 

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

Предположим, есть две таблицы: orders и customers, где каждая запись в orders ссылается на существующего клиента в customers через внешний ключ customer_id. Если какого-то конкретного customer_id в таблице orders не существует в таблице customers, что нарушает целостность ссылок. Это может привести к ошибкам, когда система попытается получить информацию о клиенте для заказа.

Частая проблема у тех QA, кто любит трогать данные в базе руками. Вы считаете, что удалили или изменили прямо в конкретной табличке что-то неважное у своего тестового юзера, а в итоге разваливается весь флоу, потому что данных в нужном месте нет или они не того типа, который ожидается. Приходится создавать нового пользователя, заново обвешивать его всеми нужными для теста сущностями и больше никогда-никогда не лезть с апдейтами в базу, не посмотрев предварительно в её схему.

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

3. Проблемы с кэшированием

  • Неправильная синхронизация кэша и базы данных, когда кэшированные данные устарели

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

  • Кэширование данных на уровне приложения, что может привести к устареванию данных

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

Цена, которая должна считаться на лету, по каким-то причинам не обновилась, и пользователь заплатил за товар или услугу меньше или наоборот больше, чем должен был. Если таких прецедентов будет много, у компании возникнут проблемы — либо финансовые, либо репутационные, либо и те и другие вместе.

При подобных проблемах QA может последовательно сбросить кэши везде, где ему доступна эта операция, наблюдая, меняется ли результат. Если меняется, значит, проблема обнаружена — можно смело отдавать её на починку. 

4. Репликация и распределённые системы

  • Проблемы с репликацией данных между серверами, что может привести к несоответствиям

  • Задержки или ошибки в синхронизации данных в распределённых базах данных

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

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

Если же говорить в общем, то разработчики могут исправить те проблемы, которые касаются реализации проблемных методов в коде, а девопсы — проблемы на уровне базы данных (с кэшированием, целостностью данных и блокировками).

***

Мы рассмотрели четыре категории возможных проблем с базами данных. Некоторые из них закрадываются еще в процессе разработки, другие зависят от инфраструктуры, а какие-то мы, QA-инженеры, можем устроить себе сами. 

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

Сохраняйте шпаргалку, гуглите непонятное, будьте критичными!

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


ссылка на оригинал статьи https://habr.com/ru/articles/861806/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *