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.

DestinationMode d’accès
Amazon S3Signature Version 4, bucket dans le nom d’hôte
Cloudflare R2Signature Version 4 vers le point de terminaison de votre compte
Google Cloud Storagevia son API compatible S3, avec une clé HMAC
MinIO, Wasabi, Backblaze B2 et autresSignature Version 4 vers votre point de terminaison
Azure Blob Storageun 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 auto sans le vérifier ; tous les autres le vérifient.
  • L’autorisation sur le préfixe. Une clé limitée à mouseplayer/ ne peut pas écrire dans mouseplayer-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.