Friday, October 17, 2008

Cache de DataSets em aplicações multi-camadas II

Identificando unicamente um DataSet.

Bem, quanto aos requisitos do post anterior:

1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet

Como identificar unicamente um DataSet obtido a partir de uma consulta a um banco de dados? Bem, no meu caso os DataSets são resultado de uma consulta SQL. Independentemente do mecanismo de acesso a dados, quando se utiliza SQL em geral temos um objeto de query (TADOQuery, TSQLQuery, etc.) e o mesmo possui uma propriedade SQL do tipo TStrings. A string que contém o comando SQL é, com certeza, um identificador único de uma query certo? Mas a string não é ideal para um identificador único, pois pode ser gigante, difícil de comparar, etc. O melhor mesmo é gerar um hash a partir do SQL. SQL diferentes deverão gerar hashes únicos com uma probabilidade de colisão suficientemente baixa.
No meu caso, optei por usar não um hash propriamente dito, mas um CRC. Optei por usar o CRC64 que me possibilita ter uma probabilidade de colisão suficientemente baixa para uma aplicação de qualquer tamanho.
Quem tiver curiosidade sobre esta probabilidade, segue um teste com o algoritmo:

http://apollo.backplane.com/matt/crc64.html

Resumindo, a probabilidade de colisão é da ordem de 1 colisão para 2E12 mensagens, o que acredito que seja mais do que satisfatório.

Então, passamos nossa expressão SQL por um algoritmo de geração de CRC64 e obtemos um identificador único para nossa sentença SQL, como por exemplo CF247CF5C26FF896.

Mas temos ainda um problema: A mesma sentença de SQL executada contra dois bancos de dados diferentes (mas com estrutura compatível, como por exemplo um banco de dados de produção e outro de homologação) possuem identificadores idênticos porém o DataSet resultante é diferente.
Neste caso devemos incorporar ao identificador único do DataSet um identificador do banco de dados. O banco de dados geralmente é identificado pelo servidor, nome do banco ou schema, usuário, etc. Muitas formas são possíveis para se gerar um identificador do banco de dados. No meu caso, usando ADO como mecanismo de acesso a dados, optei por seguir a mesma linha de raciocínio e usar o CRC64 da string de conexão (propriedade ConnectionString do TADOConnection).

Então, com o CRC do SQL mais o CRC da string de conexão eu tenho um identificador único para meu DataSet que funcionará como índice. Mais sobre isto em breve.

Wednesday, October 15, 2008

Cache de DataSets em aplicações multi-camadas

Há algum tempo utilizo mecanismos diversos de cache para melhorar a performance das aplicações que desenvolvo, principalmente com relação a acesso a bancos de dados. De uma forma mais aplicada venho desenvolvendo um mecanismo genérico de cache, para ser utilizado em aplicações multi-camadas. Problemas simples como o do post anterior (campos de lookup) podem ser muito beneficiados com mecanismo deste tipo.

É interessante ver que a maioria das aplicações mais "avançadas" ou elaboradas possuem algum mecanismo de cache mas no "mundo real" das aplicações comerciais que possuem acesso pesado a bancos de dados, o uso de um mecanismo de cache, por mais precário que seja, é raro ou inexistente.
O Delphi tem, out of the box, tudo que é necessário para fazer um cache nem tão rudimentar assim. Na verdade, depois de pronto, acho que poderia bater em muita coisa comercial que tem por aí... O ClientDataSet é um candidato perfeito para ser um DataSet genérico utilizado em um cache: é um DataSet em memória, pode ser gravado em disco em formato nativo (binário) e está presente na maioria das aplicações multi-camadas escritas em Delphi.

De forma geral e bem simplificada, o cache deveria funcionar da seguinte forma:
- Um DataSet é requerido à aplicação servidora (middle-tier) pela aplicação cliente;
- O mecanismo de cache (iremos chamá-lo de "gerenciador do cache") verifica se este DataSet já está no cache. Caso não esteja, a consulta normal ao banco de dados é efetuada. Caso o DataSet exista no cache, o gerenciador do cache deverá determinar se o cache está atualizado. Se estiver, o DataSet no cache é retornado à aplicação cliente sem necessidade de acesso ao banco de dados. Se não estiver, a consulta normal ao banco de dados é efetuada;
- Caso a consulta normal ao banco seja efetuada (o DataSet não está no cache ou o mesmo se encontra desatualizado) o gerenciador do cache se encarrega de incluir/atualizar o cache com este DataSet recém obtido do banco de dados;
- O gerenciador do cache deve prever acessos simultâneos de diferentes threads;
- O gerenciador do cache deve persistir e recuperar o cache em um repositório local (disco).

Simples não? Bem... não é tão simples mas também não é ciência de foguetes. Veremos que existem vários requisitos que deverão ser satisfeitos para que o mecanismo de cache seja viável. Entre eles, podemos citar:

1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet;
2) O gerenciador do cache deverá ser capaz de identificar de forma inequívoca se o cache reflete a situação atual do banco de dados, ou seja, se o cache está atualizado no que tange àquele DataSet específico.

O requisito número 1 exige que o banco de dados se adapte à aplicação, ou seja, ao uso do cache. Os sistemas que desenvolvo já possuíam uma estrutura adequada, mesmo antes de um mecanismo de cache ser pensado. Então neste caso foi fácil.

Irei detalhar como satisfazer os requisitos 1 e 2 no próximo post.

Sunday, September 21, 2008

Campos Lookup - Uma abordagem racional

Ando lendo em sites e publicações especializadas em Delphi muita coisa sobre campos de lookup e sua utilização em sistemas, como por exemplo as famosas LookupComboBoxes. Geralmente fala-se mal, recomenda-se não utilizar campos lookup. Ou então que sua utilização é muito restrita, e só serve para tabelas contendo poucos registros (exatamente quantos, ninguém fala, certo?).. Coisas que eu ouvia há 10 ou 15 anos, quando quase todo mundo desenvolvia usando os velhos e bons conceitos Cliente/Servidor.

De lá prá cá muita coisa mudou. A banda de internet que a alguns têm em casa é mais rápida que algumas redes que usavam cabos coaxiais em 1995.
Mesmo assim, alguns teimam em usar o mesmo conceito antigo que "não se pode trafegar pela rede meia dúzia de registros que possivelmente não serão usados"... AFFFFF!!!!
Então vejamos... O que é mesmo AJAX? Quanta informação desnecessária é trafegada para dar a uma aplicação web AJAX o seu comportamento?
De qualquer forma, nada melhor que fatos e dados para basear o meu ponto de vista.

Suponha uma tabela "cliente", com a seguinte estrutura:

codigo_cliente - Integer
nome_cliente - varchar(50)
codigo_profissao - Integer

e uma tabela "profissao" com a seguinte estrutura:

codigo_profissao - Integer
descricao_profissao - varchar(30)

Em 99% dos casos eu ouço que não se deveria fazer lookup na tabela de profissões, e sim um JOIN.
Imagine uma consulta típica para um relatório, onde irei listar os clientes e suas profissões. O SQL típico seria:

SELECT c.codigo_cliente, c.nome_cliente, p.descricao_profissao
FROM cliente c
INNER JOIN profissao p
ON c.codigo_profissao = p.codigo_profissao
ORDER BY c.codigo_cliente ASC

Com alguma simplificação podemos assumir um tamanho de registro de dados igual a 84 bytes. Se eu tivesse 2000 clientes, teríamos um pacote de dados de aproximadamente 165 kbytes.

Agora, vejamos por outro lado. Imagine que eu tivesse 2 consultas separadas:
SELECT codigo_cliente, nome_cliente, codigo_profissao
FROM cliente
ORDER BY codigo_cliente ASC

SELECT codigo_profissao, descricao_profissao
FROM profissao

Vamos supor ainda que minha tabela de profissão contivesse 500 registros. O total de dados trafegados seria 58*2000 (clientes) + 34*500 (profissões) = 130 kbytes.

Oras! Eu fiz duas consultas, trouxe toda a tabela de profissões para o lado cliente e ainda tive um tráfego menor???? Hum... Agora, imagine que este relatório é chamado por várias vezes durante a execução do programa?

Quantas vezes por dia a tabela de profissões muda? Talvez sejam adicionadas novas profissões, de vez em quando, mas em sistemas do mundo real isto ocorre muito no início do ciclo de vida do sistema e tende a diminuir - e muito - com o tempo. Não poderíamos carregar a tabela de profissões, digamos na inicialização do sistema, e usá-la como Lookup durante o ciclo de vida da aplicação?

Um mecanismo mais inteligente poderia checar as alterações da tabela de profissões antes de sua utilização (como lookup no relatório, por exemplo) e fazer um refresh sempre que necessário. O ganho de performance tanto de banco de dados quanto da aplicação é significativo.

Alguns conceitos de C/S estão certamente ultrapassados. O pessoal que os usa sem parar para pensar (e principalmente MEDIR) o que acontece de fato em sistemas do mundo real deveria revê-los.

Saturday, August 9, 2008

Novo!! Fórum de discussão da CodeGear

Está funcionando desde 7 de agosto o fórum de discussão da Embarcadero (e CodeGear) no endereço https://forums.codegear.com

Ainda é beta, mas parece bom.

Pelo visto, eu fui o primeiro a registrar e postar do Brasil! :)

O lançamento do fórum foi adiantado pelo crash do servidor de newsgroups, que pelo visto foi feio - há dois dias o newsgroup da CodeGear está offline.

Thursday, July 10, 2008

Então... Aplicações desktop estão acabadas?

Hum... Interessante. Cada um que me diz esta frase pagou algumas centenas de dinheiros para comprar uma cópia de Windows, certo? Bem provavelmente, pagou mais algumas centenas para ter a última versão do MS Office, right? Bem, não entendo é o seguinte: Se as aplicações desktop estão acabadas porquê continuar pagando por um sistema operacional SE a sua única função é servir como plataforma para um browser - que muitas vezes nem é o Internet Explorer, como no meu caso (Firefox rules!). Não seria mais fácil jogar no lixo todas as suas caixinhas com o logo "Windows compatible" e fazer o download do Linux?
Os mesmos "otimistas" que costumam falar este tipo de coisa devem estar doidos para comprar - se é que já não compraram - um processador Quad Core, e viajam pensando como será o dia que a Intel ou a AMD lançará um processador com 16, 32, 256 núcleos para desktops. Me pergunto para quê serviriam 256 núcleos num processador, se a única aplicação vivente no computador do cidadão será um web browser...

Eu me lembro quando era criança... Eu via um Puma GTB - um carrão na época - e falava com meu pai: "quero comprar um destes quando crescer". E meu pai respondia "quando você crescer os carros voarão, como no desenho Jettinsons". Alguns anos antes, a gerra fria estava no auge. Jonh Kennedy torrava notas de mil dólares para mostrar aos russos que os EUA dominavam o avanço tecnológico e podiam enviar homens à lua. Este era o objetivo. Obviamente meu pai foi levado a superestimar nosso futuro tecnológico devido ao oba-oba que arrebatou o mundo.

Hoje a história é outra. Avanço tecnológico é uma coisa. Ignorar os objetivos das forças econômicas que financiam todo este avanço tecnológico é estupidez.
Think about it.

Thursday, June 26, 2008

ExtPascal - Pascal para Ext JS

Legal! Wanderlan Anjos publicou uma versão inicial de seu framework ExtPascal, um wrapper para as bibliotecas Ext JS.
Está em seus estágios iniciais mas vale uma olhada. Baixei os fontes e irei criar um projeto piloto para verificar a possibilidade de utilização em algum projeto! Parabéns Wanderlan!

Thursday, June 12, 2008

G-Buster revisited. Batendo a praga em seu próprio jogo!

ATENÇÃO: Este procedimento não é válido para as versões mais recentes do G-Buster. Um novo post explica como bater esta praga novamente! Estou adorando brincar de gato & rato com a GAS Tecnologia! ;-)
A mais nova versão da praga pode ser removida mais fácil que antes, com o novo procedimento descrito neste link.

Pois bem amigos. Nunca pensei que o G-Buster Browser Defense fizesse tanto mal a tanta gente! Depois que fiz o post com o método que usei para remover a praga do meu PC recebo pelo menos dois e-mails por dia. Alguns agradecendo pela receita, outros se lamentando que não conseguiram, mesmo seguindo fielmente os passos para a remoção do G-Buster.
Bem, resolvi então fazer um segundo post sobre o assunto. Juntei inclusive opiniões e dicas de leitores, e apimentei um pouco a situação! Resolvi bater o G-Buster em seu próprio jogo SUJO de mudanças de permissão de acesso a chaves da registry, arquivos, etc. Então vamos lá! PS: Uso Windows XP, mas a receita funciona para todos!

1) Faça logon no Windows como administrador da máquina, sem permissões de administrador, nada feito.

2) Conforme explicado no meu outro post, o G-Buster é composto de uma DLL que é o controle ActiveX que interage com o Internet Banking do banco em questão, e também por um serviço, o abominável GbpSv.exe, ambos na pasta C:\Arquivos de programas\GbPlugin (ou C:\Program Files\GbPlugin se seu Windows for em Inglês).
O GbpSv.exe TEM que ser removido. Ele é o serviço que fica ativo 24x7 em sua máquina usando recursos sem a menor necessidade, e principalmente, SEM A SUA PERMISSÃO!

Você pode ver o G-Buster em execução teclando Ctrl+Alt+Del e abrindo o Task Manager (uso o DTaskManager), conforme a figura abaixo:



Não adianta tentar matar o G-Buster a partir do Task Manager. Ele entra em execução novamente. Mas... Se o arquivo executável do programa não pode ser acessado para leitura, então ele não pode ser executado, certo? CERTO! Então vamos remover a permissão de leitura do arquivo GbpSv.exe.

Siga os passos que constam nas figuras abaixo:

2.1) Removendo a propriedade de leitura do diretório C:\Arquivos de programas\GbPlugin:

Vá no Windows Explorer encontre a pasta C:\Arquivos de programas\GbPlugin. Clique com o botão direito do mouse e escolha no menu Propriedades:



Vá na aba Segurança. Clique em avançado e desmarque a opção "Herdar do Pai as entradas de permissão aplicáveis..."



Clique em OK. O Windows irá perguntar se é para copiar as permissões ou excluí-las. Para facilitar a demonstração, vou supor que você irá escolher COPIAR. Então teremos depois disso:



Remova, um por um, TODOS os usuários da lista de usuários com permissão. Deixe somente o usuário SYSTEM e o usuário LOCAL SERVICE. Se os usuários SYSTEM e LOCAL SERVICE não estiverem na lista adicione-os. A partir do momento que somente os dois usuários estiverem na lista você irá NEGAR acesso (TODOS) a estes usuários. Teremos então:



A partir daí, nem o Windows Explorer terá acesso aos arquivos e você não mais poderá vê-los no painel da direita.

3) Agora, reinicie a máquina. O G-Buster não conseguirá mais ser carregado, mesmo que as entradas na Registry ainda sejam mantidas, pois não terá mais acesso ao HD na pasta onde está o arquivo executável.

Após o boot, repare no Task Manager que o G-Buster não está mais na lista de processos em execução:



Agora vamos bater o G-Buster em seu próprio jogo sujo. Atenção: Estes passos são opcionais. Não é necessário fazer isto pois o G-Buster já foi removido, mas eu fiz para impedir que ele volte quando eu acessar novamente o Internet Banking.

4) Adicione acesso total ao seu usuário na pasta do G-Buster, conforme a figura:



Agora, renomeie o GbpSv.exe para um outro nome qualquer que não tenha extensão EXE (eu usei GbpSv_Ex_EXE.OLD) com esta extensão OLD ele é inofensivo, e não pode ser executado pois o Windows não reconhece esta extensão).

Crie então um arquivo com o nome que o serviço tinha antes. Pode ser um arquivo texto mesmo, de tamanho zero, com o nome GbpSv.exe. Deixe o arquivo sem permissão nenhuma de nenhum usuário como a figura:

Renomeando o GbpSv.exe:


Criando um novo arquivo GbpSv.exe, que não é executável:


Negando acesso ao arquivo GbpSv.exe:


Você pode estar se perguntando para quê eu fiz isto? Criar um arquivo vazio com o nome GbpSv.exe e bloquear o acesso a ele. Isto é uma forma simples de impedir que o instalador do plugin que roda quando você acessa o site consiga instalar de novo o GbpSv.exe original. Mesmo que as permissões da pasta sejam alteradas a existência de um arquivo de mesmo nome e sem permissão de acesso impede que um novo GbpSv.exe seja copiado para esta pasta. Ou seja, não tem mais como o instalador instalar a praga novamente. Será?

Bem a melhor forma é testar de novo, acessando o site do banco. Testei na Caixa Econômica Federal e olhem aí o Internet Banking totalmente funcional:



Será que o G-Buster não voltou? Hum... Desta vez não! ;-)

Bem, este segundo método de remover o G-Buster é mais fácil que o primeiro que fiz, mas mesmo assim é cheio de etapas. E pela extensão do processo, fica difícil explicar detalhadamente cada passo e usuários menos experientes em configurações avançadas do Windows podem ter alguma dificuldade. Mesmo não sendo difícil, é longo e passível de erros. Se você tentou e não conseguiu, respire fundo, acalme-se e comece de novo, passo-a-passo! ;-) Vale a pena! Depois disso você fica livre do G-Buster e continua acessando seu Internet Banking, sem problemas!

Você pode continuar o extermínio da praga até o fim, eliminando as entradas da registry pertinentes, conforme eu mostrei no primeiro post sobre o assunto. Não que seja necessário, mas eu gosto de começar e TERMINAR o serviço, então na minha máquina eu eliminei cada traço do G-Buster na registry!

Se quiser continuar até o fim, remova as DLL's que estão na pasta do G-Buster, ou, melhor ainda, renomeie-as usando um outro nome qualquer. Agora que o serviço GbpSv.exe não está mais ativo é fácil remover ou renomear as DLL's (após a reinicialização do sistema e ANTES de qualquer acesso ao Internet Banking do seu banco, caso contrário a DLL será carregada e estando em memória não pode ser excluída). Lembre-se somente que, excluindo a DLL, sempre que você acessar o Internet Banking será solicitado que você instale novamente o plugin ou ActiveX do banco, o que reinstalará a DLL (mas não o serviço GbpSv.exe, caso você tenha bloqueado o acesso ao arquivo de mesmo nome que está na pasta).