Как оценить «собственную» продолжительность запросов Application Insights, без учета продолжительности зависимостей?

#azure-application-insights #azure-data-explorer #kql

#azure-application-insights #azure-data-explorer #kql

Вопрос:

Я пытаюсь создать запрос Kusto для измерения «собственной» продолжительности запросов (вычитая длительности зависимостей). Однако я не могу понять, как решить это с помощью чистого запроса Kusto.

Чтобы лучше понять, чего можно было бы ожидать, ниже приведен пример:

Представление высокого уровня (где R — запрос, а Dx — зависимости)

  R    =============================== (31ms)
 D1     ******* (7ms)
 D2          ******** (8ms)
 D3                        ****** (6ms)
 D4                          ** (2ms)
 D5         **** (4ms)

 Proj ==*************======******====
 
  • D1 перекрывается D2 в течение 2 мс
  • D5 и D4 не следует принимать во внимание, что они полностью перекрываются другими зависимостями
  • Proj является проекцией потенциального промежуточного шага, на котором отображаются только значимые сегменты зависимостей

Учитывая следующий набор данных тестового стенда

 let reqs = datatable (timestamp: datetime, id:string, duration: real)
[
  datetime("2020-12-15T08:00:00.000Z"), "r1", 31    // R
];
let deps = datatable (timestamp: datetime, operation_ParentId:string, duration: real)
[
  datetime("2020-12-15T08:00:00.002Z"), "r1", 7,    // D1
  datetime("2020-12-15T08:00:00.007Z"), "r1", 8,    // D2
  datetime("2020-12-15T08:00:00.021Z"), "r1", 6,    // D3
  datetime("2020-12-15T08:00:00.023Z"), "r1", 2,    // D4
  datetime("2020-12-15T08:00:00.006Z"), "r1", 4,    // D5
];
 

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

 Expected total duration = 31 - (7   8 - 2) - (6) = 12
 

Любая помощь в продвижении вперед была бы с благодарностью <3

Ответ №1:

Мне удалось решить это с помощью этого использования row_window_session() . Это оконная функция. Подробнее об этом можно прочитать в обзоре функций окна.

Решение таково:

 let reqs = datatable (timestamp: datetime, operation_ParentId:string, duration: real)
[
  datetime("2020-12-15T08:00:00.000Z"), "r1", 31    // R
];
let deps = datatable (timestamp: datetime, operation_ParentId:string, duration: real)
[
  datetime("2020-12-15T08:00:00.002Z"), "r1", 7,    // D1
  datetime("2020-12-15T08:00:00.007Z"), "r1", 8,    // D2
  datetime("2020-12-15T08:00:00.021Z"), "r1", 6,    // D3
  datetime("2020-12-15T08:00:00.006Z"), "r1", 4,    // D5
  datetime("2020-12-15T08:00:00.023Z"), "r1", 2,    // D4
];
deps
| extend endTime = timestamp   totimespan(duration * 10000)
| sort by timestamp asc 
| serialize | extend SessionStarted = row_window_session(timestamp, 1h, 1h, timestamp > prev(endTime))
| summarize max(endTime) by operation_ParentId, SessionStarted
| extend diff = max_endTime - SessionStarted
| summarize todouble(sum(diff)) by operation_ParentId
| join reqs on operation_ParentId
| extend diff = duration - sum_diff / 10000
| project diff 
 

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

  1. Вычислите на endTime основе продолжительности. Чтобы нормализовать данные, я умножу продолжительность на 10000:
     | extend endTime = timestamp   totimespan(duration * 10000)
     
  2. Сортировка по времени запуска:
     | sort by timestamp asc 
     
  3. Это ключ к этому решению. Он рассчитывается по timestamp столбцу. Следующие два параметра — это ограничения на запуск новых сегментов. Поскольку мы не хотим запечатывать ведро в зависимости от прошедшего времени, я предоставил 1 час, который не попадет в этот ввод. Четвертый аргумент помогает нам создать новый сеанс на основе данных. Пока строк будет больше timestamp > prev(endTime) , они будут иметь одинаковое время начала.
     | serialize | extend SessionStarted = row_window_session(timestamp, 1h, 1h, timestamp > prev(endTime))
     
  4. Теперь у нас есть несколько строк для начала сеанса. Поэтому мы хотим сохранить только самое последнее время для каждого сеанса. Мы также продолжим operation_ParentId позже присоединиться к этому ключу:
     | summarize max(endTime) by operation_ParentId, SessionStarted
     
  5. Вычислите время каждого сеанса:
     | extend diff = max_endTime - SessionStarted
     
  6. Суммируйте все время сеанса:
     | summarize todouble(sum(diff)) by operation_ParentId
     
  7. Присоединяйтесь req , чтобы узнать общее время начала:
     | join reqs on operation_ParentId
     
  8. Вычислите разницу между общим временем и временем сеанса. Ненормализовать данные:
     | extend diff = duration - sum_diff / 10000
     
  9. Спроецируйте конечный результат:
     | project diff 
     

Вы можете найти этот запрос, выполняемый в открытой базе данных Kusto Samples.

Сказав это, обратите внимание, что это линейная операция. Это означает, что если есть 2 следующих сегмента, которые должны находиться под одним и тем же сегментом, но они не пересекаются, произойдет сбой. Например, добавив следующее в deps :

 datetime("2020-12-15T08:00:00.026Z"), "r1", 1,    // D6
 

что не должно ничего добавлять к вычислению, может привести к неправильному поведению. Это связано с тем, что d4 это предыдущий пункт, и он не имеет точки соприкосновения d6 , хотя d3 охватывает их оба.
Чтобы решить эту проблему, вам нужно повторить ту же логику шагов 3-5. К сожалению, в Kusto нет рекурсий, поэтому вы не можете решить эту проблему для любого вида ввода. Но если предположить, что на самом деле нет таких глубоких случаев, которые нарушают эту логику, я думаю, что это достаточно хорошо.

Комментарии:

1. Потрясающе! Большое спасибо за этот отличный ответ и очень четкое объяснение! <3

2. Конечно @nulltoken 🙂 Раньше я работал разработчиком в Kusto. Это отличный проект. Большое спасибо за вознаграждение!!

Ответ №2:

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

 let reqs = datatable (timestamp: datetime, id:string, duration: real, key1:string)
[
  datetime("2020-12-15T08:00:00.000Z"), "r1", 31 , "k1"   // R
];

let deps = datatable (timestamp: datetime, operation_ParentId:string, duration: real,name:string)
[
  datetime("2020-12-15T08:00:00.002Z"), "r1", 7, "D1", 
  datetime("2020-12-15T08:00:00.007Z"), "r1", 8, "D2", 
  datetime("2020-12-15T08:00:00.021Z"), "r1", 6, "D3", 
  datetime("2020-12-15T08:00:00.023Z"), "r1", 2, "D4", 
  datetime("2020-12-15T08:00:00.006Z"), "r1", 4, "D5"
];

let d2 = deps
| where name !in ("D4","D5")
| summarize a=sum(duration)-2
| extend key1="k1";

reqs 
| join d2 on key1
| extend result = duration - a
| project result
 

Результат теста:

введите описание изображения здесь

Комментарии:

1. Спасибо за это. Однако вопрос был о том, как «измерить «собственную» продолжительность запросов (вычитая длительности зависимостей)» в целом, а не о том, как решить этот конкретный пример. Обновление вопроса, чтобы сделать намерение более ясным.