SlideShare a Scribd company logo
1 of 53
Подход
Доктора Хауса
в тестировании
оптимизации
запросов
(третья серия)
Автор: Михалев Сергей
f1incode.com
У вас есть отличная команда

Но не докторов, а программистов,
которые занимаются оптимизацией
вашего приложения.
Понедельник: и вдруг …

С этого дня вы
сами должны
тестировать ваши
оптимизации!
Понедельник: и вдруг …
Теперь мы
отвечаем за
тестирование …
Что!?

А как же отдел
тестирования?

“Отличное”
начало
рабочей
недели.
У нас новый ПАЦИЕНТ

А что тогда
делать
мне?

Я буду
оптимизировать

Тогда я буду
тестировать
оптимизацию
…
Ты
Понедельник будешь

тестировать
функциональность
изменений

Но ведь это
самое
неинтересное
…

Разве? ..
У нас новый ПАЦИЕНТ
Понедельник
Я сделаю все так,
чтобы это было
интересно и этим
можно было
гордиться!

У нас новый ПАЦИЕНТ
Наш пациент : предмет оптимизации
Diagnosis Search Page
Patient:

Disease:

Doctor:

Search

House

DB

Doctor

Disease

Dr. House

Lumpus

Ivanov Petr
Loading 5
Records … / Pages 3

UI

Patient
Loading
123 …
Petrov Ivan

Dr. House

Lumpus

select * from (
select patient_name, doctor_name, disease_name,
row_number() over(order by patient_name)
as row_index
from list_diagnosis()
where doctor_name = 'House‘ ) d
where row_index between 1 and 2

Запрос для
постраничного
вывода данных

select count(*)
from list_diagnosis()
where doctor_name = 'House'

Запрос общего
количества
строк
Уровни тестирования
1) Чем ниже уровень
тестирования, тем оно

Тестирование
через UI

a) меньше слоев вовлекает;
b) более направленное;
c) быстрее может выполняться.

UI

Приложение

2) Автоматическое
Тестирование
тестирование дает
через API
возможность перезапускать
наши тесты, производя тем
самым регрессионное
База данных
Тестирование
тестирование.
через DB
Основные вопросы
Как
функционально
тестировать
оптимизацию
запросов на
уровне базы
данных?

Как сделать тесты
воспроизводимыми?
Требования к решению
1) Нашим тестом должен быть запрос. Это
позволит тестировать на уровне базы.
2) Нужен запрос, который сравнит
результаты функции до оптимизации с
результатами этой же функции, но уже
после оптимизации.
3) При этом сделает это для различных
входных параметров.
4) И выведет все расхождения, если они есть.
5) Плюс будет достаточно быстрым.
Конец понедельника
Вторник

Продолжаем готовиться к тестированию …
Вторник

Можем даже устроить daily standup
Сравнение двух множеств
множество мужчин
из сериала (A)

Форман
Чейз
Хаус

множество
умных врачей (B)

Форман
Чейз
Кэмерон
Хаус

A except B

B except A

пустое
множество

Кэмерон

Тогда чтобы сравнить эти два множества мы можем
использовать следующую последовательность операций.

(A except B) union (B except A)
В результате пустое множество означает, что все элементы
А есть во множестве B и все элементы B есть в А. Значит эти
два множества состоят из одинаковых элементов.
Операции со множествами в SQL
with source as
(
select patient_name,
desease_name, doctor_name
from list_diagnosis_old()
),
target as
(

Исходная функция

Оптимизированная
функция

select patient_name,
desease_name, doctor_name
from list_diagnosis_new()

)
(select * from source except select * from target)
union all
(select * from target except select * from source)

Операция сравнения
Функция с параметрами
Наша функция зависит от параметра
list_diagnosis_new(@hospital_id)

Тогда нам нужно получить множество параметров, на котором
мы будем тестировать нашу функцию.
Например, таким образом
select hospital_id
from HOSPITAL

Если параметров несколько
select hospital_id, doctor_id
from HOSPITAL
cross join DOCTOR

Или самим сформировать множество параметров
select 1 as doctor_id
union all select 3
union all select 4
union all select 6

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

select p.hospital_id, patient_name,
desease_name, doctor_name
Выходное тестируемое
from
множество, включая
(
параметры
select hospital_id
from HOSPITAL
Множество
) p

cross apply
list_diagnosis(p.hospital_id)

тестируемых
параметров

Тестируемая
функция
Итоговый запрос
with source as
Множества параметров
(
select hospital_id, patient_name, desease_name, f.doctor_name
from HOSPITAL h
cross apply list_diagnosis_old(hospital_id) f
),
target as
Тестируемые функции
(
select hospital_id, patient_name, desease_name, f.doctor_name
from HOSPITAL h
cross apply list_diagnosis_new(hospital_id) f
)
(select * from source except select * from target)
union all
(select * from target except select * from source)

Операция сравнения
Чейз закончил
оптимизацию,
дело за нами.

Вечер вторника

Быстро, я
пока еще
полностью
не готова.
Среда

Я получил
Вечер вторника
оптимизацию
в

5 раз!!!
Особенно программисты 
Поэтому ВСЁ нужно перепроверять.
Начинаем
тестировать
Экспресс анализ изменений
Старая функция

Новая функция

Очевидно, что необходимо тестировать функциональность!
Скорость и покрытие
1) Чем больше объем данных.
2) Чем больше тестируемое множество параметров.
3) Чем медленнее функция.

Тем, очевидно, дольше будет работать сравнение.
Поэтому не стоит запускать сравнение сразу на
полном объеме данных и с полным множеством
параметров.
Наращивайте скоуп тестирования постепенно.
Начинаем тестировать
with source as
(
select hospital_id, patient_name, desease_name, f.doctor_name
from (select top 1 hospital_id from HOSPITAL order by hospital_id)
cross apply list_diagnosis_old(hospital_id) f
),
target as
(
select hospital_id, patient_name, desease_name, f.doctor_name

from (select top 1 hospital_id from HOSPITAL order by hospital_id)
cross apply list_diagnosis_new(hospital_id) f
)
(select * from source except select * from target)
union all
(select * from target except select * from source)
Увеличиваем скоуп
with source as
(
select hospital_id, patient_name, desease_name, f.doctor_name

from (select top 100 hospital_id from HOSPITAL order by hospital_id
cross apply list_diagnosis_old(hospital_id) f
),
target as
(
select hospital_id, patient_name, desease_name, f.doctor_name

from (select top 100 hospital_id from HOSPITAL order by hospital_id
cross apply list_diagnosis_new(hospital_id) f
)
(select * from source except select * from target)
union all
(select * from target except select * from source)
Сравниваем на всем объеме
with source as
(
select hospital_id, patient_name, desease_name, f.doctor_name
from HOSPITAL h
cross apply list_diagnosis_old(h.hospital_id) f
),
target as
(
select hospital_id, patient_name, desease_name, f.doctor_name
from HOSPITAL h
cross apply list_diagnosis_new(h.hospital_id) f
)
(select * from source except select * from target)
union all
(select * from target except select * from source)
Тестирование на всем объеме
Даже, если мы протестировали на всем объеме имеющихся
данных. Дает ли нам это гарантию, что мы действительно
протестировали все и ошибок нет?

НЕТ
Потому что, тестирование происходит на уже
существующих данных. И база может не содержать
некоторых важных случаев.

НО
Если система существует продолжительное время. То
именно для оптимизации запросов, это позволяет
получить очень большую долю уверенности, что все
хорошо.
Результаты тестирования
И функционально
работает
правильно.
Функция
действительно
стала быстрее.

Ну … тогда
выносим.
Выносим изменения
Четверг: после оптимизации у нас
БАГ!
Ну как же
так, я же все
проверила.
Описание бага
BUG

Количество строк в гриде не совпадает с подсчитанным в футоре.
Diagnosis Search Page
Patient:

UI

Disease:

Doctor:

Search

House

Doctor

Disease

Dr. House

Lumpus

Ivanov Petr
Records 5 / Pages 3

DB

Patient
123
Petrov Ivan

Dr. House

Lumpus

select * from (
select patient_name, doctor_name, disease_name,
row_number() over(order by patient_name) as row_index
from list_diagnosis(@hospital_id)
where doctor_name = 'House‘ ) d
where row_index between 1 and 2
select count(*)
from list_diagnosis(@hospital_id)
where doctor_name = 'House'
Еще об операциях над множествами
Результат запроса – это не множество. Понятие множества
подразумевает, что в нем нет одинаковых элементов.
Результат
запроса(A)
Форман
Форман
Чейз

Результат
запроса (B)

A except B

B except A

Форман
Чейз

пустое
множество

пустое
множество

SQL операция except работает со множеством, т.е.
удалит все дубликаты.
По аналогии с union и union all в стандарте есть
операция except all, но она не реализована в SQL
Server-e.
except all – своими руками
A

Форман
Форман
Чейз
A’
1, Форман
2, Форман
1, Чейз

B

A except B

B except A

Форман
Чейз

пустое
множество

пустое
множество

B’

A’ except B’

B’ except A’

2, Форман

пустое
множество

1, Форман
1, Чейз

Таким образом мы добавили специальный идентификатор,
который будет наращиваться только для одинаковых строк.
except all – своими руками
select hospital_id, patient_name,
desease_name, doctor_name,
row_number() over
(
partition by
hospital_id,
patient_name,
desease_name,
f.doctor_name
order by
hospital_id,
patient_name,
desease_name,
f.doctor_name
) number
from list_diagnosis_old(hospital_id)

Функция row_number()
нумерует строки исходя
из сортировки и
разбиения
partition by отвечает за
разбиения на
подмножества
order by отвечает за
сортировку в
подмножестве
Наш итоговый запрос
with source as
(
select hospital_id, patient_name,
desease_name, doctor_name,
row_number() over
Исходная функция
(
partition by hospital_id, patient_name,
desease_name, f.doctor_name
order by hospital_id, patient_name,
desease_name, f.doctor_name
) number
from HOSPITAL h
cross apply list_diagnosis_old(hospital_id)
),
target as
Тестируемая функция, с
(
...
такими же изменениями
)
(select * from source except select * from target)
union all
(select * from target except select * from source)
Повторяем
тестирование
Но результаты опять
положительные …

И проблема воспроизводиться
только на продакшене.
Описание бага: еще разок
BUG

Количество строк в гриде не совпадает с подсчитанным в футоре.
Т.е. отображается одна строка, а count(*) показывает, что их там
должно быть пять.
select * from
(
select patient_name, doctor_name, disease_name,
row_number() over(order by patient_name) as row_index
from list_diagnosis(@hospital_id)
where doctor_name = 'House‘
)d
where row_index between 1 and 2

DB

select count(*)
from list_diagnosis(@hospital_id)
where doctor_name = 'House'

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

этого просто не может быть …
Вечер четверга

но причина есть всегда!
Пятница: дифференциальный анализ
Дифференциальный анализ
Симптомы:
1) На продакшене, результаты
функции count(*) не
совпадают с реальным
количеством строк.
2) На локальных окружениях
это проблема не
воспроизводиться.

Диагноз:
1) Возможно на это влияют
какие-то настройки SQL
Server-а?
2) Или в самой функции есть
какие-то логические
ошибки, которые почему-то
проявляются только на
продакшене.
Утро пятницы
Я нашла
причину!

ночь прошла не зря …
Анализ функции
select p.patient_name, desease_name, doctor_name
from PATIENT p
Достаем
cross apply
последний диагноз
(
пациента.
select top 1 desease_name, doctor_name
from DIAGNOSIS g
А если, по какой-то
inner join DOCTOR d
on g.doctor_id = d.doctor_id
причине, есть 2
where g.patient_id = p.patient_id
диагноза с одним
order by g.create_time
временем.
) d

1) В этом случае SQL Server не гарантирует какую строчку он
в действительности достанет.
2) Это зависит от плана выполнения запроса, который,
очевидно, был разным для двух использований функции.
3) Поэтому функция становиться недетерминированной, т.е.
при одинаковых входных данных возвращать разные
результаты.
Исправляем логическую ошибку
select p.patient_name, desease_name, doctor_name
from PATIENT p
cross apply
(
select top 1 desease_name, doctor_name
from DIAGNOSIS g
inner join DOCTOR d
on g.doctor_id = d.doctor_id
where g.patient_id = p.patient_id
order by g.create_time
) d

Очевидно, нужно заменить недетерминированный order by,
например так:
order by g.create_time, g.diagnosis_id

или
order by g.diagnosis_id
Начинаем
тестировать
Выносим изменения
Анализ данного подхода
1) Тестирование производится на 1) Полное тестирование не
уровне базы данных.
всегда возможно.
2) Есть возможность
2) Даже при тестировании на
протестировать изменения на
всех данных – это не гарант
очень большом объеме
того, что ошибок нет. Так как:
данных.
• данных в базе может быть
3) Подход может использоваться:
недостаточно для
a) для функций, для запросов, для
хорошего сравнения;
сравнения целых баз данных
• даже в старой функции
b) в оптимизации, в ETL, для любых
могут быть логические
сравнений, где есть эталон.
ошибки;

Но в целом, если изменения производились только на уровне
базы данных, то такой подход целесообразнее.
Подводим итоги недели
1) Разобрались с операциями над
множествами, такими как except и union.
2) Реализовали except all – своими руками.
3) Разработали метод функционального
тестирования запросов.
4) Функционально протестировали
оптимизацию запроса на уровне базы
данных.
5) Нашли очень замысловатую логическую
ошибку и исправили старый
функциональный баг.
Подход Доктора Хауса в тестировании
Подводим итоги недели
оптимизации запросов
1) Разобрались с операциями над
1) множествами, такими как except и
Все врут, а значит
всё нужно
union. И даже реализовали except all.
перепроверять.
2) Мы разработали метод
2) Разбираться в
функционального тестирования
проблеме до конца.
3) запросов. резать по
Не боятся
3) Нашли оченьвсегда
живому, но замысловатую
логическую ошибку и исправили
контролировать.
4) старый функциональный баг.
Не сдаваться!!!

More Related Content

Viewers also liked

Анализируем результаты тестирования производительности с Graphite и Grafana
Анализируем результаты тестирования производительности с Graphite и GrafanaАнализируем результаты тестирования производительности с Graphite и Grafana
Анализируем результаты тестирования производительности с Graphite и GrafanaSQALab
 
Как заводить баги понятно всем
Как заводить баги понятно всемКак заводить баги понятно всем
Как заводить баги понятно всемSQALab
 
"Дедуктивный метод тестировщика". Ищем баги анализируя статистику
"Дедуктивный метод тестировщика". Ищем баги анализируя статистику"Дедуктивный метод тестировщика". Ищем баги анализируя статистику
"Дедуктивный метод тестировщика". Ищем баги анализируя статистикуSQALab
 
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языке
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языкеКак автоматизировать рутинные задачи или учимся разговаривать на змеином языке
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языкеSQALab
 
Дополнительные материалы от Егора Васильва
Дополнительные материалы от Егора ВасильваДополнительные материалы от Егора Васильва
Дополнительные материалы от Егора ВасильваSQALab
 
User experience, как замена юзабилити
User experience, как замена юзабилитиUser experience, как замена юзабилити
User experience, как замена юзабилитиSQALab
 
Грабли тестировщика
Грабли тестировщикаГрабли тестировщика
Грабли тестировщикаSQALab
 
Тестирование в Agile для больших команд: путь трансформации
Тестирование в Agile для больших команд: путь трансформацииТестирование в Agile для больших команд: путь трансформации
Тестирование в Agile для больших команд: путь трансформацииSQALab
 
Ментальные ловушки в тестировании
Ментальные ловушки в тестированииМентальные ловушки в тестировании
Ментальные ловушки в тестированииSQALab
 
Полезные фишки тестировщика или о чем никогда не стоит забывать
Полезные фишки тестировщика или о чем никогда не стоит забыватьПолезные фишки тестировщика или о чем никогда не стоит забывать
Полезные фишки тестировщика или о чем никогда не стоит забыватьSQALab
 
Парное тестирование для "чайников"
Парное тестирование для "чайников"Парное тестирование для "чайников"
Парное тестирование для "чайников"SQALab
 
Тренировка служебных тестировщиков
Тренировка служебных тестировщиковТренировка служебных тестировщиков
Тренировка служебных тестировщиковSQALab
 

Viewers also liked (12)

Анализируем результаты тестирования производительности с Graphite и Grafana
Анализируем результаты тестирования производительности с Graphite и GrafanaАнализируем результаты тестирования производительности с Graphite и Grafana
Анализируем результаты тестирования производительности с Graphite и Grafana
 
Как заводить баги понятно всем
Как заводить баги понятно всемКак заводить баги понятно всем
Как заводить баги понятно всем
 
"Дедуктивный метод тестировщика". Ищем баги анализируя статистику
"Дедуктивный метод тестировщика". Ищем баги анализируя статистику"Дедуктивный метод тестировщика". Ищем баги анализируя статистику
"Дедуктивный метод тестировщика". Ищем баги анализируя статистику
 
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языке
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языкеКак автоматизировать рутинные задачи или учимся разговаривать на змеином языке
Как автоматизировать рутинные задачи или учимся разговаривать на змеином языке
 
Дополнительные материалы от Егора Васильва
Дополнительные материалы от Егора ВасильваДополнительные материалы от Егора Васильва
Дополнительные материалы от Егора Васильва
 
User experience, как замена юзабилити
User experience, как замена юзабилитиUser experience, как замена юзабилити
User experience, как замена юзабилити
 
Грабли тестировщика
Грабли тестировщикаГрабли тестировщика
Грабли тестировщика
 
Тестирование в Agile для больших команд: путь трансформации
Тестирование в Agile для больших команд: путь трансформацииТестирование в Agile для больших команд: путь трансформации
Тестирование в Agile для больших команд: путь трансформации
 
Ментальные ловушки в тестировании
Ментальные ловушки в тестированииМентальные ловушки в тестировании
Ментальные ловушки в тестировании
 
Полезные фишки тестировщика или о чем никогда не стоит забывать
Полезные фишки тестировщика или о чем никогда не стоит забыватьПолезные фишки тестировщика или о чем никогда не стоит забывать
Полезные фишки тестировщика или о чем никогда не стоит забывать
 
Парное тестирование для "чайников"
Парное тестирование для "чайников"Парное тестирование для "чайников"
Парное тестирование для "чайников"
 
Тренировка служебных тестировщиков
Тренировка служебных тестировщиковТренировка служебных тестировщиков
Тренировка служебных тестировщиков
 

More from SQALab

Готовим стажировку
Готовим стажировкуГотовим стажировку
Готовим стажировкуSQALab
 
Куда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщикаКуда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщикаSQALab
 
Оптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержкиОптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержкиSQALab
 
Автоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программированияАвтоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программированияSQALab
 
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...SQALab
 
Continuous performance testing
Continuous performance testingContinuous performance testing
Continuous performance testingSQALab
 
Конфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нуженКонфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нуженSQALab
 
Команда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихииКоманда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихииSQALab
 
API. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советовAPI. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советовSQALab
 
Добиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестовДобиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестовSQALab
 
Делаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIsДелаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIsSQALab
 
Вредные привычки в тест-менеджменте
Вредные привычки в тест-менеджментеВредные привычки в тест-менеджменте
Вредные привычки в тест-менеджментеSQALab
 
Мощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизацииМощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизацииSQALab
 
Как hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качествеКак hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качествеSQALab
 
Стили лидерства и тестирование
Стили лидерства и тестированиеСтили лидерства и тестирование
Стили лидерства и тестированиеSQALab
 
"Давайте не будем про качество"
"Давайте не будем про качество""Давайте не будем про качество"
"Давайте не будем про качество"SQALab
 
Apache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовApache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовSQALab
 
Тестирование геолокационных систем
Тестирование геолокационных системТестирование геолокационных систем
Тестирование геолокационных системSQALab
 
Лидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопросЛидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопросSQALab
 
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...SQALab
 

More from SQALab (20)

Готовим стажировку
Готовим стажировкуГотовим стажировку
Готовим стажировку
 
Куда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщикаКуда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщика
 
Оптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержкиОптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержки
 
Автоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программированияАвтоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программирования
 
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
 
Continuous performance testing
Continuous performance testingContinuous performance testing
Continuous performance testing
 
Конфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нуженКонфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нужен
 
Команда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихииКоманда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихии
 
API. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советовAPI. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советов
 
Добиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестовДобиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестов
 
Делаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIsДелаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIs
 
Вредные привычки в тест-менеджменте
Вредные привычки в тест-менеджментеВредные привычки в тест-менеджменте
Вредные привычки в тест-менеджменте
 
Мощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизацииМощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизации
 
Как hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качествеКак hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качестве
 
Стили лидерства и тестирование
Стили лидерства и тестированиеСтили лидерства и тестирование
Стили лидерства и тестирование
 
"Давайте не будем про качество"
"Давайте не будем про качество""Давайте не будем про качество"
"Давайте не будем про качество"
 
Apache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовApache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектов
 
Тестирование геолокационных систем
Тестирование геолокационных системТестирование геолокационных систем
Тестирование геолокационных систем
 
Лидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопросЛидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопрос
 
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
 

Подход Доктора Хауса в тестировании оптимизации запросов

  • 2. У вас есть отличная команда Но не докторов, а программистов, которые занимаются оптимизацией вашего приложения.
  • 3. Понедельник: и вдруг … С этого дня вы сами должны тестировать ваши оптимизации!
  • 4. Понедельник: и вдруг … Теперь мы отвечаем за тестирование …
  • 5. Что!? А как же отдел тестирования? “Отличное” начало рабочей недели.
  • 6. У нас новый ПАЦИЕНТ А что тогда делать мне? Я буду оптимизировать Тогда я буду тестировать оптимизацию …
  • 7. Ты Понедельник будешь тестировать функциональность изменений Но ведь это самое неинтересное … Разве? .. У нас новый ПАЦИЕНТ
  • 8. Понедельник Я сделаю все так, чтобы это было интересно и этим можно было гордиться! У нас новый ПАЦИЕНТ
  • 9. Наш пациент : предмет оптимизации Diagnosis Search Page Patient: Disease: Doctor: Search House DB Doctor Disease Dr. House Lumpus Ivanov Petr Loading 5 Records … / Pages 3 UI Patient Loading 123 … Petrov Ivan Dr. House Lumpus select * from ( select patient_name, doctor_name, disease_name, row_number() over(order by patient_name) as row_index from list_diagnosis() where doctor_name = 'House‘ ) d where row_index between 1 and 2 Запрос для постраничного вывода данных select count(*) from list_diagnosis() where doctor_name = 'House' Запрос общего количества строк
  • 10. Уровни тестирования 1) Чем ниже уровень тестирования, тем оно Тестирование через UI a) меньше слоев вовлекает; b) более направленное; c) быстрее может выполняться. UI Приложение 2) Автоматическое Тестирование тестирование дает через API возможность перезапускать наши тесты, производя тем самым регрессионное База данных Тестирование тестирование. через DB
  • 12. Требования к решению 1) Нашим тестом должен быть запрос. Это позволит тестировать на уровне базы. 2) Нужен запрос, который сравнит результаты функции до оптимизации с результатами этой же функции, но уже после оптимизации. 3) При этом сделает это для различных входных параметров. 4) И выведет все расхождения, если они есть. 5) Плюс будет достаточно быстрым.
  • 16. Сравнение двух множеств множество мужчин из сериала (A) Форман Чейз Хаус множество умных врачей (B) Форман Чейз Кэмерон Хаус A except B B except A пустое множество Кэмерон Тогда чтобы сравнить эти два множества мы можем использовать следующую последовательность операций. (A except B) union (B except A) В результате пустое множество означает, что все элементы А есть во множестве B и все элементы B есть в А. Значит эти два множества состоят из одинаковых элементов.
  • 17. Операции со множествами в SQL with source as ( select patient_name, desease_name, doctor_name from list_diagnosis_old() ), target as ( Исходная функция Оптимизированная функция select patient_name, desease_name, doctor_name from list_diagnosis_new() ) (select * from source except select * from target) union all (select * from target except select * from source) Операция сравнения
  • 18. Функция с параметрами Наша функция зависит от параметра list_diagnosis_new(@hospital_id) Тогда нам нужно получить множество параметров, на котором мы будем тестировать нашу функцию. Например, таким образом select hospital_id from HOSPITAL Если параметров несколько select hospital_id, doctor_id from HOSPITAL cross join DOCTOR Или самим сформировать множество параметров select 1 as doctor_id union all select 3 union all select 4 union all select 6 Таким образом, мы может написать совершенно любой запрос для формирования множества параметров.
  • 19. Функция с параметрами После этого нам нужно научиться вызывать нашу функцию для интересующего нас множества параметров. select p.hospital_id, patient_name, desease_name, doctor_name Выходное тестируемое from множество, включая ( параметры select hospital_id from HOSPITAL Множество ) p cross apply list_diagnosis(p.hospital_id) тестируемых параметров Тестируемая функция
  • 20. Итоговый запрос with source as Множества параметров ( select hospital_id, patient_name, desease_name, f.doctor_name from HOSPITAL h cross apply list_diagnosis_old(hospital_id) f ), target as Тестируемые функции ( select hospital_id, patient_name, desease_name, f.doctor_name from HOSPITAL h cross apply list_diagnosis_new(hospital_id) f ) (select * from source except select * from target) union all (select * from target except select * from source) Операция сравнения
  • 21. Чейз закончил оптимизацию, дело за нами. Вечер вторника Быстро, я пока еще полностью не готова.
  • 23. Особенно программисты  Поэтому ВСЁ нужно перепроверять.
  • 25. Экспресс анализ изменений Старая функция Новая функция Очевидно, что необходимо тестировать функциональность!
  • 26. Скорость и покрытие 1) Чем больше объем данных. 2) Чем больше тестируемое множество параметров. 3) Чем медленнее функция. Тем, очевидно, дольше будет работать сравнение. Поэтому не стоит запускать сравнение сразу на полном объеме данных и с полным множеством параметров. Наращивайте скоуп тестирования постепенно.
  • 27. Начинаем тестировать with source as ( select hospital_id, patient_name, desease_name, f.doctor_name from (select top 1 hospital_id from HOSPITAL order by hospital_id) cross apply list_diagnosis_old(hospital_id) f ), target as ( select hospital_id, patient_name, desease_name, f.doctor_name from (select top 1 hospital_id from HOSPITAL order by hospital_id) cross apply list_diagnosis_new(hospital_id) f ) (select * from source except select * from target) union all (select * from target except select * from source)
  • 28. Увеличиваем скоуп with source as ( select hospital_id, patient_name, desease_name, f.doctor_name from (select top 100 hospital_id from HOSPITAL order by hospital_id cross apply list_diagnosis_old(hospital_id) f ), target as ( select hospital_id, patient_name, desease_name, f.doctor_name from (select top 100 hospital_id from HOSPITAL order by hospital_id cross apply list_diagnosis_new(hospital_id) f ) (select * from source except select * from target) union all (select * from target except select * from source)
  • 29. Сравниваем на всем объеме with source as ( select hospital_id, patient_name, desease_name, f.doctor_name from HOSPITAL h cross apply list_diagnosis_old(h.hospital_id) f ), target as ( select hospital_id, patient_name, desease_name, f.doctor_name from HOSPITAL h cross apply list_diagnosis_new(h.hospital_id) f ) (select * from source except select * from target) union all (select * from target except select * from source)
  • 30. Тестирование на всем объеме Даже, если мы протестировали на всем объеме имеющихся данных. Дает ли нам это гарантию, что мы действительно протестировали все и ошибок нет? НЕТ Потому что, тестирование происходит на уже существующих данных. И база может не содержать некоторых важных случаев. НО Если система существует продолжительное время. То именно для оптимизации запросов, это позволяет получить очень большую долю уверенности, что все хорошо.
  • 33. Четверг: после оптимизации у нас БАГ! Ну как же так, я же все проверила.
  • 34. Описание бага BUG Количество строк в гриде не совпадает с подсчитанным в футоре. Diagnosis Search Page Patient: UI Disease: Doctor: Search House Doctor Disease Dr. House Lumpus Ivanov Petr Records 5 / Pages 3 DB Patient 123 Petrov Ivan Dr. House Lumpus select * from ( select patient_name, doctor_name, disease_name, row_number() over(order by patient_name) as row_index from list_diagnosis(@hospital_id) where doctor_name = 'House‘ ) d where row_index between 1 and 2 select count(*) from list_diagnosis(@hospital_id) where doctor_name = 'House'
  • 35. Еще об операциях над множествами Результат запроса – это не множество. Понятие множества подразумевает, что в нем нет одинаковых элементов. Результат запроса(A) Форман Форман Чейз Результат запроса (B) A except B B except A Форман Чейз пустое множество пустое множество SQL операция except работает со множеством, т.е. удалит все дубликаты. По аналогии с union и union all в стандарте есть операция except all, но она не реализована в SQL Server-e.
  • 36. except all – своими руками A Форман Форман Чейз A’ 1, Форман 2, Форман 1, Чейз B A except B B except A Форман Чейз пустое множество пустое множество B’ A’ except B’ B’ except A’ 2, Форман пустое множество 1, Форман 1, Чейз Таким образом мы добавили специальный идентификатор, который будет наращиваться только для одинаковых строк.
  • 37. except all – своими руками select hospital_id, patient_name, desease_name, doctor_name, row_number() over ( partition by hospital_id, patient_name, desease_name, f.doctor_name order by hospital_id, patient_name, desease_name, f.doctor_name ) number from list_diagnosis_old(hospital_id) Функция row_number() нумерует строки исходя из сортировки и разбиения partition by отвечает за разбиения на подмножества order by отвечает за сортировку в подмножестве
  • 38. Наш итоговый запрос with source as ( select hospital_id, patient_name, desease_name, doctor_name, row_number() over Исходная функция ( partition by hospital_id, patient_name, desease_name, f.doctor_name order by hospital_id, patient_name, desease_name, f.doctor_name ) number from HOSPITAL h cross apply list_diagnosis_old(hospital_id) ), target as Тестируемая функция, с ( ... такими же изменениями ) (select * from source except select * from target) union all (select * from target except select * from source)
  • 40. Но результаты опять положительные … И проблема воспроизводиться только на продакшене.
  • 41. Описание бага: еще разок BUG Количество строк в гриде не совпадает с подсчитанным в футоре. Т.е. отображается одна строка, а count(*) показывает, что их там должно быть пять. select * from ( select patient_name, doctor_name, disease_name, row_number() over(order by patient_name) as row_index from list_diagnosis(@hospital_id) where doctor_name = 'House‘ )d where row_index between 1 and 2 DB select count(*) from list_diagnosis(@hospital_id) where doctor_name = 'House' Т.е. одна и та же функция с одинаковыми фильтрами, но в разных контекстах возвращает разное количество строк.
  • 45. Дифференциальный анализ Симптомы: 1) На продакшене, результаты функции count(*) не совпадают с реальным количеством строк. 2) На локальных окружениях это проблема не воспроизводиться. Диагноз: 1) Возможно на это влияют какие-то настройки SQL Server-а? 2) Или в самой функции есть какие-то логические ошибки, которые почему-то проявляются только на продакшене.
  • 47. Анализ функции select p.patient_name, desease_name, doctor_name from PATIENT p Достаем cross apply последний диагноз ( пациента. select top 1 desease_name, doctor_name from DIAGNOSIS g А если, по какой-то inner join DOCTOR d on g.doctor_id = d.doctor_id причине, есть 2 where g.patient_id = p.patient_id диагноза с одним order by g.create_time временем. ) d 1) В этом случае SQL Server не гарантирует какую строчку он в действительности достанет. 2) Это зависит от плана выполнения запроса, который, очевидно, был разным для двух использований функции. 3) Поэтому функция становиться недетерминированной, т.е. при одинаковых входных данных возвращать разные результаты.
  • 48. Исправляем логическую ошибку select p.patient_name, desease_name, doctor_name from PATIENT p cross apply ( select top 1 desease_name, doctor_name from DIAGNOSIS g inner join DOCTOR d on g.doctor_id = d.doctor_id where g.patient_id = p.patient_id order by g.create_time ) d Очевидно, нужно заменить недетерминированный order by, например так: order by g.create_time, g.diagnosis_id или order by g.diagnosis_id
  • 51. Анализ данного подхода 1) Тестирование производится на 1) Полное тестирование не уровне базы данных. всегда возможно. 2) Есть возможность 2) Даже при тестировании на протестировать изменения на всех данных – это не гарант очень большом объеме того, что ошибок нет. Так как: данных. • данных в базе может быть 3) Подход может использоваться: недостаточно для a) для функций, для запросов, для хорошего сравнения; сравнения целых баз данных • даже в старой функции b) в оптимизации, в ETL, для любых могут быть логические сравнений, где есть эталон. ошибки; Но в целом, если изменения производились только на уровне базы данных, то такой подход целесообразнее.
  • 52. Подводим итоги недели 1) Разобрались с операциями над множествами, такими как except и union. 2) Реализовали except all – своими руками. 3) Разработали метод функционального тестирования запросов. 4) Функционально протестировали оптимизацию запроса на уровне базы данных. 5) Нашли очень замысловатую логическую ошибку и исправили старый функциональный баг.
  • 53. Подход Доктора Хауса в тестировании Подводим итоги недели оптимизации запросов 1) Разобрались с операциями над 1) множествами, такими как except и Все врут, а значит всё нужно union. И даже реализовали except all. перепроверять. 2) Мы разработали метод 2) Разбираться в функционального тестирования проблеме до конца. 3) запросов. резать по Не боятся 3) Нашли оченьвсегда живому, но замысловатую логическую ошибку и исправили контролировать. 4) старый функциональный баг. Не сдаваться!!!