Alta disponibilidade

Para criar um ambiente de alta disponibilidade é importante que haja redundância dos servidores das bases de dados e dos Engines. No cenário de utilização do sistema, os principais componentes que demandam atenção são:

  1. Banco de dados: a interrupção do acesso ao banco de dados impede o uso da maioria das aplicações desenvolvidas no sistema. Somente continuarão funcionando aquelas desenvolvidas especificamente para operações offline, que não dependam dos dados do SGBD e realizem gravações de dados de forma assíncrona via agendamento de scripts.
  2. Servidores de aplicação: os servidores de aplicação são intermediários no acesso dos servidores de borda e dos Engines instalados nas estações clientes, portanto a interrupção deles afeta o uso geral do sistema, de forma similar a uma interrupção do banco de dados.
  3. Servidores de borda: a interrupção dos servidores de borda normalmente afeta apenas as unidades de negócio onde eles estão instalados. No entanto, como normalmente são instalados em redes locais que não são conectadas diretamente à rede utilizada pelo banco de dados, a reconstrução do cache local desses Engines pode ser demorada por necessitar de um alto tráfego de dados. Por esse motivo, eles também requerem uma atenção quanto a disponibilidade em bases com cache de dados de tamanho elevado.

Este documento não trata das questões de redundância de infraestrutura, conectividade e de equipamentos, pois atualmente a redundância desses elementos pode ser obtida com facilidade ao se utilizar os principais provedores de computação em nuvem. Serão destacadas apenas as questões relacionadas aos softwares e principalmente a interação deles no contexto de uso do sistema.

Redundância do banco de dados

A configuração de um ambiente de alta disponibilidade do banco de dados é uma atividade realizada por um DBA e normalmente é transparente para o sistema. Deve-se ter atenção ao Oracle e ao Microsoft SQL Server, pois nem todas as edições suportam configurações de alta disponibilidade.

Em relação à configuração do sistema, é recomendado que o endereço do banco de dados seja um nome em vez de um IP, permitindo que a mudança do ambiente principal do banco de dados para o de contingência possa ser realizado via configuração do endereço IP associado ao nome, sem a necessidade de reconfigurar o Manage dos servidores de aplicação.

Além da cópia de dados provida pelo ambiente de contingência, é recomendado que seja criada uma política de backup que mantenha cópias históricas da base de dados para eventuais cenários de sequestro ou exclusão de dados. Essas cópias preferencialmente devem ser gravadas em um ambiente offline ou devem ser protegidas contra exclusões originadas a partir do ambiente de produção ou de contingência.

Importante: em caso de incidentes onde o ambiente de contingência não corresponde fielmente ao último estado do ambiente de produção, seja devido a falhas de sincronização ou a restauração de backups com perda parcial de dados, mesmo que seja por uma defasagem de horas ou de minutos, deve-se descartar o cache local de todos os Engines por meio do processo “Admin > Cache local > Descartar o cache de dados e de chaves”. Esse descarte precisa ocorrer antes dos usuários voltarem a utilizar o sistema, caso contrário poderão ser geradas chaves duplicadas no sistema, entre outros problemas de consistência e integridade dos dados. Por esse motivo, é sugerido que a transição entre o ambiente de produção e o de contingência não seja automático, caso não seja possível dar garantia de que os dois ambientes são idênticos quanto aos dados gravados nos bancos de dados.

Redundância dos servidores de aplicação e de borda

Via de regra, os dados do sistema são gravados no banco de dados. Portanto, se este ambiente estiver protegido e for redundante, os demais componentes do sistema podem ser facilmente reestabelecidos, sendo necessário apenas o esforço de reconfiguração do ambiente.

No entanto, para aplicações críticas, o tempo de interrupção para a configuração de um novo ambiente pode ser indesejado ou proibitivo. Para evitar esse prejuízo, deve-se configurar um ambiente com redundância dos servidores de aplicação, evitando tempos de parada para todas as unidades de negócio e usuários externos, e, idealmente, para os servidores de borda, nas unidades de negócio específicas que utilizem esses servidores.

A redundância dos servidores de aplicação e borda pode ser obtida instalando mais de um Engine, idealmente em um servidor distinto, garantindo assim a proteção para falhas de hardware e distribuindo o custo de processamento do sistema. A carga sobre esses vários Engines pode ser distribuída manualmente, por meio de URLs de acesso diferentes, ou pode ser utilizado um balanceador de carga, garantindo que os usuários não precisem alterar o endereço de acesso do sistema em caso de um incidente.

Cópia do cache de dados do Engine

Em servidores distantes do banco de dados, a construção do cache de dados do Engine pode ser uma operação demorada em função da quantidade de registros das tabelas cadastrais e da velocidade da conexão de rede entre o Engine local e o servidor de aplicação. A redundância de Engines no ambiente local diminui o impacto da interrupção de um Engine para uma eventual reconstrução do cache de dados, mas, caso seja importante reestabelecer mais agilmente um Engine, o cache de dados pode ser copiado a partir de um outro Engine em funcionamento. Para isso, devem ser realizados os seguintes passos:

  1. Interrompa o Engine com o cache de dados a ser copiado.
  2. Copie o diretório “dbCache” desse Engine para um outro diretório local.
  3. Reinicie o Engine que teve o cache de dados copiado.
  4. Mova o diretório “dbCache” copiado no passo 2 para o diretório de instalação do Engine a ser reestabelecido.
  5. Apague os diretórios “dbCacheProvider” e “dbCacheBackup” que eventualmente possam existir no diretório do Engine a ser reestabelecido.
  6. Inicie o Engine que recebeu a cópia do cache de dados.

Importante: o diretório de instalação do Engine contém arquivos de controle que devem ser únicos para cada Engine em operação, além de dados de controle como scripts agendados e cache de chaves do sistema que jamais podem ser duplicados. Portanto, nunca copie o diretório de de um Engine com o objetivo de acelerar a inicialização de um outro Engine. Para esse fim, apenas o subdiretório “dbCache” é relevante e pode ser copiado com segurança.

Balanceadores de carga

Para evitar que os usuários utilizem mais de um endereço de acesso ao sistema, pode ser configurado um balanceador de carga que publique um endereço único para o sistema e distribua as requisições recebidas dos usuários e dos Engines para um conjunto de servidores.

Para configurar o balanceamento de carga, é importante levar em consideração a natureza dos dois tipos de acesso que um Engine pode receber.

Requisições originadas dos navegadores utilizados pelos usuários finais

As requisições oriundas dos navegadores, geradas normalmente pelo Web Framework, são bem típicas em relação ao tráfego web convencional e são tratadas pelas maioria dos balanceadores de carga. No entanto, pela natureza do controle de estado no servidor utilizado pelo Web Framework, as requisições originadas de um usuário devem ser direcionadas sempre para o mesmo servidor que iniciou a sessão do usuário, conceito chamado de afinidade ou persistência de sessão pelos balanceadores de carga.

Há diversas estratégias para controlar a afinidade de sessão. Se o balanceador de carga suportar, é recomendado que a afinidade seja controlada por cookie. Para isso, o sistema define o cookie “serverId” com o objetivo de indicar para o balanceador de carga como estabelecer a afinidade de sessão. Caso ele não suporte a configuração de um cookie para esse fim, pode ser configurada a afinidade por IP de origem, que pode não ser ideal caso o tráfego seja originado por computadores que estejam em uma rede que utiliza NAT e que acabem sendo identificadas por um único IP de Internet válido. Outros balanceadores de carga, como o Google Cloud Load Balancing empregam cookies para estabelecer a afinidade, mas eles fazem de forma automática, sem permitir indicar um nome de cookie.

Requisições dos Engines clientes e de borda

A comunicação Engine-a-Engine também utiliza o protocolo HTTP, mas ela tem algumas particularidades que não são suportadas por todos os balanceadores de carga. São elas:

  • Requisições HTTP Chunked: enquanto o uso da codificação de transferência chunked é bem aceita nas repostas de servidores HTTP, nem todos os balanceadores de carga aceitam requisições nesse formato. Dado o volume de dados contidos nas requisições do sistema, o protocolo do Engine exige o uso dessa codificação tanto no envio das requisições HTTP, como nas respostas.
  • Conexões inativas de longa duração: na comunicação entre Engines, requisições originadas de um cliente podem ser demoradas, como as gravações ou consultas no banco de dados envolvendo milhares de registros. Essas requisições costumam ter uma duração muito superior a uma requisição convencional de uma navegador Web. Durante o processamento do servidor, a conexão fica inativa, sem o envio ou recebimento de dados. Alguns balanceadores de carga impõem limites de inatividade e, sempre que for possível, esses limites devem ser desativados ou configurados para valores bem altos, superiores ao tempo máximo de processamento esperado entre Engines levando em consideração o uso típico da base de dados. É recomendado que esses limites sejam ao menos superiores a 24 horas de inatividade.

Também é importante observar que esses requerimentos se aplicam a qualquer software intermediário entre os navegadores, Engines clientes e os servidores de aplicação, portanto, eles também devem ser atendidos por eventuais firewalls, proxies e antivírus que interceptem o tráfego do sistema. Para evitar ou diminuir a interferência desses softwares, é recomendado que o tráfego do sistema utilize o protocolo HTTPS sempre quando isso for possível.

Balanceadores suportados

Em resumo, qualquer balanceador de carga que implemente a afinidade de sessão, preferencialmente por cookie, suporte requisições HTTP chunked de qualquer tamanho e que não estabeleça um limite de inatividade de conexão pode ser utilizado para distribuir o tráfego do sistema. Abaixo são relacionados alguns balanceadores de carga utilizados em ambiente de produção e conhecidos por serem compatíveis com o sistema.

Google Cloud Load Balancing

Dentro dos serviços de nuvem do Google, há o balanceador de carga HTTP(S) que pode ser utilizado com o sistema, tanto para o tráfego Web como também aquele originado de Engines clientes. Para isso, devem ser realizadas as seguintes configurações nos serviços de back-end:

  • Timeout de conexão: 864000 segundos. Na prática, isso aumenta o limite de tempo de requisições para 10 dias, suficiente para as operações mais pesadas, como processamentos de arquivos fiscais. Caso a duração seja insuficiente ou muito elevada, ela deve ser gradualmente ajustada para atender o perfil do negócio.
  • Afinidade de sessão: deve estar ativa com opção “Cookie Gerado” e o TTL do cookie deve estar configurado com “0” (cookie de sessão não permanente).

Fortigate

A solução de balanceamento de carga integrada na solução Fortigate é conhecida por ser compatível com o sistema. Deve-se ter atenção às seguintes configurações:

  • persistence: utilizar “http-cookie”.
  • http-cookie-age: configurar com valor “0”, garantindo que o cookie seja preservado até o final da sessão do usuário.

Por ser uma solução integrada, é importante desativar as regras de firewall e proxy que possam interferir no tráfego nos endereços e portas utilizadas pelo sistema. O proxy é particularmente desnecessário, pois os dados gerados pelo sistema são majoritariamente dinâmicos e o seu uso é conhecido por restringir as requisições chunked originadas pelos Engines clientes.

Atualização do sistema utilizando um balanceador de carga

No início do processo de atualização do sistema, o Engine que receberá a atualização precisa ser reiniciado para efetivar a atualização do executável. Se isso ocorrer via balanceador de carga, não há garantias de que o Engine que recebeu a nova versão seja o mesmo que receberá o comando de reinício e as demais requisições da atualização. Por esse motivo, a atualização do sistema sempre deve ser realizada utilizando um endereço vinculado a uma única instância de Engine, sem passar por balanceadores de carga. Também é importante que o Engine que receba a atualização do sistema tenha acesso às demais instâncias de Engine, permitindo que ele possa atualizar e reiniciar essas instâncias.