VRackDB — Просто и со вкусом

VRackDB — это простая In Memory Graphite like база данных, предназначенная для хранения временных рядов (графиков). (TypeScript)

Особенности:

  • Очень простая/экономичная/быстрая

  • Хранит данные в памяти. При закрытии программы данные будут потеряны

  • Резервирует память метрики, последующее добавление данных метрики не увеличивает потребление памяти

  • Всегда возвращает данные в виде набора данных для графика

  • Имеет простой формат запросов

  • Агрегация и модификаторы данных

  • Оперирует временем в целых секундах

Как она может помочь?

Допустим, вам нужно строить график на момент времени работы приложения. Например, график загрузки файла.

Решение, которое первым приходит в голову — складывать каждые 5 секунд данные в массив. Вариант будет работать, если количество точек в вашем графике статично. В график нельзя засунуть сколько угодно точек — это даже звучит неправильно.

Если складывать данные в массив каждые 5 секунд, то на графике будет 300 точек, соответственно, будет помещаться всего 25 минут. А если загрузка будет идти 2 дня? Что же тогда делать?

Можно упрощать график. Убирать приближенные друг к другу значения, а при получении результата, приводить к 300 точкам. Правда, при получении данных, вам каждый раз надо будет перебирать весь массив, а результат будет нелинейным во времени.

И вот, казалось бы, задачка простая, а простое решение не так уж и просто найти. Но VRackDB легко справляется с этой задачей.

Давайте глянем как это сделать:

import { Database } from "vrack-db";
// или
const { Database } = require('vrack-db')
// Создаем инстанс базы данных
const DB = new Database()

DB.scheme('test', 'test.metric.*', '5s:1h, 15s:3h, 1m:24h, 5m:1w, 15m:1mon, 1h:1y')

const now = Math.floor(Date.now() / 1000)

// Делаем вид, что мы пишем что-то полезное в базу 
for(let i = 1; i <= 300; i++){
  DB.write(
    'test.metric.id', // Идентификатор метрики
    Math.sin(i/100), // Значение
    (i * 5) + now  // Время
  )
}

/* Вернет что-то типа 
{
    start: 1714994924,
    end: 1714995220,
    relevant: true,
    rows: [ {time: 1714994924, value. 0.2312305}}, {…}, {…}, … ]
​} */
const result = DB.readAll('test.metric.id', 300)

В итоге мы получим вот такой график:

b23199d3d54e0fc79397fb279b68413e.png

Если мы накинем данных до 6 часов:

b0861f183751cfa100c6188900f756f0.png

Теперь сутки:

e483474c30878e220a6b696bbe5dedcb.png

Ну и трое суток:

f67f5461a29544fafc70b8ca3bff8140.png

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

Давайте посмотрим на особенности базы для решения этой задачи:

  • Память для метрики резервируется один раз и последующее добавление данных этой метрики не приводит к увеличению потребления памяти

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

  • Для получения данных за определенный период нет необходимости перебирать все данные. База знает где лежат данные по времени

  • Вы можете выбирать нужный вам интервал, на который будет поделен график. Вы можете просто указать сколько точек на график вы хотите получить, независимо от размера периода

Вот примеры применения VRackDB:

  • Построение графика скачивания/копирования/выполнения работы

  • Анализ потребления памяти приложения/CPU/GPU/других метрик производительности

  • Диагностика задержек HTTP/API/WebScoket и других запросов

  • Количественный анализ успешных/не успешных операций в интервал времени

  • Приложения для SOC компьютеров и устройств на их основе (мультиметры и т.п.)

  • Кеширование данных для быстрого отображения/анализа графиков/упрощения данных

Мой публичный проект, который использует VRackDB — VGranite — софт для преобразователя интерфейсов serial<->ethernet. VGranite строит графики траффика общения между элементами сети внутри себя. Хоть это и мелочь, но очень приятная, и позволяет быстро оценить динамику стабильности работы.

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

База данных хранит все данные в памяти, что не мучает ваш SSD запросами мелких операций записи. Сейчас стараюсь внедрять эту базу там, где это уместно. Не всегда хочется тратить ресурс хранилища на оценочные метрики. А вот ресурс оперативной памяти в целом не жалко. Даже если я потрачу ~100МБ оперативы на 1000 метрик — для сервера это копейки.

VRackDB, можно сказать, не имеет зависимости. Единственная ее зависимость это Buffer, для запуска в браузере с использованием, например, vuejs. Хотя сейчас я сомневаюсь, что это хорошее решение, возможно, в будущем откажусь от зависимости. Идея была использовать ее с electron. Но в electron можно держать базу и на стороне бэка. Поэтому, как пойдет дальше — видно будет, может и откажусь.

Ну что? Если вас не заинтересовало — можно дальше и не читать.

Схемы

Чтобы начать работать с базой, как минимум нужно разобраться со схемами. Схемы определяют как долго и с какой точностью будут храниться данные.

Дело в том, что в VRackDB метрики хранятся на слоях, которые имеют разную точность (интервал) и общий период. Таким образом достигается высокая точность актуальных данных и сохраняется тренд более старых данных.

Пример добавление схемы:

DB.scheme('test', 'test.name.*', '5s:10m, 1m:2h')
  • test — Название схемы — это именованная коллекция метрик

  • test.name.* — Все метрики с маской test.name.* будут попадать в схему test.

  • 5s:10m, 1m:2h — Указания точности и размера периода хранения метрик.

Что это за 5s:10m, 1m:2h? Каждое значение через запятую добавляет в схему слой с определенной точностью (интервалом) и общим периодом.

Так что же такое слой? Слой — это хранилище данных, которое распределяет данные внутри себя по ячейкам памяти в зависимости от его длины и интервала. Вот как можно представить визуально слои 15m:6h, 1h:1d (15 мин:6 часов, 1 час: 1день):

ce299a3c34fb5003c3c8cb8d654edd24.png

Время записи всегда приводится к точности слоя (интервалу слоя). В слой 15m:6h нельзя записать данные чаще 15 минут. При попытке записи чаще, чем интервал — данные будут перезаписаны, используя модификатор (по умолчанию last — последнее значение заменяет прошлое).

Слой 15m:6h не может хранить данные дольше 6 часов. Новые данные будут смещать старые, удаляя их из слоя.

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

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

Значение типа 5s, 10m, 2h, являются интервалами, которые можно расшифровывать как 5 секунд, 10 минут, 2 часа.

Вот список поддерживаемых интервалов:

  • s — секунды

  • m — минуты

  • h — часы

  • d — дни

  • w — недели

  • mon — месяцы

  • y — годы

Интервалы начинаются с целого числа, после которого сразу идет тип интервала, например 1m, 10d, 120s, 1y.

Вот некоторые рекомендации при указании точности схемы:

  • Cлои располагаются друг над другом — если ваш самый длинный слой вмещает в себя год, все, что дольше года, вы будете терять.

  • Не стоит делать точные слои слишком длинными — в этом нет смысла. Точные и длинные слои можно делать только в некоторых редких случаях.

  • Нет смысла делать более точный слой длиннее менее точного.

  • Не стоит делать слишком много слоев (норма 6–8 слоев).

Если название метрики не подходит ни под один паттерн, будет использована схема по умолчанию. Она рассчитана примерно на 120 точек на слой, что достаточно мало. Рекомендуется создавать схемы самому под свои задачи.

Что дальше?

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

Я постарался описать Как это работает. Желающим понять как работает база и написать, например, нечто похожее на другом языке — очень рекомендую заглянуть именно сюда.

Сама база данных поставляется вместе с исходными кодами. Иногда бывает полезно глянуть именно исходник на TypeScript. Исходный код написан максимально просто и понятно, с вменяемым оформлением. Распространяется по лицензии Apache 2.0.

Развитие базы идет, она становится все более взрослой. С версии 2.0 был добавлен компонент ErrorManager. Теперь в базе все ошибки стали полностью формализованными. Вызовы ошибок можно найти в исходнике по коду ошибки. Обычно перед каждым классом идет определение, какие ошибки этот класс может вызывать.

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

Может кому пригодится. Намучившись с разными библиотеками отображения графиков, нашел для себя Apache ECharts. Для vue3 можно использовать vue-echarts. Мы на его основе рисуем красивые графики по типу таких:

a004d2bcaf9a9327f407d4ef1afa3e2a.png

Они в онлайн режиме могут полностью обновляться, вместе с настройками отображения, зонами, маркерами и т.п. Можно настроить так, чтобы были очень похожи на Grafana. Кстати, на графике выше, данные получены из VRackDB.

Телеграм канала не будет, уж простите — на этом все.

© Habrahabr.ru