Documento de Projeto do Sistema

Propaganda
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN
RELATÓRIO DE PROJETO
versão 1.0
Documento de Projeto do Sistema
1.Termo de Referência
Esse relatório diz respeito ao edital número 46, “OBJ-REL - Camada de
Persistência Objeto-Relacional”, publicado entre os dias 04 a 11 de maio de 2008,
a ser realizado no período de maio/2008 a outubro/2008.
2.Introdução
De acordo com o descrito no termo de referência desse projeto, os frameworks
utilizados pelo Interlegis permitem o armazenamento de informações tanto em
banco de dados relacionais (PostgreSQL e MySQL) quanto em banco de dados
orientados a objetos (ZODB).
De forma geral, quando trata-se de representar informações dentro do Plone
(utilizando-se o framework Archetypes), a persistência dos dados costuma ser
orientada a objetos (OO) e realizada diretamente no banco de dados orientado a
objetos ZODB. Há algumas soluções do Interlegis como, por exemplo, o SAPL
(Sistema de Apoio ao Processo Legislativo), que podem ser consideradas soluções
“híbridas”, armazenando informações em diferentes modelos (parte das
informações em banco de dados relacional e parte em banco de dados orientado a
objetos). No entanto, o SAPL não utiliza o framework Archetypes e, por sua vez, não
está relacionado com esse projeto.
Esse relatório tratará apenas das considerações relevantes ao primeiro caso, o
mapeamento de informações objeto-relacional. Nesse caso, as informações que
normalmente seriam armazenadas em bancos de dados OO precisam ser mapeadas
para um banco de dados relacional.
3.Considerações Gerais
Apesar de bancos de dados puramente OO serem tecnicamente excelentes, sabe-se
que a adoção de bancos de dados relacionais tem sido a regra geral. Isso se deve a
muitos motivos, mas principalmente ao fato dos bancos de dados relacionais serem
o reflexo de mais de 20 anos de pesquisas, terem uma forte fundamentação teórica
e acima de tudo, a disponibilidade de muitos fabricantes de bancos de dados
relacionais com excelentes ferramentas para as mais diversas plataformas. Esse
cenário “relacional” como “padrão de fato” acaba sempre se refletindo na
necessidade de mapear a representação de informações OO para modelos
relacionais.
O framework Archetypes (sobre o qual o Plone é definido) adota como padrão uma
representação de informações OO e persistência direta no ZODB. No entanto, ainda
em versões bastante antigas do framework Archetypes foi implementado um
mapeamento objeto-relacional conhecido como SQLStorage. Essa implementação
mapeava conteúdos do Plone apenas para o banco de dados PostgreSQL e teve
adoção relativamente restrita, limitada a poucos desenvolvedores. Apesar dessa
implementação ser considerada até hoje como a implementação oficial de
mapeamento objeto-relacional, é sabido que ela sofre de um problema crônico de
performance.
1
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN
Para compreender o restante desse relatório e as decisões tomadas durante o
projeto é importante esclarecer alguns conceitos. A principal consideração a ser
feita e que impacta diretamente na escalabilidade e performance dos sistemas ditos
“transacionais” (entenda-se aqui por “sistemas transacionais” aqueles sistemas
gerenciais mais comuns, como folha de pagamento, contabilidade, financeiro, etc,
onde existe um volume considerável de dados sendo processados de forma
transacional) é que esse tipo de sistema nunca foi o alvo principal de plataformas de
servidor de aplicação web como o Zope (servidor de aplicação sobre o qual o Plone
é escrito). Apesar de ser possível desenvolver sistemas transacionais sob a
plataforma Zope, em sistemas assim existe uma quantidade considerável de
transações de escrita nos banco de dados e essa quantidade costuma ser bastante
superior a que encontraríamos em uma aplicação web padrão (como um portal de
uma Câmara Municipal) e para o qual o Plone foi planejado. Em uma aplicação web
característica existe certamente um relação maior de “leituras x escritas” se
comparada a aplicações transacionais.
Nesse sentido, o ZODB, camada de persistência OO do Zope, tem características em
sua arquitetura que o fazem superior quando implementado em aplicações web,
mas que acabam por prejudica-lo quando o mesmo é implementado em aplicações
transacionais.
Dentre essas características de projeto do ZODB, sabe-se que o banco de dados
como um todo foi planejado para ter melhor performance quando submetido a
grandes volumes de leituras e poucas escritas. O mecanismo de tratamento
de conflitos de transações implementado pelo ZODB é dito otimista e baseado em
timestamps (em versões mais recentes adota “Multiversion Concurrency Control MVCC [1]”). Bancos de dados relacionais geralmente adotam decisões de projetos
voltadas para ambientes com alto nível de transações concorrentes e conflitantes. É
comum a utilização de tratamento de conflitos de transações pessimistas, através
de bloqueios. Essa diferença essencial de arquitetura claramente impacta na
performance das soluções escritas com cada tipo de banco de dados.
Conhecer as vantagens de cada arquitetura de banco de dados tem sua
importância. A performance do SQLStorage é ruim pois desconsidera essas questões
e é “agnóstico” em sua implementação. Em teoria, essa implementação poderia ser
melhorada adicionando-se uma camada de cache intermediário ao mapeador
objeto-relacional. No entanto, como o código do SQLStorage é bastante antigo e por
vezes incompleto, não suportando o mapeamento de diversas classes de objetos,
optou-se por atender aos requisitos descritos no edital desenvolvendo uma nova
camada de mapeamento objeto relacional para o Archetypes. Essa camada será
baseada em frameworks específicos de mapeamento objeto-relacional; frameworks
esses já existentes e comprovadamente de qualidade. Assim, ao invés de
desenvolver todo o mapeador objeto-relacional, esse trabalho estará focado na
integração de mapeadores conhecidos com a arquitetura do Archetypes.
4.Limitações
Antes de descrever a especificação do mapeador objeto-relacional é importante
ressaltar as limitações que a arquitetura Zope impõe a esse tipo de solução. Além
das implicações relacionadas com o mecanismo de controle de transações
concorrentes, há ainda 2 aspectos importantes a serem considerados.
O primeiro aspecto diz respeito a escalabilidade do Plone quando integrado a
bancos de dados relacionais, ou de forma mais ampla, o Zope integrado a serviços
remotos. O Plone é executado sobre o servidor de aplicações Zope e esse possui um
número fixo de threads responsáveis pelo atendimento de requisições. Aceita-se
que todas as aplicações desenvolvidas para o Zope e, por sua vez, para o Plone,
devem executar tão rápido quanto possível (cada requisição deve ser processada
2
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN
em frações de segundo). Isso é indicado não somente pela óbvia necessidade de
respostas imediatas por parte do usuário final, mas pelo fato de que requisições que
demoram muito para serem processadas mantém as threads do servidor Zope
alocadas. Se essa alocação for feita durante alguns segundos, bastariam 4 usuários
simultâneos para que tivéssemos a impressão de que o Plone “travou” durante
esses poucos segundos. Na verdade não existe realmente um travamento, mas sim
uma condição de corrida, onde o browser aguarda que o servidor web responda,
mas esse está com todos os seus recursos ocupados. Em nosso caso específico,
esses recursos seriam as threads do Zope esperando que o banco de dados
responda a uma consulta SQL.
Esse tipo de situação pode ser evitada mantendo-se o servidor de banco de dados
relacional do qual o Zope depende em um servidor rápido, com carga controlada e
acessível através de um conexão de alta velocidade (preferencialmente no mesmo
segmento de rede do servidor de aplicações Zope).
Outra limitação importante é que ainda é difícil persistir as informações
representadas pelo framework Archetypes apenas em banco de dados relacional.
Isso se deve a uma limitação do próprio Zope 2, onde as implementações internas
da camada da persistência, dos mecanismos de segurança e de herança de
conteúdo (conhecida como aquisição) impedem a clara separação do conteúdo e
das permissões a ele associadas, assim como dos demais conteúdos acessados
durante o “traverse” (acesso ao conteúdo no seu contexto de aquisição). Isso
significa que, persistido as informações contidas no “schema” de um objeto
Archetypes, essas informações podem ficar armazenadas apenas no banco de dados
relacional, mas ainda assim será necessário armazenar um objeto no ZODB. Esse
objeto será responsável por manter consistente os mecanismos de segurança e
“traverse” do Zope. Para mais informações sobre “acquisition” consulte [2].
Durante o processo de análise de requisitos desse projeto tentou-se investigar sobre
a possibilidade de contornar essa limitação e fazer o mapeamento objeto-relacional
utilizando apenas o banco relacional, sem a necessidade de persistir informações no
ZODB. Essa alternativa mostrou-se ainda incipiente pois, na melhor das hipóteses,
isso implicaria em modificações consideráveis no código fonte dos Produtos do
Interlegis (algo não desejável e explicitamente descrito no termo de referência onde
temos “ser compatível com códigos Archetypes legados”) ou na adoção de código
fonte muito instável.
5.Detalhamento do Produto
Segundo o termo de referência, temos como objetivo “tornar mais eficiente o
mecanismo de armazenamento do Archetypes permitindo considerável melhoria
na performance de transações de escrita e leitura no banco de dados”. Ainda
segundo o termo de referência, para isso deve-se “projetar e desenvolver uma
camada que faça o mapeamento de objetos do banco de dados ZODB, gerados pelo
framework Archetypes, para SGBDs relacionais tais como o PostgreSQL e MySQL,
utilizando ORMs como o SQLAlchemy ou SQLObject, fazendo com que os objetos
gerados possam, arbitrariamente, ser armazenados no ZODB ou em um SGBD
relacional ou em ambos”.
Como descrito nas considerações gerais, o SQLStorage, implementação padrão de
mapeamento objeto-relacional do Archetypes, sofre de um problema crítico de
performance. Nesse sentido optou-se pela adoção do ORM SQLAlchemy [3] para
mitigar esse problema. O SQLAlchemy implementa internamente um padrão de
projeto (“design pattern”) Unidade de Trabalho (“Unit of Work”). Na prática esse será
o grande diferencial quando comparado à implementação padrão do SQLStorage
pois as operações de escritas passarão a feitas em “batch”.
3
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN
No que diz respeito as operações SQL realizadas durante o mapeamento, a
implementação interna do SQLStorage realiza para cada operação de escrita
(resultado da invocação de qualquer “design pattern setter”) pelo menos 1 select e
1 update (ou 1 insert). Para uma classe Archetypes com 20 campos (“field”), seriam
executados pelo menos 40 consultas SQL (e na prática costumam ser mais do que
40, pois não existe otimização interna nas chamadas de métodos “getter e setter”).
Usando-se o SQLAlchemy a quantidade de operações será teoricamente diminuída
para apenas 1 ou 2 instruções SQL. O ganho de performance desejado será obtido
justamente na diminuição de operações SQL realizadas pois, para cada SQL
executado o Zope tem que enviar a consulta para o servidor de banco de dados via
rede e, mesmo que o servidor de banco de dados responda muito rapidamente, isso
certamente será mais lento do que processar apenas algumas operações. Um menor
número de operação SQL significa uma economia de tempo na latência de rede e na
alocação estática de threads do Zope.
O SQLAlchemy também atende ao requisito de permitir que esse mapeamento seja
feito para diversos bancos de dados relacionais diferentes. Segundo [4], o
SQLAlchemy suporta dialetos para o SQLite, Postgres, MySQL, Oracle, MS-SQL,
Firebird, MaxDB, MS Access, Sybase, Informix e DB2 (desde que exista um
driver que implemente a DB-API 2.0).
Ainda segundo o termo de referência, temos que a camada de mapeamento objetorelacional deverá ser compatível com todos os tipos de dados nativos do
Archetypes. A próxima seção especifica como serão feitos esses mapeamentos.
6.Especificação
O mapeamento dos diversos tipos de
implementado de acordo com a tabela 1.
Archetypes
dados nativos do Archetypes será
SQLAlchemy Observações
1
StringField
Text
Mapeamento direto.
2
TextField
Text
Mapeamento direto.
3
LinesField
Text
Mapeamento direto.
4
BooleanField
Boolean
Mapeamento direto.
5
DateTimeField
Datetime
Mapeamento direto.
6
IntegerField
Numeric
Mapeamento direto.
7
FixedPointField
Numeric
Mapeamento direto.
8
FloatField
Float
Mapeamento direto.
9
ComputedField
-
Campos do tipo ComputedField não precisam ser
mapeados pois não são persistidos. O valor de
um campo ComputedField é “computado” no
momento em que o método “getter” é acessado.
10
ReferenceField
-
Vide observação #1 e #2.
11
FileField
-
Vide observação #1 e #3.
12
ImageField
-
Vide observação #1 e #3.
13
CMFObjectField
-
“Deprecated”. Recomenda-se o uso de FileField.
14
PhotoField
-
“Deprecated”. Recomenda-se o uso de
ImageField.
15
ObjectField
-
ObjectField é uma classe base e não deve ser
4
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN
utilizada na descrição de “schemas”.
Tabela 1. Mapeamento de tipos
Observação 1: A implementação padrão de mapeamento objeto-relacional do
Archetypes (SQLStorage) não suporta o mapeamento de referências, arquivos
(FileField) e imagens (ImageField).
Observação 2: A ausência de suporte ao mapeamento de referências se deve a
uma modificação na implementação da API de referências realizada depois da
implementação do SQLStorage. Devido a essa alteração, os ReferenceField não
passam
mais
através
da
Interface
de
“storage”,
definida
em
Archetypes.Storage.StorageLayer. A implementação atual segue a API definida na
classe base Archetypes.Referenceable.Referenceable, que terá que sofrer “monkey
patches”. Todas as referências serão mapeadas para uma tabela auxiliar chamada
references que conterá 3 colunas: source_uid e target_uid, relationship (o
campo relationship pode ser nulo).
Observação 3: Não é conveniente persistir campos FileField e ImageField em um
banco de dados relacional. Campos FileField podem ser arquivos binários grandes e
servi-los através do Zope conectado a um banco de dados, apesar de possível seria
uma péssima escolha. O mesmo se aplica a campos ImageField, com o agravante
que esse tipo de campo armazena imagens em seu tamanho original e diversas
cópias da mesma em tamanhos diferentes. A alternativa a ser adotada para o
mapeamento desse tipo de informação será a adoção do FileSystemStorage [5].
Persistindo os arquivos e imagens no sistema de arquivos abre-se a possibilidade de
servir esses arquivos utilizando um servidor web padrão (solução mais simples e
rápida, especialmente para arquivos grandes).
O mapeamento objeto-relacional será feito criando uma tabela que descreva o
“schema” de cada classe Archetypes. Como regra geral, essa tabela terá tantos
campos quanto a classe original Archetypes tinha, de acordo com a Tabela 1, exceto
os campos que não precisam ser mapeados e os que tem mapeamento especial
(linhas 10 a 12 da Tabela 1). Nessa tabela ainda constarão pelo menos duas colunas
adicionais: uid e parent_uid, identificando o UID do objeto e o UID do objeto
“aq_parent”, respectivamente (a coluna uid poderá ser “unique”). Além disso, fica
convencionado a existência de 1 banco de dados diferente para cada instancia de
objeto Plone Site.
Por fim, para atender ao requisito definido no termo de referência como “permitir a
escolha arbitrária do armazenamento dos dados somente no ZODB, somente no
SGBD relacional ou em ambos”, fica convencionado que a configuração referente a
onde a persistência será realizada é feita através de um parâmetro de configuração
no código fonte do Plone Product responsável pelo mapeamento.
7.Infra-estrutura
A implementação a ser desenvolvida deverá ser compatível e devidamente testada
com as seguintes versões:

Python 2.4.4

Zope 2.9.8

Plone 2.5.5

Archetypes 1.4.6

MySQL 5.0 - Community Server - Generally Available (GA) Release
5
SENADO FEDERAL
Secretaria Especial do Interlegis - SINTER
Subsecretaria de Tecnologia da Informação - SSTIN

PostgreSQL 8.3
Acredita-se que a implementação terá compatibilidade com versões posteriores do
Plone e suas dependências (3.0.x e 3.1.x), mas essa compatibilidade só será
garantida para as versões listadas acima. Versões antigas do Plone (2.1.x e
anteriores) não serão suportadas.
8.Referências
[1] Multiversion Concurrency Control – MVCC
http://en.wikipedia.org/wiki/Multiversion_concurrency_control
[2] Zope Acquisition
http://www.zope.org/Documentation/Books/ZDG/current/Acquisition.stx
[3] SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
[4] SQLAlchemy – Key Features of SQLAlchemy
http://www.sqlalchemy.org/features.html
[5] File System Storage
http://ingeniweb.sourceforge.net/Products/FileSystemStorage
6
Download