← Infraestrutura bancária: fundamentos e decisões de produção
07 / 10 · 60 MIN

Dependências, capacidade e prazo de fecho

Calcula margens com hipóteses explícitas e identifica dependências que podem impedir o fecho operacional.

Começar pelo resultado utilizável

Num projeto fictício de fundos, recuperar servidores é apenas uma parte do resultado. O serviço precisa de obter ficheiros, consultar dados, executar a aplicação e apresentar um resultado reconciliado ao negócio. Escreve o ponto de início da medição e a condição de fim antes de estimar durações. Associa cada dependência a um responsável, forma de acesso e evidência recente. O mapa deve incluir componentes partilhados, serviços externos e pessoas. Uma ligação comum ao serviço de chaves pode interromper duas stacks em zonas distintas. O diagrama de réplicas precisa de ser acompanhado por cenários de falha.

Encontrar a sequência que determina o prazo

O modelo original desta aula tem identidade de dez minutos e rede de quinze em paralelo. A base espera por ambas e demora vinte e cinco. Middleware leva doze depois da identidade; a aplicação espera por base e middleware e leva vinte. A aceitação exige mais dez. O resultado é setenta minutos, apesar de a soma de todas as tarefas ser noventa e dois. Acelerar identidade para cinco não muda o prazo: a rede continua a limitar o início da base. Reduzir a base para quinze permite chegar a sessenta sob as mesmas hipóteses.

Verificar se o paralelismo existe

Um cronograma pode desenhar tarefas paralelas que dependem da mesma pessoa autorizada. Se identidade e rede tiverem de ser executadas por um único especialista, passam a consumir vinte e cinco minutos antes da base e o prazo sobe para oitenta. A solução pode exigir cobertura adicional comprovada ou uma janela diferente. Acrescentar nomes ao plano não cria acesso nem competência. No ensaio, regista quem executou cada passo e que ajuda foi necessária. Um resultado obtido com um programador fora da escala não demonstra que o turno previsto consegue repetir o procedimento sozinho.

Estimar a recuperação de uma fila

Para uma fila de doze mil itens, mil conclusões por minuto parecem dar doze minutos. Essa conta só vale no modelo sem novas entradas. Se continuam a chegar oitocentos por minuto, a capacidade líquida é duzentos e a drenagem demora sessenta. Com mil e duzentas conclusões e as mesmas entradas, demora trinta. Se chegam tantos itens quantos saem, um atraso positivo não desaparece. Observa também idade dos itens e qualidade das conclusões. Uma mensagem retirada da fila sem produzir o resultado correto não deve ser contada como fecho de negócio bem-sucedido.

Relacionar capacidade com a carga real

As taxas constantes ajudam a revelar erros de cálculo, mas não eliminam a variabilidade. Um teste de pedidos leves pode não representar o fecho, que partilha a base com reporting e usa operações mais caras. Compara a mistura, o volume, a concorrência e os limites das dependências. Se a política permite adiar uma exportação não urgente, essa ação pode libertar capacidade sem remover a reconciliação crítica. Define autoridade, duração e monitorização da mitigação. Não transformes uma previsão com entradas fictícias numa promessa de prazo para um sistema que ainda não foi observado nessas condições.

Executar e discutir o modelo

Guarda o código desta aula como run.py e executa python3 run.py --output evidence.json numa pasta temporária. O programa verifica trinta e duas observações determinísticas sobre dependências, filas, margens e reconciliação. Não abre portas, não usa credenciais e não executa recuperação real. Lê o resultado e explica porque setenta minutos é um prazo do modelo e não uma duração medida. Depois prepara uma tabela com hipótese, evidência necessária, responsável e ação. O produto do exercício é uma decisão fundamentada sobre o que falta demonstrar, além do ficheiro de resultados automáticos.

"""Original planning model, not a bank system or recovery benchmark.
Run: python3 run.py --output evidence.json
No network, credentials, live service, human approval, or real elapsed-time claim.
"""
import argparse
import hashlib
import json
import platform
from pathlib import Path


def finish_times(tasks):
    pending = dict(tasks)
    done = {}
    while pending:
        ready = [name for name, task in pending.items() if all(d in done for d in task['depends'])]
        if not ready:
            raise ValueError('cycle or missing dependency')
        for name in ready:
            task = pending.pop(name)
            if task['minutes'] < 0:
                raise ValueError('negative duration')
            done[name] = max((done[d] for d in task['depends']), default=0) + task['minutes']
    return done


def drain(backlog, arrivals, completions):
    if min(backlog, arrivals, completions) < 0:
        raise ValueError('negative workload')
    if backlog == 0:
        return 0
    return backlog / (completions - arrivals) if completions > arrivals else None


def reconciliation(expected, actual):
    return {
        'missing': sorted(set(expected) - set(actual)),
        'unexpected': sorted(set(actual) - set(expected)),
        'changed': sorted(k for k in set(expected) & set(actual) if expected[k] != actual[k]),
    }


def run():
    checks = []
    def check(name, actual, expected):
        assert actual == expected, (name, actual, expected)
        checks.append(dict(name=name, actual=actual, expected=expected, passed=True))
    tasks = {
        'identity': {'minutes': 10, 'depends': []},
        'network': {'minutes': 15, 'depends': []},
        'database': {'minutes': 25, 'depends': ['identity', 'network']},
        'middleware': {'minutes': 12, 'depends': ['identity']},
        'application': {'minutes': 20, 'depends': ['database', 'middleware']},
        'acceptance': {'minutes': 10, 'depends': ['application']},
    }
    finish = finish_times(tasks)
    check('parallel prerequisites', [finish['identity'], finish['network']], [10, 15])
    check('database waits for both prerequisites', finish['database'], 40)
    check('middleware finishes before database', finish['middleware'], 22)
    check('usable service includes acceptance', finish['acceptance'], 70)
    check('sixty minute target fails', finish['acceptance'] <= 60, False)
    check('seventy five minute target margin', 75 - finish['acceptance'], 5)
    faster_identity = {k: dict(v) for k, v in tasks.items()}
    faster_identity['identity']['minutes'] = 5
    check('noncritical acceleration leaves completion unchanged', finish_times(faster_identity)['acceptance'], 70)
    faster_database = {k: dict(v) for k, v in tasks.items()}
    faster_database['database']['minutes'] = 15
    check('critical branch acceleration', finish_times(faster_database)['acceptance'], 60)
    shared_operator = {k: dict(v) for k, v in tasks.items()}
    shared_operator['network']['depends'] = ['identity']
    check('shared operator serializes prerequisite work', finish_times(shared_operator)['acceptance'], 80)
    invalid = {'a': {'minutes': 1, 'depends': ['b']}, 'b': {'minutes': 1, 'depends': ['a']}}
    try:
        finish_times(invalid)
    except ValueError as error:
        check('dependency cycle rejected', str(error), 'cycle or missing dependency')
    else:
        raise AssertionError('cycle accepted')
    check('continued arrivals reduce net drain', drain(12000, 800, 1000), 60)
    check('no arrivals changes the estimate', drain(12000, 0, 1000), 12)
    check('higher demonstrated completion rate', drain(12000, 800, 1200), 30)
    check('equal rates never drain positive backlog', drain(12000, 1000, 1000), None)
    check('overload never drains positive backlog', drain(12000, 1100, 1000), None)
    check('empty backlog needs no drain time', drain(0, 800, 1000), 0)
    check('fifteen minutes of overload', (1100 - 1000) * 15, 1500)
    check('rollback validation and contingency budget', 35 + 15 + 10, 60)
    check('latest rollback start in 150 minute window', 150 - (35 + 15 + 10), 90)
    check('five minutes before latest rollback start', 150 - 85 - (35 + 15 + 10), 5)
    check('five minutes after latest rollback start', 150 - 95 - (35 + 15 + 10), -5)
    check('data recovery point lag', 180 - 168, 12)
    check('data point meets fifteen minute RPO', (180 - 168) <= 15, True)
    check('backup completion is not data point', 180 - 178 == 180 - 168, False)
    expected = {'A': 100, 'B': 200, 'C': 300}
    actual = {'A': 100, 'B': 250, 'D': 250}
    check('counts agree despite wrong records', len(expected) == len(actual), True)
    check('totals agree despite wrong records', sum(expected.values()) == sum(actual.values()), True)
    check('reference reconciliation exposes divergence', reconciliation(expected, actual), {'missing': ['C'], 'unexpected': ['D'], 'changed': ['B']})
    check('identical records reconcile', reconciliation(expected, dict(expected)), {'missing': [], 'unexpected': [], 'changed': []})
    required = ['runtime', 'functional', 'integrity', 'access', 'dependencies', 'owner']
    gates = dict.fromkeys(required, True)
    gates['integrity'] = False
    check('green runtime cannot override integrity failure', all(gates[k] for k in required), False)
    gates['integrity'] = True
    check('all fictional evidence criteria met', all(gates[k] for k in required), True)
    check('missing criterion fails closed', all(gates.get(k, False) for k in required + ['return_path']), False)
    check('critical chain distinct from sum of all tasks', sum(t['minutes'] for t in tasks.values()), 92)
    return dict(runtime=platform.python_version(),scope='Deterministic arithmetic, dependency-graph and record-comparison model with fictional inputs. No measured recovery, bank transaction, network service or human acceptance.',passed=len(checks),checks=checks,runnerSha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest())


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--output')
    args = parser.parse_args()
    result = json.dumps(run(), indent=2) + '\n'
    if args.output:
        Path(args.output).write_text(result)
    else:
        print(result, end='')
NA PRÁTICA

Com 12000 itens, 800 entradas/min e 1000 conclusões/min, a previsão de drenagem é 60 min; uma janela de 40 min exige outra opção demonstrada.

Armadilhas comuns

Somar tudo apesar do paralelismo, acelerar uma tarefa sem impacto no prazo, ignorar novas entradas ou assumir duas pessoas quando só existe uma.

Tópicos relacionados: Serviços, dependências e evidência · Resiliência e recuperação de dados · Cloud, custos e autonomia RUN

Leva esta ideia contigo

A previsão depende da sequência, das pessoas e da capacidade líquida; cada hipótese precisa de evidência do ambiente relevante.

Criar conta

Referência: REL13-BP01 Define recovery objectives for downtime and data loss · DR banking infrastructure professional assessment2026.10