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

Desenhar recuperação com dependências verificáveis

Avalia mecanismo de failover, proteção, dependências regionais e caminho crítico antes de prometer recuperação do serviço.

Definir a recuperação que o negócio aceita

Uma equipa fictícia de tecnologia de fundos recebe notícia de uma falha regional. Existe uma cópia dos dados noutra região, mas o relatório de posições também depende de identidades, chaves, capacidade de execução, rotinas e consultas agendadas. O patrocinador pergunta quando pode voltar a usar o serviço. A resposta deve indicar o resultado recuperado: consultar posições de uma data conhecida, reconciliar movimentos e disponibilizar o relatório ao consumidor autorizado. Dizer apenas que o dataset existe não responde à pergunta. Desenha as dependências desse resultado com os responsáveis técnicos. Para cada uma, regista duração estimada, pré-requisitos, evidência de preparação e responsável por corrigir lacunas. Inclui caminhos de acesso alternativos e a localização da evidência do incidente, pois a consola habitual pode estar indisponível. Se o RTO acordado é de trinta minutos desde o incidente e já passaram dez, restam vinte para recuperar e validar. O relógio não recomeça quando a equipa abre o plano. Este exemplo ensina planeamento; as durações são didáticas e não representam garantias de um fornecedor nem procedimentos BNP Paribas.

Escolher o mecanismo para o cenário de falha

Na replicação BigQuery entre regiões, a réplica secundária é de leitura. A documentação distingue essa replicação de managed disaster recovery durante uma indisponibilidade total da região primária. No primeiro caso, a promoção da secundária depende de a primária estar disponível. Uma arquitetura que precisa de retomar escritas durante a perda regional deve avaliar explicitamente o mecanismo de recuperação, a edição e a reserva aplicáveis, em vez de inferir essa capacidade da existência de uma réplica. Em managed disaster recovery, hard failover pode avançar com a primária indisponível, mas não espera pelos dados ainda não replicados. Soft failover espera pela sincronização e exige ambas as regiões disponíveis. A escolha deve relacionar continuidade, perda potencial e condições reais do incidente. Prepara uma tabela com ponto de replicação conhecido, escritas posteriores e consumidores afetados. Não transformes um ponto de replicação num comprovativo de que todas as entregas a sistemas externos também foram concluídas. Um relatório pode ter sido enviado antes da falha e precisar de reconciliação própria. O ensaio deve testar esse percurso completo, incluindo a recuperação da evidência necessária para decidir.

Recuperar proteção e acesso com os dados

Uma versão Cloud KMS desativada conserva material de chave, mas não pode ser usada enquanto estiver nesse estado. Restaurar uma versão agendada para destruição coloca-a em disabled; não a torna logo utilizável. O plano deve identificar a versão necessária, o controlo que autoriza a sua utilização e a evidência de leitura no destino. Acesso administrativo à consola não prova que a identidade do serviço consegue decifrar os dados. Na réplica BigQuery com CMEK, verifica a configuração da chave regional da réplica. Se o dataset de origem tem default_kms_key, a criação exige replica_kms_key adequada à região de destino. A proteção de colunas também merece tratamento por mecanismo: os policy tags baseados em taxonomias não são automaticamente corrigidos pela promoção. A documentação atual trata separadamente políticas atribuídas diretamente a colunas ou a data governance tags; não generalizes uma regra a todos os tipos. Se houver uma rotina de masking personalizada, inclui a respetiva UDF e a sua localização. Durante o incidente, evita resolver uma falha específica abrindo acesso geral. Regista a correção, testa a identidade do consumidor e conserva a evidência para auditoria.

Inventariar recursos que a cópia não torna utilizáveis

Uma rotina pode aparecer na região secundária e continuar a referenciar uma conexão regional da origem. Uma tabela externa pode ter metadata replicada, mas depender de objetos num bucket que não satisfaz a localização necessária. Inventaria estes recursos como dependências executáveis, com ensaios de leitura e transformação. A presença do nome no catálogo é apenas uma observação de metadata. Inclui o comportamento de desempenho no plano. Para search indexes, a documentação descreve replicação de metadata e reconstrução dos dados do índice na região promovida. Não prometas a mesma latência apenas porque a definição foi copiada. Para materialized views, verifica onde estão as tabelas referenciadas; a réplica não elimina requisitos de localização. Quando várias reservas de failover participam na mesma consulta, confirma as condições de destino comum previstas pelo produto. No exemplo de fundos, um teste deve executar a consulta do consumidor com a sua identidade, chamar as rotinas necessárias e verificar o prazo de entrega. Conserva também um caso negativo, como uma dependência externa indisponível, para mostrar como o serviço falha e quem deve agir.

Executar o grafo local de recuperação

Executa python3 run.py < case.json no laboratório pde-recovery-path. Os tempos são minutos num relógio didático. A entrada contém incidentAt, asOf, targetRto, maxEvidenceAge, targets e nodes. Cada nó tem identificador, dependências, duração e evidência confirmed, missing ou failed. Uma evidência ausente exige timestamp null; nos restantes estados, o timestamp não pode estar no futuro. A duração representa trabalho ainda por executar a partir da avaliação, não o tempo total de uma tarefa já parcialmente concluída. A fixture tem dois ramos. access demora cinco e antecede engine, que demora seis. key demora dois e antecede storage, que demora oito. reconcile demora quatro e espera por ambos os ramos; consumer demora três. O caminho mais longo é access,engine,reconcile,consumer, com dezoito minutos. Como já passaram dez desde o incidente, o total planeado é 28. A evidência de key está em falta, pelo que o resultado é evidence-blocked apesar de o tempo caber no RTO de trinta. O programa calcula tempos planeados mesmo quando falta evidência, mas não inclui o esforço desconhecido para resolver essa lacuna.

Distinguir caminho crítico de conjunto de bloqueios

O caminho crítico indica o ramo que determina a duração no modelo com paralelismo suficiente. Não contém necessariamente todas as dependências que podem impedir a recuperação. key está no ramo mais curto e bloqueia consumer na mesma. O programa percorre todos os antecessores do alvo para reunir evidências ausentes, falhadas ou antigas. Um nó não relacionado com esse alvo não deve bloquear a sua decisão; se for outro alvo obrigatório, terá a sua própria avaliação. Corrige key para confirmed com evidência no instante 110: a previsão passa a plan-feasible. Aumenta a duração de key para quatro: o caminho crítico muda para key,storage,reconcile,consumer e o total passa a 29. Com três, os ramos empatam; o programa escolhe o caminho lexicograficamente menor apenas para tornar a saída reproduzível. Isso não significa maior prioridade de negócio. Os testes permutam a lista de nós e a ordem das dependências. Ciclos, dependências desconhecidas e referências duplicadas são rejeitados. Não elimines um nó problemático só para obter uma previsão verde: seria uma alteração do âmbito de recuperação, que precisa de justificação própria.

Ensaiar prazo, evidência e capacidade

Com todas as evidências confirmadas, targetRto=28 aceita a previsão de 28; targetRto=27 não aceita. Se a equipa só avaliar no instante 113, o total sobe para 31 mesmo sem alterar durações. Este exercício torna visível o tempo de deteção, decisão e preparação já consumido. A idade da evidência também tem fronteira: com asOf=110 e máximo vinte, uma observação de 90 ainda é aceite; uma de 89 fica antiga. Estas regras são didáticas e devem ser adaptadas ao contrato real do serviço. O cálculo admite execução paralela sem contenção. Se access e key dependem da mesma pessoa indisponível, as durações não podem ser tratadas como simultâneas sem outra análise. O modelo também não mede RPO, consistência entre datasets ou capacidade de decifrar dados. rtoProven e dataConsistencyProven permanecem false. Usa o exercício para contestar pressupostos e preparar ensaios reais com início e fim observados, identidades corretas e reconciliação. Regista separadamente previsão, execução medida e aceitação do consumidor. Um ensaio anterior favorável não substitui evidência depois de uma alteração relevante de arquitetura ou permissões.

Retomar entregas e preparar o retorno

A promoção não redireciona automaticamente as consultas BigQuery agendadas para a nova região. O procedimento precisa de as recriar no destino aplicável e verificar identidade, parâmetros e destino de escrita. O histórico de jobs da região original também não passa automaticamente a aparecer na região secundária. Prepara a recolha de evidência antes do incidente e evita concluir que um job nunca existiu só porque a consulta regional devolve vazio. Antes de remover a origem, confirma que a promoção terminou, que os consumidores executam no destino e que as entregas foram reconciliadas. Planeia o retorno com os dados produzidos durante a recuperação e os controlos de acesso válidos nessa fase. Na reunião de decisão, apresenta capacidades confirmadas, bloqueios, estimativa temporal e perda potencial separadamente. Se falta a chave de uma dependência, explica por que a previsão temporal favorável não basta. O responsável de RUN deve conseguir reproduzir o diagnóstico sem depender do autor do projeto. Fecha o exercício com uma atualização curta em inglês que identifique o próximo responsável e a evidência necessária para retomar o serviço.

"""Original offline recovery dependency model. No infrastructure operations."""
import json
import re
import sys


def require(ok, message):
    if not ok:
        raise ValueError(message)


def integer(value, low, high):
    return type(value) is int and low <= value <= high


def identifier(value):
    return type(value) is str and re.fullmatch(r'[A-Za-z0-9_-]{1,48}', value) is not None


def keys(value, fields):
    require(type(value) is dict and set(value) == set(fields.split()), 'Unexpected fields')


def evaluate(payload):
    keys(payload, 'incidentAt asOf targetRto maxEvidenceAge targets nodes')
    for name in ['incidentAt', 'asOf', 'targetRto', 'maxEvidenceAge']:
        require(integer(payload[name], 0, 1000000000), 'Invalid time')
    require(payload['asOf'] >= payload['incidentAt'], 'Assessment precedes incident')
    nodes = payload['nodes']
    require(type(nodes) is list and 1 <= len(nodes) <= 30, 'Need 1..30 nodes')
    registry = {}
    for node in nodes:
        keys(node, 'id dependencies minutes evidence evidenceAt')
        require(identifier(node['id']) and node['id'] not in registry, 'Invalid or duplicate node id')
        require(integer(node['minutes'], 1, 10000), 'Invalid duration')
        require(node['evidence'] in ['confirmed', 'missing', 'failed'], 'Invalid evidence status')
        if node['evidence'] == 'missing':
            require(node['evidenceAt'] is None, 'Missing evidence requires null time')
        else:
            require(integer(node['evidenceAt'], 0, payload['asOf']), 'Invalid evidence time')
        deps = node['dependencies']
        require(type(deps) is list and len(deps) <= 29 and all(identifier(v) for v in deps), 'Invalid dependencies')
        require(len(set(deps)) == len(deps) and node['id'] not in deps, 'Duplicate or self dependency')
        registry[node['id']] = node
    for node in nodes:
        require(all(v in registry for v in node['dependencies']), 'Unknown dependency')
    targets = payload['targets']
    require(type(targets) is list and 1 <= len(targets) <= 10, 'Need 1..10 targets')
    require(all(identifier(v) and v in registry for v in targets) and len(set(targets)) == len(targets), 'Invalid targets')
    visiting, calculated = set(), {}

    def visit(name):
        if name in calculated:
            return calculated[name]
        require(name not in visiting, 'Dependency cycle')
        visiting.add(name)
        node = registry[name]
        parents = [visit(v) for v in sorted(node['dependencies'])]
        critical = min(parents, key=lambda p: (-p['remaining'], p['path'])) if parents else None
        ancestors = {name}
        for parent in parents:
            ancestors.update(parent['ancestors'])
        result = {'remaining': node['minutes'] + (critical['remaining'] if critical else 0),
                  'path': (critical['path'] if critical else []) + [name], 'ancestors': ancestors}
        calculated[name] = result
        visiting.remove(name)
        return result

    # Validate every component, including nodes outside requested target closures.
    for name in sorted(registry):
        visit(name)
    results = []
    for target in sorted(targets):
        plan = calculated[target]
        blockers = []
        for name in sorted(plan['ancestors']):
            node = registry[name]
            reasons = []
            if node['evidence'] != 'confirmed':
                reasons.append(node['evidence'])
            if node['evidenceAt'] is not None and payload['asOf'] - node['evidenceAt'] > payload['maxEvidenceAge']:
                reasons.append('stale')
            if reasons:
                blockers.append({'id': name, 'reasons': reasons})
        total = payload['asOf'] - payload['incidentAt'] + plan['remaining']
        fits = total <= payload['targetRto']
        results.append({'id': target, 'plannedRemainingMinutes': plan['remaining'],
                        'plannedMinutesFromIncident': total,
                        'plannedFinishAt': payload['asOf'] + plan['remaining'],
                        'criticalPath': plan['path'], 'evidenceBlockers': blockers,
                        'timeFitsRto': fits,
                        'decision': 'evidence-blocked' if blockers else 'plan-feasible' if fits else 'deadline-infeasible'})
    return {'targets': results, 'timeUnit': 'minutes', 'allTargetsPlanFeasible': all(t['decision'] == 'plan-feasible' for t in results),
            'recoveryPerformed': False, 'rtoProven': False,
            'dataConsistencyProven': False, 'productionApproval': False}


def main():
    raw = sys.stdin.read(1000001)
    require(len(raw) <= 1000000, 'Input too large')
    print(json.dumps(evaluate(json.loads(raw)), sort_keys=True))


if __name__ == '__main__':
    try:
        main()
    except (ValueError, TypeError, RecursionError):
        print('Invalid recovery-path fixture', file=sys.stderr)
        sys.exit(2)
NA PRÁTICA

A fixture prevê dezoito minutos restantes e 28 desde o incidente, mas key sem evidência bloqueia consumer embora esteja fora do caminho crítico.

Armadilhas comuns

Confundir réplica com recuperação de escritas; contar só o caminho crítico como dependências; reiniciar o relógio RTO; assumir que promoção redireciona agendamentos; tratar metadata como execução comprovada.

Tópicos relacionados: Retenção e recuperação · Controlo de tentativas · Contratos de migração

Leva esta ideia contigo

Uma previsão só é útil quando explicita dependências, evidência, tempo já consumido e condições de aceitação do consumidor.

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.