Thursday, April 07, 2016

Error -76719 / Erro -76719

If you see this error, you're most likely using a J2EE application server, and you have a firewall between it and your database (original version http://informix-technology.blogspot.com/2015/02/error-76719-erro-76719.html)




English version
I've found this error many times. And recently it happened again while working on a customer. Invariably it happens on application servers with a firewall between them and the database. The error description is:

-79716    System or internal error.

An operating or runtime system error or a driver internal error
occurred.

The accompanying message describes the problem.

Often the error also shows this message:

java.net.SocketException: Broken pipe

If you find this, most likely you have a firewall between the database and the application server and your firewall is forcing the closing of the sockets. Why would it do that? Typically the firewalls are configured to close connections they consider "idle" for a period of time. And usually in an application server, using connection pooling it may happen that a specific connection is not used for many minutes. Without traffic in the connection the firewall will decide the connecting is "forgotten" and will close it. To be honest this is the part I never was able to figure out... when a system decides to close a socket, a signal (13) is usually sent to the endpoints. Upon receiving the signal, the client and server learn the connection was broken and they can create another one, or raise an appropriate error. The problem I'm used to, is that the client (in this case the J2EE application server) never receive (or ignore?) that signal. So they don't "know" the socket is not there anymore. Instead they keep the socket reference associated with a connection that is part of their pools. Once a client application requests a connection from the pool, it may get one of these "phantom" connections. When it tries to send the SQL request, the exception is generated and the error is raised.
All this explains the problem. But what we really want is the solution. And that is a bit more complex to explain but trivial to implement. In fact there are two solutions:

  1.  Have you network administrators change the firewall rules either to increment the period before considering a connection as stale, or to ignore Informix connections. I never saw a situation where this happened. Somehow the network admins act like dictators and they refuse any change.
  2. Force some sort of traffic through the connection so that the firewall doesn't think the connection is stale
As mentioned, the first option is usually not considered possible. And the second is easy to implement. Thankfully the operating systems already have a mechanism to test the connections which is called "keepalive" or "keepalive probes". The idea is very simple. At specific intervals the system sends a "probe" (special packet) to the other side and wait until either a response is received or a timeout is reached. Keep in mind this is an OS mechanism. And as you would expect it requires a few things to work:
  1. That the OS "decides" to activate the mechanism (as we'll see we can direct Informix to ask for it)
  2. That we configure the "interval" at which a "probe" will be sent
  3. That we configure the maximum time to wait for an answer
  4. Eventually that we configure the number of probes to send before assuming the connection is not good if no answer is received
Typical defaults imply that keepalive is not used, and that the interval is 7200 seconds (2H). Not all operating systems have the timeout and number of probes as parameters.. But I mentioned that there is a way to instruct Informix to request for the keepalive mechanism on it's sockets. Currently we can do it on the server side and on the client (JDBC) side:
  • On the server side just adding a "k=1" to the $INFORMIXSQLHOSTS options field
  • On the client (JDBC) side just add the property IFX_SOC_KEEPALIVE and set it to true. Note that this is available only on version 4.10.JC6+
Keep in mind that these options only request the OS to activate the keepalive functionality. The intervals and timeouts must be configured at the operating system level where the Informix functionality is activated. Informix does not send any "fake" traffic by itself.

Another option on some application servers would be to "test" the connections (usually requires some dummy and simple query), or define a maximumage for the connections. But these configurations depend on your application server 

A final note: Sometimes we need to check if an opened socket is using the keepalive option. In most operating systems we can use the lsof command with the option "-Tf". This adds the socket options used to open the socket (or added later) to the output. But this option does not work on Linux. In this OS we can use the netstat command with the "-o" option:


castelo@primary:informix-> netstat -no | grep ":10000"
tcp        0      0 192.168.142.202:10000       192.168.142.202:33053       ESTABLISHED keepalive (5332.90/0/0)
tcp        0      0 192.168.142.202:33053       192.168.142.202:10000       ESTABLISHED keepalive (5332.88/0/0)
castelo@primary:informix->
 


The number shown between parenthesis is the counter till the sending of the next probe.





Versão Portuguesa
Encontrei este erro muitas vezes. E voltou a acontecer novamente enquanto estava num cliente. Invariavelmente isto acontece quando existe um firewall entre um servidor aplicacional (normalmente J2EE) e a base de dados. A descrição do erro é:

-79716    System or internal error.

An operating or runtime system error or a driver internal error
occurred.

The accompanying message describes the problem.

Muitas vezes aparece também a seguinte mensagem:

java.net.SocketException: Broken pipe

Se encontrar isto, a probabilidade é que exista um firewall entre o servidor aplicacional e a base de dados que esteja a fechar os sockets de comunicação. Porque o haveria de fazer? Porque tipicamente os firewalls estão configurados para fecharem as ligações que consideram inativas por um determinado período de tempo. E é normal que num servidor aplicacional que utilize connection pooling uma determinada conexão pode não ser usada durante largos minuto. Sem tráfego na ligação o firewall irá considerá-la como "esquecida" e fechá-la-á. Para ser honesto esta é a parte que nunca percebi.... Quando um sistema quebra uma ligação (por interrupção física da comunicação por exemplo) a aplicação associada ao socket normalmente recebe um sinal (13). Ao receber este sinal a aplicação sabe que a conexão foi fechada e pode tomar as ações apropriadas, como recriá-la ou gerar um erro.
O problema a que me habituei é que o cliente (neste caso o servidor aplicacional J2EE) nunca recebe (ou porventura ignora?) o sinal. Por isso não "sabe" que o socket já não existe. E portanto mantém uma referência a um socket que já não existe na sua pool de conexões. Quando uma aplicação pede uma conexão dessa pool pode receber a referência "fantasma". E ao tentar usá-la para enviar uma instrução SQL para a base de dados é gerada a exceção com o erro referido.
Tudo isto explica o problema. Mas o que queremos realmente é resolvê-lo. E isso é um pouco mais complicado de explicar, mas simples de implementar. De facto há duas soluções:

  1. Fazer com que os administradores de rede mudem as regras do firewall para aumentar o período de inatividade aceitável ou para ignorar completamente as conexões ao informix. Nunca presenciei uma situação onde tal acontecesse. Por algum motivo que me ultrapassa os administradores de rede atuam como ditadores e recusam qualquer mudança
  2. Forçar o envio de algum tipo de tráfego através de cada conexão para que o firewall não a considere "inativa"
Como referido, a primeira opção normalmente não é considerada. E a segunda é fácil de implementar. Felizmente os sistemas operativos já dispõem de um mecanismo que testa as conexões, o qual é chamado de "keepalive" ou "keepalive probes". A ideia é muito simples. De tempos a tempos o sistema envia uma "sonda" (pacote especial) para a outra ponta da conexão e espera por uma resposta ou que seja atingindo um timeout. Não nos esqueçamos que isto é um mecanismo do sistema operativo. E para tal há que configurar algumas coisas para que funcione adequadamente:
  1. Que o SO "decida" ativar o mecanismo para determinadas conexões (como veremos podemos configurar o Informix para requisitar isto ao SO)
  2. Que se configure o "intervalo" de teste das ligações (quando serão enviadas as "sondas")
  3. Que se configure o tempo máximo para receção da resposta
  4. Eventualmente que se configurem o número de "sondas" a serem enviadas e que fiquem sem resposta antes de o SO considerar a ligação como "morta"
Por omissão, muitas vezes o mecanismo de keepalive não é utilizado. E os intervalos frequentement estão definidos para 7220s (2H). Nem todos os sistemas operativos usam os parâmetros para timeouts e retries.
Atrás referi que é possível forçar o Informix a activar este mecanismo no SO. Atualmente podemos fazer isso no lado do servidor e no lado do cliente (JDBC)::
  • No lado do servidor basta acrescentar a opção "k=1" no campo das opções no $INFORMIXSQLHOSTS
  • No cliente (JDBC) basta adicionar a propriedade IFX_SOC_KEEPALIVE com o valor true. Note-se que isto só está disponível nas versões 4.10.JC6+
Saliento que estas opções só pedem ao SO que ative a funcionalidade de keepalive. Os intervalos e timeouts serão configurados com ferramentas do sistema operativo. O Informix nunca envia "falso" tráfego por ele próprio.

Outra opção em alguns servidores aplicacionais seria "testar" as conexões (normalmente com uma query simples e rápida), ou definir uma idade máxima para as conexões. Mas estas configurações dependem de servidor para servidor.

Uma nota final: Por vezes é necessário perceber ou verificar se um socket aberto tem mesmo a opção de keepalive ativa. Na maioria dos sistemas operativos podemos usar o comando lsof com a opção "-Tf". Isto acrescenta as flags usadas na abertura do socket. Mas esta opção não funciona em Linux. Neste SO pode usar-se o "netstat" com a opção "-o":

castelo@primary:informix-> netstat -no | grep ":10000"
tcp        0      0 192.168.142.202:10000       192.168.142.202:33053       ESTABLISHED keepalive (5332.90/0/0)
tcp        0      0 192.168.142.202:33053       192.168.142.202:10000       ESTABLISHED keepalive (5332.88/0/0)
castelo@primary:informix->
 


O número entre parenteses é o contador até ao envio da próxima "sonda"