Ir para o conteúdo

Blogue ·

Uma pausa que mantinha a encomenda aberta

Uma espera dentro de um fluxo mantinha o pedido de encomenda aberto todo o tempo. A solução foi um limiar e não uma caixa de seleção, e o número foi medido.

Por Provibr

Um fluxo pode conter uma espera. Aprovisiona-se uma máquina, dá-se um momento ao agente e envia-se depois o comando seguinte. Até esta semana essa espera passava-se a dormir dentro do pedido que iniciou o fluxo, o que significava que um cliente a fazer uma encomenda ficava a olhar para um indicador de progresso durante todo o tempo da pausa.

O limite superior antigo eram cinco minutos. Alguém podia portanto construir um fluxo de aspeto perfeitamente razoável cujo efeito era um formulário de encomenda de cinco minutos.

Agora que uma execução pode parar e ser continuada por um trabalhador, a pausa já não tem de acontecer dentro do pedido. O limite superior passou para trinta dias. A questão interessante não é o teto, porém. É onde fica a linha entre uma pausa que o pedido consegue absorver e uma pausa que deve suspender a execução.

O limiar é medido e não escolhido

Trinta segundos.

A pergunta que fizemos não foi «quanto tempo pode durar uma pausa», que não tem resposta, mas «quanta pausa é que o pedido em que este fluxo corre já tolera», que tem. Esse pedido é a encomenda.

Medido em relação ao que já lá acontece: um nó de ação espera sessenta segundos por um comando por omissão e pode ser configurado até trezentos, e uma notificação com correio eletrónico custa cerca de meio segundo por destinatário. Uma pausa que acrescente mais meio minuto a isso é defensável. Uma pausa que acrescente um minuto inteiro não é.

Há aqui uma armadilha na qual entrámos deliberadamente em vez de a contornarmos. A escolha confortável teria sido pôr o limiar no limite superior antigo de trezentos segundos, porque então todos os grafos existentes se comportam exatamente como antes e ninguém se surpreende. Isso também está errado: preserva a classe inteira de problema que motivou a mudança. Os grafos com uma pausa entre trinta e um e trezentos segundos passam de facto a comportar-se de outra maneira. A encomenda termina mais cedo e o resto do fluxo segue depois. É essa a melhoria, e não o custo dela.

O teto de trinta dias é uma regra de produto e não um limite técnico. A maquinaria de espera aguentaria de boa vontade uma execução durante um ano. O que não aguenta um ano é uma organização em que ninguém se lembra por que motivo uma execução está aberta.

Porque é um limiar e não uma caixa de seleção

A alternativa óbvia é um interruptor de «esperar de forma duradoura» no nó. Não construímos nenhum, e a razão é uma pergunta a que não se consegue responder.

Se existisse uma caixa de seleção, quando é que a quereria desligada? A resposta honesta é nunca. A espera duradoura é um pormenor de implementação: quem escreve um fluxo está a exprimir «pausar aqui durante este tempo» e não tem opinião sobre se isso custa uma linha de base de dados. Uma definição cujo valor correto é sempre o mesmo não é uma definição, é uma coisa que se esqueceu de decidir.

Portanto quem escreve digita um número de segundos e o limiar decide. Abaixo da linha a travessia limita-se a dormir, dentro do pedido, sem custar nada. Acima dela, a execução escreve a sua continuação na fila com um momento de despertar e quem chamou regressa de imediato.

O passo que acaba por aparecer no registo da execução leva uma marca de espera duradoura e o tempo decorrido efetivamente medido, e não o solicitado, porque não são o mesmo número e a diferença é a única maneira de ver que a fila está atrasada.

Fluxos que chamam fluxos, e dois limites verificados duas vezes cada um

A segunda metade deste trabalho é um nó de subfluxo: um fluxo que corre outro fluxo e espera por ele.

Isso cria duas maneiras de construir algo que nunca termina, e parecem-se o suficiente para ser tentador verificá-las em conjunto. Não são a mesma coisa. Um ciclo é infinito. Uma cadeia demasiado profunda é finita mas demasiado longa. Se responder a ambas com uma só função, um ciclo é comunicado como «demasiado profundo», e quem escreveu vai à procura de uma cadeia longa quando a sua tem dois fluxos.

Portanto há duas perguntas, e cada uma é feita duas vezes.

São feitas uma vez quando um fluxo é guardado, estaticamente, em relação aos outros fluxos da organização. Essa verificação tem de existir porque é o único sítio onde uma boa mensagem de erro é possível: pode nomear o fluxo que fecha o laço.

São feitas outra vez durante a execução. Essa verificação tem de existir porque os fluxos são editados de forma independente. O fluxo A é guardado e está bem. Alguém edita depois o fluxo C, que passa a apontar de volta para A. Nada revalidou A. A verificação estática é uma cortesia para quem escreve; a dinâmica é a garantia a sério.

Vale a pena nomear dois pormenores de implementação. Perguntar «consegue este fluxo alcançar-se a si próprio em pelo menos um passo» responde ao caso direto e ao indireto numa única consulta, e o «pelo menos um» é a definição inteira: um caminho de zero passos faria com que todos os fluxos se alcançassem a si próprios. E a travessia de profundidade guarda o seu próprio caminho e devolve infinito quando encontra um fluxo que já lá está, portanto não fica presa precisamente na entrada para a qual a verificação de ciclos existe. Isso importa, porque significa que as duas verificações podem correr por qualquer ordem, o que quer dizer que ninguém tem de se lembrar de uma regra de ordenação.

A profundidade é guardada como coluna na execução filha, o que vai contra a nossa regra habitual de que um valor derivável não é uma coluna. A chave estrangeira para o pai fica nula quando o pai é eliminado, portanto depois de uma eliminação a cadeia não pode ser percorrida para trás para o recalcular. Um valor derivado cuja fonte pode desaparecer não é derivável.

O relógio de recurso é um dia, não cinco minutos

Quando uma execução filha termina, define para agora o momento de despertar da linha de espera do pai, e o pai é apanhado dentro de dez segundos. É esse o mecanismo.

Há também um relógio de recurso, para o caso em que isso não acontece: um contentor que caiu a meio de terminar o filho, uma linha tocada à mão. São vinte e quatro horas.

Cinco minutos seriam a escolha instintiva e é a errada. Um filho que está parado numa aprovação pode legitimamente esperar trinta dias. Com um relógio curto o pai acordaria milhares de vezes ao longo desse período para observar que nada mudou, e cada um desses despertares é um pedido HTTP do gateway para o contentor da web. Um dia limita o dano de um sinal perdido a um dia e custa no máximo trinta despertares inúteis ao longo da espera mais comprida que o sistema permite.

Uma aprovação de que ninguém é avisado é uma execução parada

Um pedido de aprovação pode agora enviar um correio eletrónico, e a categoria em que é enviado é a sua própria e não a categoria geral dos fluxos. Foi um desvio deliberado ao plano.

A categoria geral dos fluxos vai para toda a gente que tenha o módulo de automação. Um pedido de aprovação deve ir para as pessoas que o conseguem de facto decidir, que é um conjunto diferente e mais pequeno. Portanto há um segundo crivo sobre a categoria: a notificação só é entregue aos membros que têm a permissão de decidir.

Medido na nossa organização de testes: a categoria geral dos fluxos resolveu para três destinatários e a categoria de aprovação para dois. Dois números que diferem são a prova inteira de que o crivo está a fazer alguma coisa.

Um clique que não selecionou nada

A lição de medição desta ronda é sobre o teste de navegador e não sobre o produto.

O nosso auxiliar de navegador tem um método de clique que despacha uma pressão e uma libertação do botão do rato. Num botão vulgar isso funciona. Num nó da tela de fluxos não faz absolutamente nada: o painel de propriedades ficou no nó de gatilho e a lista pendente que queríamos alterar não existia, o que se lê exatamente como uma funcionalidade em falta.

A tela inicia um arrasto ao carregar do ponteiro e chama setPointerCapture no nó. Sem um evento de movimento de rato anterior, e sem o campo buttons definido na pressão, o navegador não produz um clique que o caminho de captura reconheça. Acrescentar esses dois torna a seleção imediata.

Há um segundo pormenor no mesmo auxiliar. Não carregue no meio de um nó. O meio de um nó com portas fica entre os pontos das portas, e esses têm o seu próprio tratador de ponteiro para arrastar um fio para fora. Carregue antes no canto superior esquerdo do cabeçalho.

Ambos parecem defeitos do produto quando se esbarra neles, e nenhum o é. É essa a forma geral do problema: uma simulação de entrada que está quase certa falha de uma maneira que não se distingue de uma avaria naquilo que se estava a tentar testar.

O que registaríamos

  1. Um limiar ganha a uma caixa de seleção quando o valor correto da caixa é sempre o mesmo. Pergunte quando é que alguém a desligaria; se não houver resposta, não construa o interruptor.
  2. Meça o limiar em relação ao orçamento em que tem de caber, e não contra a funcionalidade que está a limitar.
  3. Não ponha o novo limiar no limite antigo só porque assim nada muda. Nada mudar é o problema que estava a resolver.
  4. Um ciclo e uma cadeia demasiado longa são erros diferentes. Uma só mensagem para ambos manda as pessoas para o sítio errado.
  5. Valide ao guardar pela mensagem e em tempo de execução pela garantia. Os registos vizinhos mudam sem revalidar o seu.
  6. Uma procura que termina no seu próprio caminho pode correr por qualquer ordem relativamente à verificação que apoia, o que elimina uma regra de ordenação de que ninguém se lembraria.
  7. Um valor derivado cuja fonte pode ser eliminada não é derivado. Guarde-o.
  8. Dimensione um relógio de recurso em função da espera legítima mais comprida, e não contra a rapidez com que gostaria de dar por ela.
  9. Encaminhe uma notificação por quem pode agir sobre ela, e não pelo módulo a que pertence.
  10. Uma entrada simulada que está quase certa falha como uma funcionalidade avariada. Verifique como o componente recebe de facto os eventos antes de acreditar no resultado.

Para que fique claro o que isto é: a verificação da fusão correu 16 de 16 passos sem uma linha de falha, a bateria de testes da web deu 4583 verdes, as baterias dos pacotes, do gateway e das ferramentas 7286 verdes, os testes de travessia 75 verdes, as verificações de subfluxo 13 verdes, a execução de ponta a ponta contra a base de dados de desenvolvimento 58 verdes, a medição de destinatários 4 verdes, e o teste de navegador 31 verdes com um trabalhador de gateway a sério, no qual uma pausa de noventa segundos retomou ao fim de noventa e cinco segundos, uma execução pai e uma execução filha ficaram ligadas, foi enviado exatamente um correio eletrónico de aprovação, e uma cadeia de quatro e uma referência recursiva foram ambas recusadas com a cadeia infratora nomeada na mensagem. O teste do editor deu 9 verdes e a passagem de acessibilidade não encontrou nada em seis páginas. Por provar: os ramos de esgotamento de tentativas e de reivindicação obsoleta, o relógio de recurso de vinte e quatro horas, um subfluxo dentro de um ciclo, uma execução filha desaparecida, o correio eletrónico para quem não é proprietário através de um envio a sério, e absolutamente nada contra um hipervisor a sério.

Do trabalho de plataforma na Provibr. Como é que a travessia aprendeu a parar, para começar, está em cada chamada recursiva estava em posição de cauda, e a forma do próprio grafo está em um fluxo é um grafo, e um grafo tem de ser finito.

Comece com um host e um agente

Crie uma conta, instale o agente com um único comando e ligue o seu primeiro host Proxmox. O período experimental dura 14 dias e não pede quaisquer dados de pagamento.