Експорт до об’єктного сховища

Один об’єкт JSON на кожну досягнуту ціль у вашому власному бакеті. Це шлях до будь-якого сховища даних, крім BigQuery.

1. Яку прогалину це заповнює

Досі з MousePlayer до сховища даних вів рівно один шлях, і він вів до Google. Для Snowflake, Redshift чи ClickHouse шляху не було, хоча всі три без проблем читають з об’єктних сховищ.

Тому міст записує до об’єктного сховища, а не по черзі до кожного сховища даних. Snowflake отримує дані з S3, Redshift — з S3, ClickHouse — з S3, а BigQuery — з Google Cloud Storage. Один експорт охоплює їх усі.

КудиЯк адресуються запити
Amazon S3Signature Version 4, бакет у назві хоста
Cloudflare R2Signature Version 4 до кінцевої точки вашого облікового запису
Google Cloud Storageчерез сумісний із S3 API, з ключем HMAC
MinIO, Wasabi, Backblaze B2 та іншіSignature Version 4 до вашої кінцевої точки
Azure Blob Storageпосилання SAS на контейнер — узагалі без підписування на нашому боці

2. Чому Azure відрізняється і чому це краще

Azure Blob не використовує Signature Version 4: він підписує через SharedKey. Ми могли б побудувати другу схему підписування. Натомість міст приймає посилання SAS на контейнер — URL, який ви генеруєте у власному обліковому записі сховища з уже вбудованим підписом.

Це менше коду на нашому боці й менше довіри з вашого. Ми взагалі не бачимо ключа облікового запису: ви передаєте дозвіл, який самі обмежили одним контейнером і вибраним строком дії та можете будь-коли відкликати, не зачіпаючи решти.

Такий самий стриманий підхід варто застосувати й до S3: дайте ключу доступу право записувати в цей єдиний префікс і більше ніде. Ключ, який може лише додавати до mouseplayer/goals/, не прочитає інші ваші бакети навіть у разі витоку.

3. Один об’єкт на ціль, а ключ — дата

Об’єктне сховище не має дописування. Щоденний файл довелося б повністю переписувати щоразу, а дві одночасно досягнуті цілі перезаписували б одна одну. Тому кожна досягнута ціль стає окремим об’єктом.

Ціна — багато малих об’єктів; саме структура ключів робить це придатним до роботи:

prefix/YYYY/MM/DD/channel/session-goal.json

Сховище даних читає один день як одну партицію, а дозавантаження історії — це сканування префікса, а не запит. Якщо хочете поділяти за проєктом, а не датою, додайте ідентифікатор каналу до префікса.

Кожен об’єкт — один рядок JSON

Навмисно без відступів, щоб увесь префікс читався як NDJSON без відкриття кожного об’єкта окремо. Часова позначка, проєкт, сеанс, ідентифікатор клієнта, ціль, цінність, валюта й посилання, яке відтворює запис.

Порожня цінність — null, а не нуль

Ціль без грошової цінності записує null. У сховищі даних нуль увійшов би до суми, якої ніколи не існувало, — відмінність між «суму не погоджено» й «сума дорівнює нулю» зберігається під час експорту.

4. Якщо відповідь 403, спочатку прочитайте це

Відхилений запит S3 майже нічого не пояснює, і є причина, чому повідомлення в нашому журналі говорить більше за S3: регіон входить до підпису. Неправильний регіон і неправильний ключ дають однакову відповідь 403, тож очевидна підозра — ключ — справджується лише в половині випадків.

Три перевірки в такому порядку:

  • Регіон — точно такий, як повідомляє бакет. R2 і Google Cloud Storage приймають auto і не перевіряють його; усі інші перевіряють.
  • Дозвіл для префікса. Ключ, обмежений mouseplayer/, не може записувати до mouseplayer-goals/ — кінцева похила риска має значення в ARN ресурсу.
  • Кінцева точка — для R2 і сховищ, сумісних із S3. Вставте хост без https://; ми в будь-якому разі прибираємо це, але дописаний шлях — уже інший бакет.

Ідентифікатор ключа доступу навмисно зберігається відкрито. За задумом він передається з кожним підписаним запитом, тож його шифрування було б зусиллям без захисту. Секрет зашифрований, більше не показується й ніколи не потрапляє у внутрішню чергу завдань.

Усі згадані тут торговельні марки належать їхнім відповідним власникам. MousePlayer не має ділових відносин із цими постачальниками.