O mesmo trabalho, quatro formas
Uma grande quantidade de iRules faz uma coisa: pega um valor, compara com um conjunto de valores conhecidos e age sobre o casamento. Rotear um host para um pool, bloquear um caminho, mapear uma rede de cliente para uma decisão. Há quatro formas comuns de escrever isso, e elas não custam o mesmo. O próprio "How To Write Fast Rules" da F5 as ordena, e a ordem vale a pena conhecer porque a opção rápida muitas vezes não é mais difícil de escrever que a lenta.
As constatações abaixo são da F5, e a F5 as declara com uma ressalva honesta ("provavelmente por causa de"), porque vêm de medição e não das entranhas do sistema. Elas valem para o subconjunto permitido de que roda dentro do TMM.
A ordem, dos melhores hábitos primeiro
Encadeie suas condições com elseif em vez de empilhar instruções if separadas. Uma sequência de blocos if independentes avalia todos; uma cadeia if / elseif é uma única instrução que para no primeiro casamento, então menos comandos são buscados e executados e menos valores de retorno são ponderados.
# Mais lento: três instruções separadas, todas avaliadas
if { ${x} eq "a" } {
pool a_pool
}
if { ${x} eq "b" } {
pool b_pool
}
if { ${x} eq "c" } {
pool c_pool
}
# Mais rápido: uma instrução só, que para no primeiro casamento
if { ${x} eq "a" } {
pool a_pool
} elseif { ${x} eq "b" } {
pool b_pool
} elseif { ${x} eq "c" } {
pool c_pool
}
Prefira switch a qualquer forma de if quando puder. Comparar uma variável com uma lista de valores é exatamente para o que o switch serve, e ele evita a avaliação extra de expressão que cada if carrega. Também é bem mais fácil de ler.
switch -- [string tolower [HTTP::host]] {
"www.example.com" { pool web_pool }
"api.example.com" { pool api_pool }
default { pool default_pool }
}
Para um conjunto fixo de cerca de cem entradas ou menos, switch (mesmo switch -glob) supera matchclass desde que os dados não precisem mudar independentemente da regra. A razão que a F5 dá é que o antigo comando matchclass faz hash com (Message Digest 5), o que o switch de conjunto pequeno não precisa fazer.
Para uma busca por chave exata em dados estáticos, um array indexado diretamente é o mais rápido de todos. Se o que você tem é de fato um mapa de chave para valor, coloque-o em um array static:: no carregamento e indexe-o diretamente. Isso é uma busca hash O(1) sem nada do trabalho por comparação do switch ou da chamada de função do matchclass.
when RULE_INIT priority 500 {
array set static::pool_for {
www web_pool
api api_pool
img image_pool
}
}
when HTTP_REQUEST priority 500 {
set key [getfield [HTTP::host] "." 1]
if { [info exists static::pool_for($key)] } {
pool $static::pool_for($key)
}
}
As duas perguntas que de fato decidem
Velocidade é só metade da história. Antes de escolher a partir da ordem, responda duas perguntas.
Os dados mudam independentemente da regra? Um corpo de switch e um array static:: estão embutidos no iRule: mudar uma entrada significa editar e recarregar a regra. Um data group (uma class), lido com o comando class, é um objeto de configuração separado que você pode editar sozinho, sem mudança no código que o usa. Então no momento em que sua lista é mantida por outra pessoa, muda com frequência ou é grande, um data group vence por motivos que nada têm a ver com velocidade bruta, e é seguro para CMP.
when HTTP_REQUEST priority 500 {
if { [class match [HTTP::path] starts_with blocked_paths] } {
HTTP::respond 403 content "forbidden"
}
}
Que tipo de casamento é? As quatro ferramentas não são intercambiáveis. Um array static:: faz apenas buscas por chave exata. O switch faz casamentos exatos, e o switch -glob faz padrões curinga simples. Um data group com class match faz exato, e também as formas cientes de endereço e de string (starts_with, ends_with, contains, casamento mais longo em sub-redes IP) que as outras não expressam de forma limpa. Escolha o construto cujo tipo de casamento serve, depois otimize dentro dele.
Primeiro casamento ou casamento mais longo
As quatro construções também diferem em qual entrada vence quando várias poderiam casar. Uma cadeia if / elseif e um switch pegam a primeira, na ordem escrita. class match com starts_with ou ends_with devolve a entrada mais longa que casar, seja qual for a ordem do data group (a referência de class da F5 diz isso). Então, com as entradas /api e /api/v2, o caminho /api/v2/users cai no ramo /api de uma cadeia if ou de um switch, e na entrada /api/v2 de um data group. Ponha o prefixo mais longo primeiro no if e no switch quando for isso que você quer; o data group dispensa ordenação.
Seja qual for a escolha, coloque -- antes do valor que um switch testa, como no exemplo acima. O switch lê qualquer palavra que comece com hífen como opção, então sem -- um valor da requisição como -glob pode mudar o jeito como ele casa, e um desconhecido para o iRule com um erro "bad option" (F5 K15650046).
O construtor de if / switch / class escreve uma mesma decisão dos três jeitos, com as definições dos data groups, e passa um valor de teste por cada um para que a diferença entre o primeiro casamento e o mais longo fique visível.
Uma ressalva sobre os números, com honestidade
Aquele ponto de virada de "cem entradas ou menos" é medido contra o matchclass, que foi descontinuado na versão 10 em favor do comando class. O comando class foi introduzido justamente porque oferece melhor funcionalidade e desempenho que seu antecessor, então o ponto de virada onde um data group ultrapassa o switch é uma diretriz herdada do comando antigo, não um número exato para código moderno. Quando importar, não adivinhe: leia o custo real por evento com estatísticas de timing usando a calculadora de runtime de iRules, e deixe os números da sua própria caixa decidirem.
E quando nenhuma dessas serve, e só então, recorra a uma expressão regular. O linter de desempenho de iRules sinaliza regexp e regsub exatamente por isso: são a forma mais cara de casar, e um de switch, class ou um array static:: é quase sempre mais barato. O artigo companheiro sobre CMP e o namespace static:: cobre por que o static:: é o lugar certo para esse array em primeiro lugar.
Fontes
A ordem, seus motivos com ressalva (incluindo o matchclass usar MD5 como hash) e o array indexado diretamente vêm do How To Write Fast Rules da F5; a regra do casamento mais longo vem da referência de class da F5, e a exposição das opções do switch, do K15650046.