← Professional Data Engineer: pipelines e decisões de dados
14 / 18 · 135 MIN

Alterar contratos, segurança e consumidores

Avalia o impacto de mudanças nos consumidores, verifica segurança e prepara migrações com critérios de aceitação e retorno.

1. Definir a mudança e a fronteira de aceitação

Uma alteração de arquitetura pode preservar os nomes das tabelas e quebrar um processo de negócio. Num exemplo fictício APS, uma equipa substitui montantes inteiros em cêntimos por decimais em euros e acrescenta uma classificação de risco. O dashboard diário continua a abrir, mas o fecho financeiro interpreta a unidade antiga. Antes de planear a janela, identifica produtores, consumidores, unidades, granularidade, identificadores e responsáveis pela aceitação. Estes exemplos não representam procedimentos BNP Paribas. Escreve o contrato anterior e o proposto separadamente. Regista o que cada consumidor exige, incluindo se aceita campos adicionais. Um exportador mensal pode estar ausente dos testes de uma semana; a ausência de atividade não prova ausência de dependência. Combina inventário técnico, observação e confirmação dos responsáveis. Para cada lacuna, indica quem a deve esclarecer e como condiciona a decisão. Uma entrega utilizável inclui um resultado verificável: o fecho recebe os movimentos esperados, calcula os valores na unidade correta e respeita o prazo acordado. Um ficheiro bem formado cobre apenas parte dessa evidência. Separa aceitação do contrato, reconciliação dos dados e comportamento da aplicação. Esta separação permite localizar falhas sem chamar “sucesso” a uma execução que apenas terminou.

2. Alterar segurança sem perder o âmbito

Uma aplicação externa com fornecedor de identidade compatível pode usar Workload Identity Federation para evitar distribuir uma chave de conta de serviço duradoura. Define a identidade externa aceite, os atributos necessários e as permissões do recurso. A federação resolve a forma de apresentar credenciais; não substitui a escolha do acesso mínimo. Testa também uma identidade semelhante que deve ser recusada. VPC Service Controls e IAM respondem a verificações distintas. Uma chamada permitida pelo perímetro ainda precisa de autorização IAM. No modo dry run, as violações do perímetro são registadas sem impor essa configuração. Usa esses registos para reconhecer caminhos legítimos e ajustar regras específicas antes de ativar enforcement. Não interpretes a ausência de bloqueios como validação de acesso proibido. Mantém testes positivos e negativos com contexto reproduzível. Numa migração regional BigQuery, confirma as regras de localização da chave CMEK e da taxonomia. Para um dataset regional, a chave regional tem de corresponder à localização aplicável; não basta pertencer ao mesmo projeto. A taxonomia de policy tags deve existir na localização da tabela de destino. Nomes iguais não provam identidade nem permissões iguais. Guarda decisões de segurança junto do plano de migração para que RUN consiga manter o resultado.

3. Distinguir um esquema aceite de dados corretos

O serviço e o consumidor podem aplicar regras diferentes. Numa tabela BigQuery existente, não podes acrescentar diretamente uma nova coluna de topo REQUIRED como se fosse uma coluna NULLABLE. Planeia uma evolução suportada, com validação dos valores e transição dos consumidores. Não generalizes esta restrição a todos os campos aninhados ou a todos os métodos de reconstrução de tabelas; consulta a operação concreta. Tornar um campo anulável pode ser permitido pelo armazenamento e quebrar um leitor que exige um valor. Acrescentar uma coluna pode ser aceitável para um leitor que seleciona campos por nome e incompatível com outro que rejeita campos desconhecidos. Testa as duas perspetivas. Usa exemplos em que o produtor é válido e o consumidor falha, para evitar confundir validação de serviço com compatibilidade de integração. A mesma estrutura também pode esconder uma mudança de granularidade: uma linha por movimento passa a uma linha por cliente por dia. O exercício desta aula não deteta essa mudança. Acrescenta reconciliação de contagens, chaves, totais e casos de negócio. Quando mudares limites de deteção de dados sensíveis, mede falsos negativos e falsos positivos; aumentar o mínimo de likelihood pode omitir ocorrências reais e não anonimiza os dados.

4. Executar o exercício de impacto

O código apresentado abaixo é um modelo local original em Python, sem chamadas cloud nem dados reais. Lê JSON com before, after e consumers. Cada campo declara name, type, nullable e unit. Os tipos são integer, decimal, string e boolean; a comparação de tipo e unidade é exata. Cada consumidor declara os campos necessários e allowAdditionalFields. Não existem conversões implícitas nem descoberta automática de consumidores. No exemplo incluído, amount passa de integer/cents para decimal/EUR e surge riskBand. O leitor close aceita campos adicionais, mas exige o montante antigo: falha por tipo e unidade. strict também rejeita o campo adicional. ids só exige id e aceita o resto, pelo que permanece compatível. new-api já esperava o contrato novo e passa a ser compatível. audit exige checksum, ausente antes e depois; continua incompatível, sem constituir uma regressão introduzida pela mudança. Executa python3 content/labs/pde-contract-impact/run.py < content/labs/pde-contract-impact/case.json na raiz do projeto. Prevê o resultado antes de executar. newlyIncompatible deve conter close e strict; incompatibleAfter contém também audit. Explica por escrito por que motivo os dois conjuntos diferem. Depois muda apenas allowAdditionalFields e observa que corrigir campos adicionais não corrige divergências de tipo ou unidade.

5. Interpretar limites e testar hipóteses

O modelo distingue quatro transições: permanece compatível, torna-se incompatível, torna-se compatível e permanece incompatível. Os resultados são ordenados para não depender da ordem das entradas. Campos duplicados, propriedades inesperadas e valores fora do contrato são rejeitados. A entrada está limitada a 100 campos por esquema, 100 consumidores e um milhão de caracteres no CLI. Estas restrições tornam o exercício compreensível; não representam limites de produtos cloud. Experimenta as duas direções da nullability. Um produtor que pode fornecer null não satisfaz um leitor que o proíbe; um produtor não anulável satisfaz essa parte do contrato de um leitor que aceita null. Retira um campo necessário e confirma a razão missing. Mantém o tipo, mas altera cents para EUR: a unidade continua a ser uma incompatibilidade independente. A comparação exata também considera diferentes unidades com capitalização diferente. O resultado não valida linhas reais, permissões, valores permitidos, codificação de ficheiros ou descoberta de dependências. Os indicadores actualRowsValidated, allConsumersDiscovered, cloudSchemaValidated e productionApproval permanecem falsos. Na revisão, identifica qual a evidência adicional que preencheria cada lacuna. Um relatório verde permite continuar a investigação dentro deste contrato; não autoriza uma passagem a produção.

6. Portabilidade exige testar o comportamento

Não uses as regras do exercício como especificação de Avro ou de BigQuery. Em Avro 1.12.0, a resolução entre o esquema do writer e o do reader tem regras próprias. Um campo exigido pelo reader e ausente no writer pode usar o default definido pelo reader. Um default num campo do writer não torna esse campo opcional na codificação. Identifica sempre qual é o esquema que contém o default antes de tirar uma conclusão. Uma API comum também não garante capacidades iguais em todos os executores. Ao mudar um pipeline Apache Beam, consulta a matriz do runner para as funcionalidades utilizadas e executa casos representativos no destino. Regista versões e restrições; não memorizes uma tabela de suporte como se fosse permanente. Se o caso depende de temporizadores, estado ou janelas, o teste tem de exercitar essa dependência. Um formato aberto reduz uma dependência, mas não elimina SQL específico, identidades, políticas ou semântica transacional. Constrói uma pequena matriz de funcionalidades, evidências e lacunas antes de estimar o esforço. Num script BigQuery com exception handler, registar o erro não executa por si só ROLLBACK TRANSACTION. Torna explícito o tratamento da transação no caminho de falha e testa-o no contexto utilizado.

7. Preparar cutover e retorno com dados

Um lag de replicação igual a zero descreve um instante. Se a origem continua a aceitar escritas, esse valor não estabelece uma fronteira estável de passagem. Define como controlar novas escritas, drenar trabalho em curso, identificar o ponto a atingir no destino e confirmar a aplicação até esse ponto. Coordena os consumidores e a aceitação funcional; uma mudança de endpoint não resolve movimentos em trânsito. Ensaia o caminho de retorno com a mesma atenção. Se o destino recebe novas escritas, voltar o DNS não as transporta para a origem. Identifica como reconciliar essas escritas e se existe transformação inversa. Um total diário não permite reconstruir todos os movimentos que o produziram. Quando a informação se perde, não prometas um retorno sem perda apenas porque ainda existe uma cópia antiga. A conversão monetária também precisa de uma política explícita. O valor 0,015 EUR corresponde a 1,5 cêntimos e não cabe exatamente num inteiro. Arredondar é uma decisão com consequências, não uma prova de reversibilidade. Define tratamento de exceções, precisão, reconciliação e responsável pela aprovação. No ensaio em paralelo, isola efeitos externos para que dois caminhos não publiquem o mesmo resultado real.

8. Entregar a decisão e a manutenção a RUN

Prepara um registo curto que permita a outra equipa repetir o raciocínio. Inclui contrato anterior, proposta, inventário de consumidores, incompatibilidades novas, problemas preexistentes, resultados de dados reais autorizados e limites dos ensaios. Associa cada ação a responsável, prazo e condição de aceitação. Uma pendência antiga pode continuar a impedir a passagem, mesmo sem ter sido causada por esta alteração. No nosso caso, close e strict precisam de transição coordenada; audit precisa de tratamento próprio para checksum. A melhoria de new-api não compensa a quebra do fecho. Para um consumidor ainda não inventariado, conserva a incerteza em vez de o marcar como aprovado. O comité recebe impacto no negócio, opções e evidência, com critérios concretos para avançar, adiar ou interromper. Como exercício final, escreve uma sequência de cinco decisões: inventariar e fixar contratos, verificar segurança e compatibilidade, reconciliar dados e comportamento, ensaiar passagem e retorno, confirmar aceitação e suporte. Justifica onde colocarias uma pausa se faltar o exportador mensal. Relaciona esta aula com replay, histórico e capacidade: compatibilidade define o que pode ser lido; reconciliação confirma o que foi entregue; operação sustenta a entrega dentro do prazo.

"""Original offline field-contract model, not an Avro/BigQuery schema validator."""
import json
import re
import sys


def identifier(value):
    if not isinstance(value, str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,64}', value):
        raise ValueError('invalid identifier')


def fields(rows):
    if not isinstance(rows, list) or not 1 <= len(rows) <= 100:
        raise ValueError('fields must contain 1..100 entries')
    result = {}
    for row in rows:
        if not isinstance(row, dict) or set(row) != {'name', 'type', 'nullable', 'unit'}:
            raise ValueError('invalid field shape')
        identifier(row['name'])
        identifier(row['unit'])
        if row['name'] in result:
            raise ValueError('duplicate field')
        if row['type'] not in ['integer', 'decimal', 'string', 'boolean'] or type(row['nullable']) is not bool:
            raise ValueError('invalid type or nullability')
        result[row['name']] = row
    return result


def check(producer, reader, additional):
    issues = []
    for name in sorted(reader):
        expected = reader[name]
        if name not in producer:
            issues.append({'field': name, 'reason': 'missing'})
            continue
        actual = producer[name]
        for property_name in ['type', 'unit']:
            if actual[property_name] != expected[property_name]:
                issues.append({'field': name, 'reason': property_name,
                               'expected': expected[property_name], 'actual': actual[property_name]})
        if actual['nullable'] and not expected['nullable']:
            issues.append({'field': name, 'reason': 'nullable', 'expected': False, 'actual': True})
    if not additional:
        for name in sorted(set(producer) - set(reader)):
            issues.append({'field': name, 'reason': 'unexpected'})
    return issues


def evaluate(data):
    if not isinstance(data, dict) or set(data) != {'before', 'after', 'consumers'}:
        raise ValueError('invalid input shape')
    before, after = fields(data['before']), fields(data['after'])
    consumers = data['consumers']
    if not isinstance(consumers, list) or not 1 <= len(consumers) <= 100:
        raise ValueError('consumers must contain 1..100 entries')
    seen, results = set(), []
    for consumer in consumers:
        if not isinstance(consumer, dict) or set(consumer) != {'id', 'fields', 'allowAdditionalFields'}:
            raise ValueError('invalid consumer shape')
        identifier(consumer['id'])
        if consumer['id'] in seen:
            raise ValueError('duplicate consumer')
        seen.add(consumer['id'])
        if type(consumer['allowAdditionalFields']) is not bool:
            raise ValueError('additional-field policy must be boolean')
        reader = fields(consumer['fields'])
        old = check(before, reader, consumer['allowAdditionalFields'])
        new = check(after, reader, consumer['allowAdditionalFields'])
        transition = ('still-incompatible' if old else 'became-incompatible') if new else (
            'became-compatible' if old else 'still-compatible')
        results.append({'consumer': consumer['id'], 'beforeIssues': old,
                        'afterIssues': new, 'transition': transition})
    results.sort(key=lambda row: row['consumer'])
    return {'results': results,
            'newlyIncompatible': [r['consumer'] for r in results if r['transition'] == 'became-incompatible'],
            'incompatibleAfter': [r['consumer'] for r in results if r['afterIssues']],
            'actualRowsValidated': False, 'allConsumersDiscovered': False,
            'cloudSchemaValidated': False, 'productionApproval': False}


def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError('duplicate JSON field')
        result[key] = value
    return result


if __name__ == '__main__':
    try:
        raw = sys.stdin.read(1000001)
        if len(raw) > 1000000:
            raise ValueError('input too large')
        print(json.dumps(evaluate(json.loads(raw, object_pairs_hook=unique_object)), sort_keys=True))
    except (ValueError, TypeError, RecursionError) as error:
        print('invalid input: ' + str(error), file=sys.stderr)
        sys.exit(2)
NA PRÁTICA

close e strict quebram com a mudança de tipo e unidade; audit já exigia checksum ausente.

Armadilhas comuns

Confundir regressão com problema anterior, esquema com fidelidade, dry run com enforcement e retorno de endpoint com recuperação dos dados.

Tópicos relacionados: Replay e ingestão · Reconciliação e granularidade · Continuidade e recuperação

Leva esta ideia contigo

Compara antes e depois por consumidor; valida dados e comportamento antes de aprovar a passagem.

Criar conta

Referência: Professional Data Engineer standard exam guide · Current linked standard guide (document title v4.2); edition date unconfirmed (2026-09-30 inspection)

Google Cloud é uma marca comercial de Google LLC. A dr.pt é uma plataforma de preparação independente e não está afiliada, associada, patrocinada, autorizada nem aprovada por Google. Os conteúdos e as perguntas são originais, não são perguntas oficiais de exame, e concluir os nossos testes não atribui nem garante qualquer certificação. Os nomes são usados apenas para identificar o tema. Todas as outras marcas pertencem aos respetivos titulares.