Eksport do magazynu obiektów

Jeden obiekt JSON na każdy osiągnięty cel, w buckecie, który należy do Ciebie. To droga do każdej hurtowni, która nie jest BigQuery.

1. Lukę, którą to zamyka

Do tej pory z MousePlayera do hurtowni danych prowadziła dokładnie jedna droga, i wiodła do Google. Kto prowadzi Snowflake, Redshift albo ClickHouse, nie miał żadnej - choć wszystkie trzy bez problemu czytają z magazynu obiektów.

Mostek zapisuje więc do magazynu obiektów, zamiast po kolei do każdej hurtowni. Snowflake czyta z S3, Redshift czyta z S3, ClickHouse czyta z S3, a BigQuery z Google Cloud Storage. Jeden eksport dociera do wszystkich.

DokądJak jest adresowane
Amazon S3Signature Version 4, bucket w hoście
Cloudflare R2Signature Version 4 wobec punktu końcowego Twojego konta
Google Cloud Storageprzez API zgodne z S3, z kluczem HMAC
MinIO, Wasabi, Backblaze B2 i inneSignature Version 4 wobec Twojego punktu końcowego
Azure Blob Storagelink SAS do kontenera - żadnego podpisywania po naszej stronie

2. Dlaczego Azure jest wyjątkiem - i dlaczego to lepiej

Azure Blob nie mówi w Signature Version 4; podpisuje przez SharedKey. Moglibyśmy zbudować drugi mechanizm podpisu. Zamiast tego mostek przyjmuje link SAS do kontenera - adres, który tworzysz we własnym koncie magazynu i w którym podpis już się znajduje.

To mniej kodu po naszej stronie i mniej zaufania po Twojej. W ogóle nie widzimy klucza konta: przekazujesz uprawnienie, które sam przyciąłeś - do jednego kontenera, z wybraną przez siebie datą wygaśnięcia, odwoływalne w każdej chwili bez dotykania czegokolwiek innego.

Ta sama powściągliwość opłaca się po stronie S3: nadaj kluczowi prawo zapisu do dokładnie tego jednego prefiksu i niczego więcej. Klucz, który może tylko dopisywać do mouseplayer/goals/, nie odczyta Twoich innych bucketów nawet wtedy, gdy wycieknie.

3. Jeden obiekt na cel, a klucz to data

Magazyn obiektów nie zna dopisywania. Plik dzienny trzeba by za każdym razem zapisywać od nowa w całości, a dwa cele docierające jednocześnie nadpisywałyby się nawzajem. Każdy osiągnięty cel staje się więc własnym obiektem.

Ceną jest wiele małych obiektów; użytecznym czyni to budowa klucza:

prefiks/RRRR/MM/DD/kanal/sesja-cel.json

Hurtownia czyta jeden dzień jako jedną partycję, a douzupełnienie danych to przejście po prefiksie, a nie zapytanie. Kto chce partycjonować po projekcie zamiast po dacie, wstawia identyfikator kanału do prefiksu.

Każdy obiekt to jedna linia JSON

Celowo bez wcięć, żeby cały prefiks dało się czytać jako NDJSON, bez otwierania obiektów po kolei. Znacznik czasu, projekt, sesja, identyfikator klienta, cel, wartość, waluta i odnośnik odtwarzający nagranie.

Brak wartości to null, a nie zero

Cel bez wartości pieniężnej zapisuje null. W hurtowni zero zsumowałoby się do kwoty, której nigdy nie było - różnica między „nie uzgodniono kwoty" a „kwota zero" przeżywa eksport.

4. Jeśli przyjdzie 403, przeczytaj najpierw to

Odrzucone żądanie S3 mówi niemal nic, i nie bez powodu komunikat w naszym dzienniku mówi więcej niż samo S3: region wchodzi do podpisu. Błędny region i błędny klucz dają ten sam 403 - oczywiste podejrzenie, czyli klucz, trafia więc tylko w połowie przypadków.

Trzy rzeczy do sprawdzenia, w tej kolejności:

  • Region, dokładnie taki, jaki zgłasza bucket. R2 i Google Cloud Storage przyjmują auto i go nie sprawdzają; wszystkie pozostałe tak.
  • Uprawnienie do prefiksu. Klucz do mouseplayer/ nie może zapisywać do mouseplayer-goals/ - końcowy ukośnik ma znaczenie w ARN zasobu.
  • Punkt końcowy, przy R2 i magazynach zgodnych z S3. Wklej host bez https://; i tak go obcinamy, ale dopisana ścieżka to inny bucket.

Identyfikator klucza dostępu leży świadomie otwartym tekstem. Z założenia podróżuje w każdym podpisanym żądaniu, więc szyfrowanie go byłoby wysiłkiem bez ochrony. Klucz tajny jest zaszyfrowany, nigdy więcej nie pokazywany i nigdy nie trafia do wewnętrznej kolejki niosącej zadanie.

Wszystkie wymienione znaki towarowe należą do ich właścicieli. MousePlayer nie pozostaje w żadnych relacjach biznesowych z tymi dostawcami.