Export vers un stockage objet
Un objet JSON par objectif atteint, dans un bucket qui vous appartient. C’est la voie vers tous les entrepôts de données autres que BigQuery.
1. Le besoin couvert
Jusqu’ici, il n’existait qu’une voie de MousePlayer vers un entrepôt de données, et elle menait à Google. Avec Snowflake, Redshift ou ClickHouse, il n’y en avait aucune, alors que tous trois savent lire un stockage objet sans difficulté.
La passerelle écrit donc dans un stockage objet plutôt que dans chaque entrepôt séparément. Snowflake, Redshift et ClickHouse importent depuis S3, et BigQuery depuis Google Cloud Storage. Un seul export permet de tous les alimenter.
| Destination | Mode d’accès |
|---|---|
| Amazon S3 | Signature Version 4, bucket dans le nom d’hôte |
| Cloudflare R2 | Signature Version 4 vers le point de terminaison de votre compte |
| Google Cloud Storage | via son API compatible S3, avec une clé HMAC |
| MinIO, Wasabi, Backblaze B2 et autres | Signature Version 4 vers votre point de terminaison |
| Azure Blob Storage | un lien SAS de conteneur, sans signature de notre côté |
2. Pourquoi Azure fait exception, et pourquoi c’est préférable
Azure Blob n’utilise pas Signature Version 4, mais SharedKey. Nous aurions pu construire un second mécanisme de signature. La passerelle utilise plutôt un lien SAS de conteneur - une URL que vous générez dans votre compte de stockage, avec la signature déjà intégrée.
Cela signifie moins de code de notre côté et moins de confiance à accorder du vôtre. Nous ne voyons jamais de clé de compte : vous nous confiez une autorisation dont vous avez défini la portée, limitée à un conteneur, avec une date d’expiration choisie et révocable à tout moment sans toucher au reste.
La même retenue est utile côté S3 : autorisez la clé d’accès à écrire uniquement dans ce préfixe précis et nulle part ailleurs. Une clé qui peut seulement ajouter à mouseplayer/goals/ ne peut pas lire vos autres buckets, même en cas de fuite.
3. Un objet par objectif, et une clé fondée sur la date
Le stockage objet ne permet pas d’ajouter des données à un objet existant. Un fichier quotidien devrait être entièrement réécrit à chaque fois, et deux objectifs simultanés s’écraseraient mutuellement. Chaque objectif atteint devient donc un objet distinct.
Cela produit de nombreux petits objets ; c’est l’organisation des clés qui rend cette approche utilisable :
prefix/YYYY/MM/DD/channel/session-goal.json
Un entrepôt lit une journée comme une partition, et une reprise historique consiste à parcourir un préfixe plutôt qu’à lancer une requête. Pour partitionner par projet plutôt que par date, placez l’identifiant du canal dans le préfixe.
Chaque objet est une ligne JSON
Volontairement sans indentation, pour lire tout un préfixe comme du NDJSON sans ouvrir les objets un par un. Horodatage, projet, session, identifiant client, objectif, valeur, devise et lien de relecture de l’enregistrement.
Une valeur absente vaut null, pas zéro
Un objectif sans valeur monétaire écrit null. Dans un entrepôt, un zéro entrerait dans une somme qui n’a jamais existé : la distinction entre « aucun montant convenu » et « un montant nul » est préservée par l’export.
4. En cas de réponse 403, lisez d’abord ceci
Une requête S3 refusée donne très peu d’informations. Notre journal en dit davantage pour une raison précise : la région entre dans la signature. Une région incorrecte et une clé incorrecte produisent la même erreur 403. Le soupçon évident, la clé, n’est donc fondé qu’une fois sur deux.
Trois éléments à vérifier, dans cet ordre :
- La région, exactement telle que le bucket l’indique. R2 et Google Cloud Storage acceptent
autosans le vérifier ; tous les autres le vérifient. - L’autorisation sur le préfixe. Une clé limitée à
mouseplayer/ne peut pas écrire dansmouseplayer-goals/- la barre oblique finale compte dans un ARN de ressource. - Le point de terminaison, pour R2 et les stockages compatibles S3. Collez le nom d’hôte sans
https://; nous le retirons dans tous les cas, mais un chemin ajouté désigne un autre bucket.
L’identifiant de clé d’accès est volontairement stocké en clair. Il accompagne chaque requête signée par conception : le chiffrer ajouterait du travail sans protection. Le secret est chiffré, jamais réaffiché et jamais placé dans la file interne qui transporte la tâche.
Toutes les marques citées appartiennent à leurs propriétaires respectifs. MousePlayer n’entretient aucune relation commerciale avec ces fournisseurs.