Експорт до об’єктного сховища
Один об’єкт JSON на кожну досягнуту ціль у вашому власному бакеті. Це шлях до будь-якого сховища даних, крім BigQuery.
1. Яку прогалину це заповнює
Досі з MousePlayer до сховища даних вів рівно один шлях, і він вів до Google. Для Snowflake, Redshift чи ClickHouse шляху не було, хоча всі три без проблем читають з об’єктних сховищ.
Тому міст записує до об’єктного сховища, а не по черзі до кожного сховища даних. Snowflake отримує дані з S3, Redshift — з S3, ClickHouse — з S3, а BigQuery — з Google Cloud Storage. Один експорт охоплює їх усі.
| Куди | Як адресуються запити |
|---|---|
| Amazon S3 | Signature Version 4, бакет у назві хоста |
| Cloudflare R2 | Signature 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 не має ділових відносин із цими постачальниками.