Um Data Lake, Três Nuvens: Analytics Multi-Cloud com DataSpoc
Sua empresa adquiriu uma startup que roda no GCP. Sua plataforma principal está na AWS. A equipe financeira armazena exports no Azure Blob Storage. Agora você precisa unir dados de clientes das três nuvens para um único relatório.
O DataSpoc Lens registra buckets do S3, GCS e Azure como um catálogo unificado. O DuckDB consulta todos eles com SQL padrão. Uma consulta, três nuvens, zero movimentação de dados.
O Cenário
Uma empresa de médio porte com dados espalhados entre provedores de nuvem:
| Nuvem | Bucket | Dados | Equipe |
|---|---|---|---|
| AWS S3 | s3://acme-production | Dados core do app (usuários, pedidos) | Engenharia |
| GCS | gs://acme-acquired-app | Dados do produto adquirido | Ex-equipe da startup |
| Azure | az://acme-finance | Exports financeiros, faturas | Financeiro |
Cada equipe escolheu sua nuvem. Mover tudo para um único provedor é um projeto de 6 meses. Você precisa de analytics agora.
Passo 1: Registrar Todos os Buckets
# AWS S3 bucketdataspoc-lens add-bucket s3://acme-production --name production
# Google Cloud Storage bucketdataspoc-lens add-bucket gs://acme-acquired-app --name acquired
# Azure Blob Storage bucketdataspoc-lens add-bucket az://acme-finance/data --name financeVerifique que os três estão conectados:
dataspoc-lens tablesproduction.raw.usersproduction.raw.ordersproduction.raw.productsproduction.curated.daily_revenueacquired.raw.app_usersacquired.raw.app_eventsacquired.raw.subscriptionsfinance.raw.invoicesfinance.raw.paymentsfinance.raw.budget_forecastTrês nuvens, um catálogo.
Passo 2: Configurar Credenciais para Cada Provedor
AWS S3
Use um perfil AWS ou variáveis de ambiente:
# Option 1: AWS profile (recommended)export AWS_PROFILE=production
# Option 2: Explicit credentialsexport AWS_ACCESS_KEY_ID="AKIA..."export AWS_SECRET_ACCESS_KEY="..."export AWS_REGION="us-east-1"Ou use IAM roles se estiver rodando no EC2/ECS — nenhuma credencial necessária.
Google Cloud Storage
Use uma chave de service account ou credenciais padrão da aplicação:
# Option 1: Service account keyexport GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
# Option 2: Application default credentials (if on GCP or gcloud configured)gcloud auth application-default loginAzure Blob Storage
Use uma chave de storage account ou managed identity:
# Option 1: Storage account keyexport AZURE_STORAGE_ACCOUNT="acmefinance"export AZURE_STORAGE_KEY="..."
# Option 2: Connection stringexport AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;AccountName=..."
# Option 3: Managed identity (if running on Azure)# No env vars needed -- uses Azure Instance MetadataO DataSpoc Lens detecta o provedor pelo prefixo da URI do bucket (s3://, gs://, az://) e usa as credenciais correspondentes.
Passo 3: Consultas Cross-Cloud
Agora a parte interessante. Consulte dados de todas as três nuvens com uma única instrução SQL:
-- Match acquired app users with production users by emailSELECT p.name AS production_name, p.email, p.plan AS production_plan, a.subscription_tier AS acquired_plan, a.last_active AS acquired_last_active, f.total_invoicedFROM production.raw.users pJOIN acquired.raw.app_users a ON LOWER(p.email) = LOWER(a.email)LEFT JOIN ( SELECT customer_email, SUM(amount) AS total_invoiced FROM finance.raw.invoices GROUP BY customer_email) f ON LOWER(p.email) = LOWER(f.customer_email)ORDER BY f.total_invoiced DESC NULLS LASTLIMIT 20;Esta consulta faz join de dados AWS com dados GCS e dados Azure em uma única instrução. O DuckDB cuida das leituras cross-cloud de forma transparente.
Execute:
dataspoc-lens query " SELECT p.name, p.email, p.plan, a.subscription_tier, f.total_invoiced FROM production.raw.users p JOIN acquired.raw.app_users a ON LOWER(p.email) = LOWER(a.email) LEFT JOIN ( SELECT customer_email, SUM(amount) AS total_invoiced FROM finance.raw.invoices GROUP BY customer_email ) f ON LOWER(p.email) = LOWER(f.customer_email) ORDER BY f.total_invoiced DESC NULLS LAST LIMIT 10"┌─────────────────┬──────────────────────┬────────┬───────────────────┬────────────────┐│ name │ email │ plan │ subscription_tier │ total_invoiced │├─────────────────┼──────────────────────┼────────┼───────────────────┼────────────────┤│ Acme Corp │ admin@acme.com │ enterprise │ premium │ 125,400.00 ││ TechFlow Inc │ ops@techflow.io │ business │ pro │ 89,200.00 ││ Global Systems │ data@globalsys.com │ business │ premium │ 67,800.00 │└─────────────────┴──────────────────────┴────────┴───────────────────┴────────────────┘Passo 4: Cache para Performance
Consultas cross-cloud podem ser lentas porque dados são lidos pela rede de três provedores diferentes. O cache resolve isso:
# Cache the most-queried tables locallydataspoc-lens cache refresh production.raw.usersdataspoc-lens cache refresh production.raw.ordersdataspoc-lens cache refresh acquired.raw.app_usersdataspoc-lens cache refresh finance.raw.invoicesVerifique o status do cache:
dataspoc-lens cache statusTable Status Size Last Refreshedproduction.raw.users cached 1.2 MB 2 min agoproduction.raw.orders cached 8.4 MB 2 min agoacquired.raw.app_users cached 3.1 MB 1 min agofinance.raw.invoices cached 5.6 MB 1 min agoproduction.raw.products stale 42 KB 3 days agoacquired.raw.app_events not cached -- --Consultas em cache rodam contra arquivos Parquet locais — sem chamadas de rede, sem latência cross-cloud. O mesmo JOIN cross-cloud que levava 12 segundos sem cache roda em 200ms com cache.
Atualize caches desatualizados em um comando:
dataspoc-lens cache refresh-stalePasso 5: Controle de Acesso por Bucket via IAM
O DataSpoc nunca implementa autenticação. O IAM de cada provedor de nuvem controla quem pode acessar o quê:
AWS S3 (production): └── IAM Policy: DataEngineers group → full access └── IAM Policy: Analysts group → read-only
GCS (acquired): └── IAM Policy: IntegrationTeam → read-only └── IAM Policy: AcquiredTeam → full access
Azure (finance): └── IAM Policy: FinanceTeam → full access └── IAM Policy: Executives → read-onlyQuando um analista executa uma consulta que toca o bucket financeiro, ele precisa de credenciais Azure com acesso de leitura. Se não tiver, a consulta falha com um erro de permissão. O DataSpoc não tenta contornar o IAM da nuvem — ele respeita.
Exemplo Prático: Customer 360 Unificado
Construa uma visão completa do cliente cruzando as três nuvens:
-- transforms/01_customer_360.sqlCREATE OR REPLACE TABLE gold.customer_360 ASWITH production_data AS ( SELECT LOWER(email) AS email, name, plan AS production_plan, created_at AS production_signup, DATE_DIFF('day', created_at, CURRENT_DATE) AS days_as_customer FROM production.raw.users),acquired_data AS ( SELECT LOWER(email) AS email, subscription_tier AS acquired_plan, last_active AS acquired_last_active, feature_usage_score FROM acquired.raw.app_users),finance_data AS ( SELECT LOWER(customer_email) AS email, SUM(amount) AS total_invoiced, COUNT(*) AS invoice_count, MAX(invoice_date) AS last_invoice_date FROM finance.raw.invoices WHERE status = 'paid' GROUP BY 1),order_data AS ( SELECT LOWER(u.email) AS email, COUNT(*) AS total_orders, SUM(o.amount) AS total_revenue, MAX(o.order_date) AS last_order_date FROM production.raw.orders o JOIN production.raw.users u ON o.user_id = u.user_id WHERE o.status = 'completed' GROUP BY 1)SELECT p.email, p.name, p.production_plan, p.production_signup, p.days_as_customer, a.acquired_plan, a.acquired_last_active, a.feature_usage_score, f.total_invoiced, f.invoice_count, o.total_orders, o.total_revenue, o.last_order_date, CASE WHEN a.email IS NOT NULL THEN 'both_products' ELSE 'production_only' END AS product_usage, COALESCE(o.total_revenue, 0) + COALESCE(f.total_invoiced, 0) AS total_ltvFROM production_data pLEFT JOIN acquired_data a ON p.email = a.emailLEFT JOIN finance_data f ON p.email = f.emailLEFT JOIN order_data o ON p.email = o.email;Execute este transform:
dataspoc-lens transform run --file 01_customer_360.sqlAgora consulte a visão unificada:
dataspoc-lens query " SELECT product_usage, COUNT(*) as customers, ROUND(AVG(total_ltv), 2) as avg_ltv FROM gold.customer_360 GROUP BY product_usage"┌────────────────┬───────────┬──────────┐│ product_usage │ customers │ avg_ltv │├────────────────┼───────────┼──────────┤│ both_products │ 2,340 │ 1,245.80 ││ production_only│ 10,110 │ 342.50 │└────────────────┴───────────┴──────────┘Clientes usando ambos os produtos têm LTV 3,6x maior. Essa é uma oportunidade de cross-sell que você só poderia encontrar consultando entre nuvens.
Uso do SDK
A mesma configuração multi-cloud funciona a partir do Python:
from dataspoc_lens import LensClient
lens = LensClient()
# Tables from all clouds appear in one catalogtables = lens.tables()print(tables)# ['production.raw.users', 'acquired.raw.app_users', 'finance.raw.invoices', ...]
# Cross-cloud queryresult = lens.query(""" SELECT COUNT(DISTINCT p.email) AS overlap FROM production.raw.users p JOIN acquired.raw.app_users a ON LOWER(p.email) = LOWER(a.email)""")print(f"Users on both platforms: {result}")Considerações de Custo
Transferência de dados cross-cloud custa dinheiro. Veja o que esperar:
| Transferência | Custo por GB |
|---|---|
| S3 para internet | $0,09 |
| GCS para internet | $0,12 |
| Azure para internet | $0,087 |
| Cache local (após primeira leitura) | $0,00 |
Estratégia para minimizar custos:
- Cache agressivo — primeira consulta lê da nuvem, consultas subsequentes leem do cache local
- Refresh de cache agendado —
dataspoc-lens cache refresh-staleuma vez por hora - Pré-agregar — construa tabelas gold que resumem dados cross-cloud, cache apenas as tabelas gold
- Filtre cedo — cláusulas
WHEREno pushdown de Parquet reduzem bytes lidos
Para uma empresa com 10 GB em três nuvens, atualizar cache diariamente custa cerca de $3/mês em egress. Isso é mais barato que um único crédito Snowflake.
Um catálogo, três nuvens, zero movimentação de dados. Registre seus buckets, escreva SQL e deixe o DuckDB cuidar do resto.