Estou trabalhando em um projeto em que preciso procurar os nomes de host associados aos endereços IP que foram registrados fazendo solicitações HTTP. Atualmente, as pesquisas acontecem como parte de um trabalho ETL diário. O método atual é usar uma função CLR escalar (código semelhante a este é postado em vários lugares na web, postado abaixo com minhas revisões; não tenho certeza de quem era o autor original):
using System.Data.SqlTypes;
using System.Net;
using System.Security;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
public partial class udfn_GetHostName
{
[Microsoft.SqlServer.Server.SqlFunction]
public static string udfn_GetHostname(string IPAddr)
{
try
{
/*
Using deprecated method intentionally.
GetHostEntry() is now recommended.
But it does some irritating things like returning an error if a PTR
record points to a name that doesn't have an A record.
*/
IPHostEntry IpEntry = Dns.GetHostByAddress(IPAddr);
// Test whether the record returned has at least one alphabetic character
// If it does, then it's a name
// Otherwise the DNS server might have returned the IP address
Match match = Regex.Match(IpEntry.HostName.ToString(), @"[a-zA-Z]+");
if (match.Success)
{
return IpEntry.HostName.ToString();
}
else
{
return "None";
}
}
catch(Exception ex)
{
return "Failed";
//return ex.Message.ToString();
}
}
}
Não sou um desenvolvedor C#, então a qualidade do código CLR provavelmente não é boa.
Então eu chamo a função assim depois de carregar novas linhas na dimensão:
-- Update only rows that we just inserted
UPDATE DIM.Network_Addresses
SET reverse_dns = dbo.[udfn_GetHostname](client_ip)
WHERE reverse_dns IS NULL
AND is_current = 1
AND created_date = (SELECT MAX(created_date) FROM DIM.API_Network_Address);
Esse método funciona, mas é muito lento, pelo menos por alguns motivos.
1) O uso de uma função escalar faz com que o SQL Server chame a função CLR uma vez por linha que precisa ser atualizada, usando um novo contexto SQL.
2) As próprias chamadas de função são muito lentas devido à forma como GetHostname() e outras funções de resolução de nome CLR funcionam: tempos limite longos, às vezes várias viagens de ida e volta pela rede que atingem o tempo limite se um servidor DNS não responder ou não houver PTR registro, etc
Alguém poderia recomendar um padrão de design que melhorasse o desempenho da pesquisa de registros DNS reversos e da atualização da tabela?
Estou considerando algumas coisas diferentes:
1) Mova este trabalho para fora do banco de dados e use uma ferramenta como dig para fazer as pesquisas em paralelo.
2) Tente encontrar uma maneira de chamar a função em paralelo ou convertê-la em uma função in-line (não progredindo muito nisso!)
No entanto, qualquer ideia seria bem-vinda.
Não faça isso em SQL. Das poucas coisas que podem ser categorizadas como 'uso estúpido de SQLCLR', fazer chamadas de rede longas e caras está em primeiro lugar. No mínimo, certifique-se de que o código CLR chama
Thread.BeginThreadAffinity()
antes de aguardar a resposta do intertubez (pesquisa DNS e pesquisa reversa incluídas).A maneira correta de lidar com isso é usar um processo externo, colocar os IPs para resolver em uma fila , desenfileirar em lotes e resolver vários (dezenas) de IPs em paralelo usando I/O assíncrono, por exemplo. o não obsoleto
Dns.BeginGetHostEntry()
.