Em poucas palavras, devemos projetar a morte em nossos programas, processos e threads em um nível baixo, para o bem de todo o sistema?
Falhas acontecem. Processos morrem. Planejamos um desastre e, ocasionalmente, nos recuperamos dele. Mas raramente projetamos e implementamos a morte imprevisível do programa. Esperamos que o tempo de atividade de nossos serviços seja o máximo que desejarmos mantê-los funcionando.
Um exemplo macro desse conceito é o Chaos Monkey da Netflix , que encerra aleatoriamente as instâncias da AWS em alguns cenários. Eles afirmam que isso os ajudou a descobrir problemas e criar sistemas mais redundantes.
O que eu estou falando é de nível mais baixo. A idéia é que processos tradicionalmente de longa duração saiam aleatoriamente. Isso deve forçar redundância no design e, finalmente, produzir sistemas mais resilientes.
Esse conceito já tem um nome? Já está sendo usado na indústria?
EDITAR
Com base nos comentários e respostas, receio que não tenha esclarecido minha pergunta. Para maior clareza:
- sim, quero dizer aleatoriamente,
- sim, quero dizer em produção, e
- não, não apenas para testes.
Para explicar, gostaria de fazer uma analogia com organismos multicelulares.
Na natureza, os organismos consistem em muitas células. As células se bifurcam para criar redundância e acabam morrendo. Mas sempre deve haver células suficientes do tipo certo para o organismo funcionar. Este sistema altamente redundante também facilita a cicatrização quando ferido. As células morrem para que o organismo viva.
Incorporar a morte aleatória em um programa forçaria o sistema maior a adotar estratégias de redundância para permanecer viável. Essas mesmas estratégias ajudariam o sistema a permanecer estável diante de outros tipos de falhas imprevisíveis?
E, se alguém já tentou isso, como é chamado? Eu gostaria de ler mais sobre isso, se ele já existir.
Respostas:
Não.
Devemos projetar o tratamento adequado do caminho incorreto e projetar casos de teste (e outras melhorias de processo) para validar se os programas lidam bem com essas condições excepcionais. Coisas como o Chaos Monkey podem fazer parte disso, mas assim que você faz "deve travar aleatoriamente" um requisito, travamentos aleatórios reais se tornam coisas que os testadores não podem apresentar como bugs.
fonte
O processo de introdução de defeitos no software ou no hardware para testar os mecanismos de tolerância a falhas é chamado de injeção de falha .
Da Wikipedia:
fonte
Sim. Não Talvez.
A terminação periódica é uma faca de dois gumes. Você será atingido por uma borda ou outra, e qual é o menor dos dois males depende da sua situação.
Uma vantagem é a confiabilidade: se você forçar o programa a terminar aleatoriamente (ou previsivelmente) e de uma maneira ordenada, poderá estar preparado para esse evento e lidar com ele. Você pode garantir que o processo será encerrado quando não estiver ocupado fazendo algo útil. Isso também garante que os bugs que se manifestariam além do tempo de execução sancionado não criarão suas cabeças feias na produção, o que é uma coisa boa. O Apache HTTPD possui uma configuração que permite ajustar quantas solicitações um processo filho (ou thread em versões mais recentes) atenderá antes de terminar.
A outra vantagem também é a confiabilidade: se você não permitir que o programa seja executado por muito tempo, nunca encontrará bugs que se manifestem com o tempo. Quando você finalmente encontra um desses erros, é muito mais provável que o programa retorne uma resposta errada ou falhe em retornar uma. Pior ainda, se você executar muitos threads do mesmo trabalho, um bug induzido por tempo ou contagem pode afetar um grande número de tarefas de uma só vez e resultar em uma viagem às três da manhã no escritório.
Em uma configuração em que você executa muitos dos mesmos threads (por exemplo, em um servidor da Web), a solução prática é adotar uma abordagem mista que resulte em uma taxa de falha aceitável. Se você executar 100 threads, executar uma proporção de curto para longo de 99: 1 significa que apenas um exibirá bugs de longo prazo enquanto os outros continuarão fazendo o que eles fazem sem falhar. Compare isso com a execução de 100%, em que você corre um risco muito maior de que todos os threads falhem ao mesmo tempo.
Onde você tem um único encadeamento, provavelmente é melhor deixá-lo rodar e falhar, porque o tempo morto durante uma reinicialização pode resultar em latência indesejada quando houver trabalho real a ser feito, com êxito.
Em ambos os casos, é importante que haja algo supervisionando os processos para que eles possam ser reiniciados imediatamente. Além disso, não existe uma lei que diga que suas decisões iniciais sobre quanto tempo um processo deve ser executado devem ser convertidas em pedra. A coleta de dados operacionais ajudará você a ajustar seu sistema para manter as falhas em um nível aceitável.
Eu recomendaria não fazer a rescisão aleatória, porque isso dificulta a identificação de erros relacionados ao tempo. O Chaos Monkey faz isso para garantir que o software de supervisão funcione, o que é um problema ligeiramente diferente.
fonte
Você realmente quer dizer aleatório? Ter o seu software aleatoriamente se mata parece uma péssima idéia. Em que ponto isso serviria?
Estou supondo que o que você realmente quer dizer é que devemos ser realistas sobre processos / threads de execução longa e aceitar que, quanto mais tempo eles rodarem, maior a probabilidade de encontrar algum tipo de bug oculto e entrar em um estado não funcional Estado. Portanto, como uma medida puramente pragmática, a vida útil dos processos e threads deve ser limitada.
Acredito que, no final dos anos 90, o servidor da web Apache usasse algo assim. Eles tinham um conjunto de processos de trabalho (não threads) e cada processo de trabalho seria eliminado após uma vida útil fixa. Isso impedia que o servidor fosse monopolizado por processos de trabalho que haviam ficado presos em algum estado patológico.
Não trabalho na área há algum tempo, então não sei se esse ainda é o caso.
fonte
O problema que vejo é que, se um programa desse tipo morrer, diremos "Oh, é apenas mais uma terminação aleatória - nada com que se preocupar". Mas e se houver um problema real que precise ser corrigido? Será ignorado.
Os programas já "aleatoriamente" falham devido a desenvolvedores que fazem mystaykes, bugs em sistemas de produção, falhas de hardware etc. Quando isso ocorre, queremos saber sobre isso para que possamos corrigi-lo. Projetar a morte em programas apenas aumenta a probabilidade de falha e nos forçaria a aumentar a redundância, que custa dinheiro.
Não vejo nada de errado em matar processos aleatoriamente em um ambiente de teste ao testar um sistema redundante (isso deve estar acontecendo mais do que é), mas não em um ambiente de produção. Tiraríamos alguns discos rígidos de um sistema de produção ao vivo a cada poucos dias ou desativaríamos um dos computadores em uma aeronave, enquanto ela voava cheia de passageiros? Em um cenário de teste - tudo bem. Em um cenário de produção ao vivo - prefiro não.
fonte
Não é necessário adicionar código de saída aleatório ao aplicativo. Os testadores podem escrever scripts que matam aleatoriamente os processos do aplicativo.
Na rede, é necessário simular uma rede não confiável para testar uma implementação de protocolo. Isso não é incorporado ao protocolo; pode ser simulado no nível do driver do dispositivo ou com algum hardware externo.
Não adicione código de teste, faça o programa para situações que podem ser alcançadas externamente.
Se isso é destinado à produção, não acredito que seja sério!
Em primeiro lugar, a menos que os processos saiam abruptamente para que as transações em andamento e os dados voláteis sejam perdidos, não será uma implementação honesta do conceito. Saídas planejadas e graciosas, mesmo que tenham um tempo aleatório, não ajudam a preparar adequadamente a arquitetura para lidar com falhas reais, que não são graciosas.
Se problemas de funcionamento reais ou realistas são incorporados ao aplicativo, eles podem resultar em danos econômicos, assim como defeitos reais, e danos econômicos intencionais são basicamente um ato criminoso quase por definição.
Você pode se safar das cláusulas do contrato de licença que renunciam à responsabilidade civil por quaisquer danos decorrentes da operação do software, mas se esses danos forem intencionais, talvez não seja possível renunciar à responsabilidade criminal.
Nem pense em acrobacias como essa: faça com que funcione da maneira mais confiável possível e coloque cenários de falha falsos apenas em versões ou configurações especiais.
fonte
Você pode procurar por " recuperação proativa " e " rejuvenescimento " no contexto de sistemas distribuídos tolerantes a falhas, para lidar com falhas arbitrárias (ou seja, não apenas processos travados, mas dados corrompidos e comportamento potencialmente malicioso). Tem havido muita pesquisa sobre com que freqüência e em que condições um processo (em um sentido abstrato, pode realmente ser uma VM ou um host) deve ser reiniciado. Intuitivamente, você pode entender as vantagens da abordagem ao preferir lidar com um processo morto do que com um processo traidor ...
fonte
Isso realmente não é diferente de testar. Se você estiver projetando uma solução de failover sempre disponível (como a Netflix), sim - você deve testá-la. Não sei se as saídas aleatórias espalhadas por toda a base de código são uma maneira apropriada de testar isso. A menos que você realmente pretenda testar se seu design é resistente a um tiro no pé, parece mais apropriado testá-lo manipulando o ambiente em torno do código e verificando se ele se comporta adequadamente.
Se você não estiver projetando sistemas redundantes, não - você não deve adicionar esse recurso porque adicionou algumas saídas aleatórias. Você deve apenas remover as saídas aleatórias e não terá esse problema. Seu ambiente ainda pode falhar com você; nesse momento, você o classifica como não suportado / não corrige ou reforça seu código contra essa falha e adiciona um teste. Faça isso com bastante frequência, e você vai perceber que você realmente está projetando um sistema redundante - consulte o cenário # 1.
Em algum momento, você pode determinar que não tem mais certeza do que as falhas são ou não tratadas. Agora você pode começar a puxar o tapete aleatoriamente para detectar os pontos de falha.
A única coisa interessante sobre o exemplo da Netflix é que eles executam esses testes em produção. Isso faz certo sentido - alguns bugs realmente produzem apenas coisas que são muito difíceis ou impossíveis de simular em um ambiente isolado. Suspeito que a Netflix tenha passado muito tempo em ambientes de teste antes de se sentirem confortáveis o suficiente para fazer isso na produção. E, na verdade, tudo o que eles estão fazendo é tentar fazer com que as falhas ocorram durante o horário comercial, o que faz um certo sentido para o mercado deles, mas não para muitos outros.
fonte
O termo que você está procurando foi recentemente cunhado por Nassim Nicholas Taleb: Antifragility. Seu livro Antifragile é definitivamente recomendado. Mal menciona TI, mas os paralelos óbvios e não ditos são mais inspiradores. Sua idéia é estender a escala de frágil <-> robusto para frágil <-> robusto <-> antifrágil. Quebras frágeis com eventos aleatórios, gerencia robusto com eventos aleatórios e ganhos anti-frágeis com eventos aleatórios.
fonte
Depende. Percebi que os programadores tendem a generalizar demais as técnicas que se aplicam ao seu domínio específico, ignorando todos os outros. Por exemplo, obter o programa liberado com o custo de corrigir todos os erros pode ser bom ... a menos que você programe o controlador da aeronave, o reator nuclear etc. "Não otimize - o custo do programador é maior que o custo do programa em execução" não é necessário válido para HPC, pois um programa relativamente simples pode ocupar um cluster por meses etc. (ou mesmo um programa popular usado por grande quantidade de usuários). Portanto, mesmo que a empresa X esteja fazendo Y por uma boa razão, você não precisa seguir os passos deles, pois sua situação pode ser diferente.
Geralmente, as rotinas de tratamento de erros são a parte mais mal testada do código - embora pareça simples, é difícil simular que há memória insuficiente ou que algum arquivo importante não existe. Por esse motivo, li textos que propunham que o kernel do Unix falhasse aleatoriamente em algumas chamadas do sistema. No entanto, isso tornaria os programas simples mais difíceis de escrever (se eu precisar conectar 3 bibliotecas C ++ para executar um programa em 2 arquivos, uma vez que não queira me preocupar com o tratamento de erros). Mesmo com exceções, GC, você precisa garantir que você deixou um estado consistente para trás (imagine a exceção no meio da adição de nó à lista vinculada).
Quanto mais serviços distribuídos você tiver, maiores serão as falhas "com que frequência" e "se" ou "quando". Nos data centers, a substituição de disco nos RAIDs faz parte das operações de rotina, pelo que sei - não por falhas inesperadas. Se você opera em larga escala, é necessário levar em consideração, mesmo que a probabilidade de falha de um componente seja pequena, é provável que algo falhe.
Não sei exatamente o que você está fazendo, mas para saber se vale a pena, você precisa pensar se a falha é algo que você precisa levar em conta (como ignorar isso custa) ou é algo muito caro para analisar (como aceitar erros) tempo de desenvolvimento dos custos).
fonte
O servidor IIS possui um recurso configurável que recicla automaticamente os processos de trabalho depois que eles usam uma certa quantidade de memória ou depois de atender a um certo número de solicitações ou depois de estarem ativos por um período de tempo especificado. ( http://msdn.microsoft.com/en-us/library/ms525803(v=vs.90).aspx ) e ( http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/ 1652e79e-21f9-4e89-bc4b-c13f894a0cfe.mspx? Mfr = true )
Quando um CONTAINER como o IIS faz isso, faz sentido proteger o servidor contra processos não autorizados. No entanto, eu preferiria manter isso desativado, porque não faz sentido se você tiver testado suficientemente o seu código.
Já trabalhamos em camadas não confiáveis (hardware, rede), para que eu nunca escreva nenhum código que mate aleatoriamente seus threads ou processos intencionalmente. Matar aleatoriamente também é uma má idéia do ponto de vista econômico - ninguém usaria minha API se eles achassem que eu a havia programado para travar aleatoriamente. Por fim, se eu consumisse uma API ou usasse um sistema com encadeamentos aleatoriamente, teria que gastar muito dinheiro para criar um mecanismo de monitoramento suficientemente robusto para poder dormir em paz à noite.
Em vez disso, se eu estivesse desenvolvendo um sistema ou uma API, escreveria scripts ou usaria um arnês que faria isso puramente para testar a resistência do sistema. E eu faria esse teste em todas as versões para identificar versões ruins. No entanto, embora esse seja um teste necessário, nunca poderia ser um teste "suficiente".
fonte
Existe uma literatura relacionada a essa idéia, chamada de software Crash-Only (também Recovery Oriented Computing) e você pode começar com este artigo da Candea & Fox em 2003. Em vez de mortes aleatórias, os autores argumentam que você pode melhorar a confiabilidade do sistema apenas sempre parando seus programas, matando-os, para ter um único interruptor de interrupção como botão de desligar e um único caminho de inicialização bem exercitado para recuperação.
Embora eu não tenha certeza de quão bem a idéia surgiu, algumas técnicas específicas continuam sendo úteis. Por exemplo, não confiando no seu software para poder desligar-se quando solicitado e, portanto, usando programas de supervisão especializados (por exemplo, supervisord etc.), e também pensando cuidadosamente sobre qual estado do programa é essencial e verifique se ele é registrado nos momentos apropriados em um armazenamento de dados projetado para ativar a recuperação (por exemplo, um banco de dados sql).
fonte
Verdadeiramente aleatoriamente, não. Mas provavelmente é uma boa ideia que processos / threads de execução demorada saiam / reiniciem em um determinado intervalo ou depois de ficarem ociosos por um determinado período (mas dependente de certos critérios) ou após executar um tipo específico de tarefa. Processos de longa execução criam estado inevitavelmente, incluindo coisas obsoletas, presumivelmente podem se prender à memória, impedindo que o espaço de troca seja liberado, o que é limpo (ou deve ser) limpo quando eles saem, melhorando a estabilidade geral do sistema.
fonte
Depende do tipo de aplicativo que você está projetando.
Falhas aleatórias são uma ótima maneira de testar e melhorar a robustez dos sistemas distribuídos (em rede).
No exemplo da Netflix, quando seu programa depende de serviços remotos que podem falhar por vários motivos que estão fora de seu controle (o disco rígido fica com defeito, perda de energia, meteoro cai no data center, etc.). Seu serviço ainda precisa continuar funcionando de alguma forma.
Como você faz isso? Adicione redundância e o dimensionamento é uma solução comum.
Por exemplo, se um mouse roçar o cabo de alimentação do servidor, seu serviço deverá ter alguma solução para continuar funcionando. Por exemplo, ele pode manter servidores de backup redundantes que eles começarão a usar.
No entanto, se o seu programa for um aplicativo de processo único que não opera em uma rede, fazê-lo se matar não testará nada, pois não há como se recuperar disso.
Aqui estão alguns comentários adicionais sobre o conceito do Chaos Monkeys http://www.codinghorror.com/blog/2011/04/working-with-the-chaos-monkey.html
fonte
É possível que um giro aleatório de bits aconteça devido à radiação cósmica . Esse problema foi reconhecido e várias técnicas foram desenvolvidas para impedir que ocorressem inversões de bits.
No entanto, não é possível corrigi-lo 100%, e a corrupção de memória ainda pode causar problemas, e esses problemas ainda estão acontecendo ( com probabilidade muito baixa ).
Agora respondendo à sua questão. Se você precisa ou não projetar um sistema muito robusto, isso depende do que você está fazendo. Se você precisar criar uma nave espacial, é melhor torná-la super robusta e precisará levar em consideração todos os problemas possíveis.
Se você precisar criar um aplicativo de desktop normal, observe falhas aleatórias como bugs no seu código.
fonte
Isso não parece uma idéia absurda.
O sistema operacional Android mata e reinicia aplicativos / serviços do usuário o tempo todo. Na minha experiência, isso definitivamente me ajudou a pensar mais profundamente sobre as condições de erro, bem como projetar arquiteturas mais robustas.
fonte
onDestroy
,onPause
,onSaveInstanceState
, etc ... nunca vai ser chamado em uma actividade ou serviço. No nível do aplicativo, não há nem mesmo umonDestory
retorno de chamada. Então, sim, existem alguns ganchos para desligamentos simples, mas você ainda precisa estar preparado para saídas aleatórias.onPause()
antes que uma atividade seja encerrada. Após o Honeycomb, você terá a garantia dissoonStop()
. Os aplicativos Android são apenas coleções de atividades que estão relacionadas e não existe um conceito no nível de aplicativo de qualquer coisa no que diz respeito ao ciclo de vida da execução.