quinta-feira, 27 de agosto de 2009

RelStorage: ZODB usando backend relacional (SGDBs)

Introdução

O ZODB (banco de dados orientado à objetos para aplicalções Python), originalmente criado como um componente do servidor de Aplicação Zope2 (Z Object Publishing Environment), foi desenvolvido com o conceito de "Storage Layer", o qual abstrai o tipo de backend responsável pela persistência dos objetos.

Historicamente, o primeiro Storage Layer desenvolvido para o ZODB foi FileStorage, que tem como objetivo ser simples e robusto, e por isso armazena todos os objetos e transações um único arquivo de forma sequencial. Para acessar os objetos através do seu caminho na hieraquia de objetos do ZODB (ex: obj2 = root['obj1']['obj2'] ), o FileStorage cria um arquivo auxiliar de índice e que deve estar completamente na memória para que o desempenho seja adequado.

Compartilhando o ZODB

Mais tarde, devido a necessidade de compartilhar um mesmo banco de dados por diversas instâncias de uma aplicação (ou diversas aplicações), foi desenvolvido o ZEO (Zope Enterprise Objetcts), que é composto por um servidor RPC que fornece acesso via rede a um ou mais bancos de dados ZODB em FileStorage.

Segue abaixo um de acesso ao ZODB com ZEO e ClientStorage retirado do ZODB Guide:
from ZEO import ClientStorage
from ZODB import DB
import transaction

# modificar essa linha de acordo com a configuração do servidor ZEO
addr = 'zeoserver.example.com', 1975
storage = ClientStorage.ClientStorage(addr)
db = DB(storage)
conn = db.open()
root = conn.root()

# vamos armazenar uma lista e um dicionário no objeto raiz
root['lista'] = ['a', 'b', 1.0, 3]
root['dicionario'] = {'a':1, 'b':4}

# o ZODB é um banco ACID e transacional, é preciso fazer o commit
# no caso do ZOPE, esse commit é automático caso exista uma exceção não tratada
transaction.commit()

Dessa forma, cada instância da aplicação cliente acessa o ZEO usando o Storage Layer ClientStorage para se concectar ao banco de dados compartilhado pelo servidor ZEO. Para o tratamento de concorrência, o ZEO / ZODB utiliza um mecanismo "otimista" de controle de conflitos entre transações que acessam os mesmo objetos, se um conflito ocorrer, uma exceção do tipo "ConflictError" é levantada para avisar o problema a aplicação cliente.

O conjunto de ZEO + FileStorage e aplicação cliente (Zope por exemplo) + ClientStorage permite que os mesmos arquivos de dados ZODB no formato FileStorage sejam acessados simultanemente para criação de Clusters, muito usados para aumentar o desempenho de sites de alto tráfego que usam Zope/Plone, por exemplo.

Essa combinação, apesar de ser a única alternativa para "compartilhar" o ZODB por várias instâncias de uma aplicação, possui algumas limitações de escalabilidade e redundância:
  • O ZEO é uma aplicação Python e devido ao GIL (Global Lock Interpreter) ele somente consegue ser escalonado em uma CPU por vez e, por isso, não aproveita melhor os sistemas com vários processadores/núcleos. Essa limitação prejudica a escalabilidade do backend e pode ser percebida quando muitas instâncias da aplicação precisam acessar ou gravar dados ao mesmo tempo.

  • A Zope Corp possui um componente proprietário, o Zope Replication Services, que é vendido separadamente para replicação de bases de dados ZODB + FileStorage, possibilitando replicar em tempo real o servidor ZEO, conseguindo então uma solução de alta disponibilidade, porém o custo é alto e proibitivo para a maioria das organizações e situações de uso do ZODB.

  • Quanto mais objetos no ZODB, maior o arquivo de índice do FileStorage e, em alguns casos de grandes bases de dados, a inicialização pode ser bem demorada, principalmente se for necessário recriar esse índice.
Surge o RelStorage

Foi com objetivo de atacar essas deficiências e seguindo a idéia do BerkeleyStorage, que foi desenvolvido RelStorage (inicialmente chamado de PGStorage) o qual suporta o aramazenamento dos objetos opacos do ZODB (no formato de byte-code python), em bases de dados relacionais.

Isso significa que esses objetos não podem ser consultados diretamente através do banco de dados relacional (ex: realizando consultas para reltórios), mas apenas pelo ZODB (aplicações Python), o qual ao ser configurado com RelStorage, sabe como recuperar e gravar os objetos, de forma transparente, que estão persistidos em um backend relacional e que substitui o ZEO + FileStorage na tarefa de compartilhar o ZODB por diversas instâncias da mesma aplicação ou diversas aplicações.

Atualmente existem "adapters" no RelStorage para armazenar bases de dados ZODB em três diferentes backends relacionais: MySQL, PostgreSQL e Oracle. Novas servidores de banco de dados podem ser integrados atrvés da implementação de um novo adapter.

Segue um exemplo simples para configuração de um banco de dados ZODB com RelStorage e usando o PostgreSQLAdapter, lembrando que é preciso primeiro criar o banco de dados no PostgreSQL e o RelStorage inicializa as tabelas no primeiro acesso:


import transaction
from ZODB.DB import DB

from relstorage.relstorage import RelStorage
from relstorage.adapters.postgresql import PostgreSQLAdapter

# alterar a configuração de acordo com o banco de dados no PostgreSQL
dsn = "dbname='ruda' user='ruda' host='localhost' password='12345'"

adapter = PostgreSQLAdapter(dsn)
storage = RelStorage(adapter)
db = DB(storage)
conn = db.open()
root = conn.root()
# vamos armazenar uma lista e um dicionário no objeto raiz
root['lista'] = ['a', 'b', 1.0, 3]
root['dicionario'] = {'a':1, 'b':4}
transaction.commit()

Desempenho e Alta Disponibilidade

Os testes realizados por Shane Hataway (criados do RelStorage) indicam que o RelStorage possui um desempenho superiror ao ZEO tanto na leitura quanto na gravação, e que esse ganho aumenta quanto maior concorrência de acesso ao ZODB. É importante notar que é possível fazer um tunning no ZEO que melhora seu desempenho, porém o problema de escalabilidade decorrente do GIL, tente a tornar o ZEO mais lento em ambientes de alta concorrêcia, como por exemplo sites Zope/Plone que possuem muitas instâncias Zope.

Além disso, devido ao uso de bases de dados relacionais, as bases de dados ZODB armazenadas com RelStorage podem ser replicadas usando as ferramentas disponíveis em cada SGDB. Por esse motivo, o RelStorage passa a ser uma alternativa interessante para criação de ambientes de alta disponibilidade que usam o ZODB, novamente, grandes portais Zope/Plone são um público em potecial.

Estabilidade, Migração e Integração

O RelStorage pode ser considerado estável e já está sendo usado em produção por diversos sites, incluindo um grande portal Zope/Plone que usa RelStorage com Oracle e foi um dos patrocinadores da implementação desse novo Storage para ZODB.

Além disso, RelStorage possui uma excelente cobertura de testes e inclui em sua distribuição uma ferramenta (zodbconvert.py) para migração de bases de dado do formato FileStorage para RelStorage (MySQLAtapter, PostgresSQLAdapter e OracleAdapter) e vice-versa.

As opções do arquivo de configuração para migração usando zodbconvert.py são semlhantes as opções usadas na configuração de cada adapter do RelStorage (cada um possui opções distintas). No caso do PostgreSQL a configuração (migration.conf) seria:

<filestorage>
path /tmp/Data.fs
</filestorage>

<relstorage>
<postgresql>
dsn dbname='ruda' user='ruda' host='localhost' password='12345'
</postgresql>
</relstorage>


Para realizar a migração, em um ambiente Zope/Plone configurado via Buildout, basta executar os seguintes comandos no shell, assumindo a versão 1.2.0b2 do RelStorage:

$ cd meu_diretorio_buildout
$ ./bin/zopepy ./eggs/RelStorage-1.2.0b2-py2.4.egg/relstorage/zodbconvert.py /tmp/migration.conf
Finalmente, já existe suporte no pacote plone.recipe.zope2instance para que o RelStorage seja configurado com Buildout para automatização do deploy de aplicações Zope/Plone. Porém, para uso com Plone 2.5 (Zope 2.9) e Plone 3.x (Zope 2.10), é necessário incluir o repositório com versões modificadas do ZODB 3.7 e 3.8 para suportar o RelStorage. Já no Plone 4.x (Zope 2.12) que virá com o ZODB 3.9, não será preciso usar a versão com patch pois o mesmo já foi aplicado no repositório oficial da nova versão do ZODB.

Conclusão

Com RelStorage podemos criar ambientes que necessitam de maior desempenho e disponibildiade do ZODB, substituindo o ZEO e consequentemente algumas de suas limitações. Grandes portais desenvolvidos em Plone podem aproveitar esses recursos, principalmente em ambientes nos quais são usados clusters Zope e que tem o requisito de maior alto desempenho e redundância.

Embora ainda não exista informações divulgadas sobre sites que usam o RelStorage, incluindo detalhes sobre ganhos de desempenho e escalabilddade que demostrem claramente as vantagens da sua adoção, dependendo do ambiente a possibilidade de replicação on-line já seria um motivo suficiente para justificar a migração para o RelStorage.

Nenhum comentário: