A F5 informa no K6091 que o conjunto de comandos de iRules do BIG-IP foi desenvolvido a partir da versão base 8.4.6 do (Tool Command Language), e que essa base é usada em todas as versões do BIG-IP. Então, quando um iRule compara um cabeçalho com um número ou faz conta com um valor da requisição, ele segue as regras do Tcl 8.4.6 para ler números. A maioria dessas regras é o que se espera. Uma delas não é, e aparece em produção como uma regra que funciona a manhã inteira e falha às 08:00.
Como o Tcl 8.4 lê um inteiro
O manual de expr do Tcl 8.4 dá três formas de escrever um inteiro:
- dígitos sem prefixo são decimais:
10é dez; - um
0inicial torna os dígitos octais, base 8:010é oito; - um
0xinicial os torna hexadecimais:0x10é dezesseis.
A regra do octal é a armadilha. Os dígitos octais vão de 0 a 7, então 08 e 09 têm a forma de um número octal e um dígito que o octal não tem. O Tcl não volta para decimal. O texto 08 simplesmente não é um número.
| Escrito | Lido como | Valor |
|---|---|---|
10 | decimal | 10 |
010 | octal | 8 |
0x10 | hexadecimal | 16 |
08 | não é número | nenhum |
Onde morde: valores com zero à esquerda
As requisições estão cheias de números com zero à esquerda: horários, datas, códigos, campos de versão. Digamos que um cabeçalho traga um horário e um iRule separe a hora:
set hour [getfield "08:30" ":" 1]
if { $hour > 7 } {
pool day_pool
}
Às 08:30 essa condição é falsa. $hour contém 08, que não é um número, então > passa a comparar texto, e o texto 08 vem antes de 7 porque o caractere 0 vem antes do 7. A aritmética é mais escandalosa: expr {$hour + 1} falha com "can't use invalid octal number as operand of +". Às 07:30 as duas funcionam, porque 07 é um sete octal válido.
A correção é ler o valor como decimal de propósito. scan com %d faz exatamente isso, com ou sem zero à esquerda:
scan "08:30" "%d:%d" hour minute
# hour vale 8, minute vale 30
Dois lugares onde o Tcl lê um número
O Tcl lê números em dois lugares diferentes, e eles falham de jeitos diferentes:
- Um literal escrito dentro da expressão, como
expr {$code == 08}. O Tcl 8.4 verifica os literais ao compilar a expressão, então08é um erro antes que qualquer parte da expressão rode: "expected integer but got 08 (looks like invalid octal number)". - Um valor guardado numa variável, como
set code 08; expr {$code == 8}. Aqui o valor só é lido quando um operador precisa de um número. A aritmética falha nesse momento; uma comparação passa a comparar texto sem avisar, e$code == 8simplesmente dá falso.
O segundo caso é o perigoso, porque nada vai para o log.
Doubles, e os dígitos que você não vê
Um número com ponto decimal ou expoente é um double, um valor binário de ponto flutuante. O Tcl nunca trata um double como inteiro, nem um redondo: 3.0 == 3 é verdadeiro, mas 3.0 é impresso com o ponto decimal e o operador %, que só aceita inteiros, o recusa. 1e3 é lido como o double 1000.0.
O Tcl 8.4 imprime doubles com 12 dígitos significativos, porque sua variável tcl_precision vale 12 por padrão. Isso deixa a saída limpa e esconde o resto do valor: expr {0.1 + 0.2} imprime 0.3, mas expr {0.1 + 0.2 == 0.3} dá 0, porque a soma guardada é 0.30000000000000004. Compare doubles com uma tolerância, ou trabalhe com unidades inteiras (milissegundos, centavos) quando puder.
A divisão segue o tipo dos operandos. 10 / 3 é divisão inteira e dá 3; 10 / 3. é divisão de doubles e imprime 3.33333333333. A divisão inteira arredonda em direção ao infinito negativo, então -7 / 2 dá -4, não -3.
Valores grandes
Os inteiros neste motor têm 64 bits, como o Tcl 8.4 os compila numa plataforma de 64 bits. Valores de 2 elevado a 63 até 2 elevado a 64 menos 1 são aceitos e dão a volta para números negativos, então 0xFFFFFFFFFFFFFFFF é lido como -1; acima disso, o valor é recusado com "integer value too large to represent". Um build de 32 bits do Tcl 8.4 leria valores grandes de outro jeito, então código que depende de valores além de 32 bits merece um teste no sistema que vai rodá-lo.
Experimente
O conversor de notação numérica recebe qualquer valor como você o escreveria e mostra como ele é lido, suas outras notações, as leituras como literal e como variável lado a lado, e como ==, eq, < e > o comparam com um valor à sua escolha. O laboratório de expressões avalia expressões inteiras e mostra onde cada operando foi lido como número ou como texto.
Fontes
A versão do Tcl vem da base de conhecimento da própria F5, as regras de leitura do manual do Tcl 8.4, e a precisão de impressão do código-fonte do Tcl 8.4.6; todos os exemplos acima foram rodados no Tcl 8.4.6.