У наш час практычна кожная кампанія ў міры збірае статыстыку аб дзеяннях карыстача на web рэсурсе. Матывацыя зразумелая - кампаніі жадаюць ведаць як выкарыстоўваецца іх прадукт/вэб сайт і лепш разумець сваіх карыстачоў. Вядома на рынку існуе вялікая колькасць інструментаў для вырашэння дадзенай праблемы - ад сістэм аналітыкі, якія падаюць дадзеныя ў выглядзе дашбордаў і графікаў (напрыклад
Але мы знайшлі праблему, якая яшчэ не была вырашана. Так нарадзіўся
Навошта нам распрацоўваць уласны сервіс?
Гэта былі дзевяностыя, мы выжывалі як маглі. 2019 год, мы распрацоўвалі API First Customer Data Platform kSense, якая дазваляла агрэгаваць дадзеныя з розных крыніц (Facebook ads, Stripe, Salesforce, Google play, Google Analytics і інш) для зручнейшага аналізу дадзеных, выяўленні залежнасцяў і г.д. Мы заўважылі, што шматлікія карыстачы выкарыстоўваюць нашу платформу для аналізу дадзеных менавіта Google Analytics (далей GA). З некаторымі карыстальнікамі мы пагаварылі і высветлілі, што ім патрэбныя дадзеныя аналітыкі іх прадукта, якія яны атрымліваюць з дапамогай GA, але
Яны ўсталёўвалі Segment javascript піксель на свой web рэсурс і дадзеныя аб паводзінах іх карыстачоў загружаліся ў паказаную базу дадзеных (напрыклад Postgres). Але і ў Segment ёсць свой мінус – кошт. Напрыклад, калі ў інтэрнэт рэсурсу 90,000 MTU (monthly tracked users) то трэба аплаціць у касу ~1,000 $ у месяц. Таксама была і трэцяя праблема - некаторыя пашырэнні для браўзэра (такія як AdBlock) блакавалі збор аналітыкі т.к. http запыты з браўзэра адпраўляліся на дамены GA і Segment. Зыходзячы з жадання нашых кліентаў, мы зрабілі сэрвіс аналітыкі, які збірае поўны набор даных (без сэмплінга), бясплатны і можа працаваць на ўласнай інфраструктуры.
Як уладкованы сэрвіс
Сэрвіс складаецца з трох частак: javascript піксель (які мы пасля перапісалі на typescript), серверная частка рэалізаваная на мове GO і ў якасці in-house базы дадзеных планавалася выкарыстоўваць Redshift і BigQuery (пазней дадалі падтрымку Postgres, ClickHouse і Snowflake).
Структуру падзей GA і Segment вырашылі пакінуць без змены. Усё, што было патрэбна, гэта дубліраваць усе падзеі з web рэсурсу, дзе ўсталяваны піксель, у наш бэкенд. Як аказалася, гэта зрабіць нескладана. Javascript піксель перавызначаў арыгінальны метад бібліятэкі GA на новы, які дубліраваў падзею ў нашу сістэму.
//'ga' - стандартное название переменной Google Analytics
if (window.ga) {
ga(tracker => {
var originalSendHitTask = tracker.get('sendHitTask');
tracker.set('sendHitTask', (model) => {
var payLoad = model.get('hitPayload');
//отправка оригинального события в GA
originalSendHitTask(model);
let jsonPayload = this.parseQuery(payLoad);
//отправка события в наш сервис
this.send3p('ga', jsonPayload);
});
});
}
З пікселем Segment ўсё прасцей, ен мае middleware метады, адным з іх мы і скарысталіся.
//'analytics' - стандартное название переменной Segment
if (window.analytics) {
if (window.analytics.addSourceMiddleware) {
window.analytics.addSourceMiddleware(chain => {
try {
//дублирование события в наш сервис
this.send3p('ajs', chain.payload);
} catch (e) {
LOG.warn('Failed to send an event', e)
}
//отправка оригинального события в Segment
chain.next(chain.payload);
});
} else {
LOG.warn("Invalid interceptor state. Analytics js initialized, but not completely");
}
} else {
LOG.warn('Analytics.js listener is not set.');
}
Апроч капіявання падзей мы дадалі магчымасць адпраўляць адвольны json:
//Отправка событий с произвольным json объектом
eventN.track('product_page_view', {
product_id: '1e48fb70-ef12-4ea9-ab10-fd0b910c49ce',
product_price: 399.99,
price_currency: 'USD'
product_release_start: '2020-09-25T12:38:27.763000Z'
});
Далей пагаворым пра серверную частку. Backend павінен прымаць http запыты, напаўняць іх дадатковай інфармацыяй, да прыкладу, геа дадзенымі (дзякуй
//входящий json
{
"field_1": {
"sub_field_1": "text1",
"sub_field_2": 100
},
"field_2": "text2",
"field_3": {
"sub_field_1": {
"sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
}
}
}
//результат
{
"field_1_sub_field_1": "text1",
"field_1_sub_field_2": 100,
"field_2": "text2",
"field_3_sub_field_1_sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
}
Аднак масівы на дадзены момант проста канвертуюцца ў радок т.я. не ўсе рэляцыйныя базы дадзеных падтрымліваюць паўтаральныя палі (repeated fields). Таксама ёсць магчымасць змяняць назвы палёў ці выдаляць іх з дапамогай апцыянальных правіл мапінгу. Яны дазваляюць змяняць схему дадзеных, калі гэта запатрабуецца ці прыводзіць адзін тып дадзеных да іншага. Напрыклад, калі ў json поле знаходзіцца радок з timestamp (field_3_sub_field_1_sub_sub_field_1 з прыкладу вышэй) то для таго каб стварыць поле ў базе дадзеных з тыпам timestamp, неабходна напісаць правіла мапінгу ў канфігурацыі. Іншымі словамі, тып дадзеных поля вызначаецца спачатку па json значэнню, а затым ужываецца правіла прывядзення тыпаў (калі яно сканфігуравана). Мы вылучылі 4 асноўных тыпу даных: STRING, FLOAT64, INT64 і TIMESTAMP. Правілы мапінгу і прывядзенні тыпаў выглядаюць наступным чынам:
rules:
- "/field_1/subfield_1 -> " #правило удаления поля
- "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
- "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа
Алгарытм вызначэння тыпу дадзеных:
- пераўтварэнне json структуры ў плоскую структуру
- вызначэнне тыпу дадзеных палёў па значэннях
- прымяненне правілаў мапінгу і прывядзення тыпаў
Тады з уваходнай json структуры:
{
"product_id": "1e48fb70-ef12-4ea9-ab10-fd0b910c49ce",
"product_price": 399.99,
"price_currency": "USD",
"product_type": "supplies",
"product_release_start": "2020-09-25T12:38:27.763000Z",
"images": {
"main": "picture1",
"sub": "picture2"
}
}
будзе атрымана схема дадзеных:
"product_id" character varying,
"product_price" numeric (38,18),
"price_currency" character varying,
"product_type" character varying,
"product_release_start" timestamp,
"images_main" character varying,
"images_sub" character varying
Таксама мы падумалі, што карыстач павінен мець магчымасць наладзіць партыцыянаванне або падзяляць дадзеныя ў БД па іншых крытэрыях і рэалізавалі магчымасць задаваць імя табліцы канстантай або
tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'
Аднак структура ўваходных падзей можа змяняцца ў runtime. Мы рэалізавалі алгарытм праверкі розніцы паміж структурай існуючай табліцы і структурай уваходнай падзеі. Калі розніца знойдзена - табліца будзе абноўлена новымі палямі. Для гэтага выкарыстоўваецца patch SQL запыт:
#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying
Архітэктура
Навошта трэба запісваць падзеі на файлавую сістэму, а не проста пісаць іх адразу ў БД? Базы дадзеных не заўсёды дэманструюць высокую прадукцыйнасць пры вялікай колькасці ўставак (
Open Source і планы на будучыню
У нейкі момант сэрвіс стаў падобны на паўнавартасны прадукт і мы вырашылі выкласці яго ў Open Source. На дадзены момант рэалізаваны інтэграцыі з Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Усе інтэграцыі падтрымліваюць як batch, так і streaming рэжымы загрузкі даных. Дададзена падтрымка запытаў праз API.
Бягучая інтэграцыйная схема выглядае наступным чынам:
Нягледзячы на тое, што сэрвіс можна выкарыстоўваць самастойна (напрыклад з дапамогай Docker), у нас таксама ёсць
→
→
→
Будзем рады калі EventNative дапаможа вырашыць вашыя задачы!
Толькі зарэгістраваныя карыстачы могуць удзельнічаць у апытанні.
Якая сістэма збору статыстыкі выкарыстоўваецца ў вашай кампаніі
-
48,0%Google Analytics12
-
4,0%Segment1
-
16,0%Іншую (напішыце ў каментарах)4
-
32,0%Рэалізавалі свой сервіс8
Прагаласавалі 25 карыстальнікаў. Устрымаліся 6 карыстальнікаў.
Крыніца: habr.com