Prevalência com Swap e/ou Passivation

Algumas pessoas tem levantado a bola que o Space4J/Prevayler deveriam ter um esquema de Swap para poder comportar mais dados que o limite da RAM.

Uma idéia seria monitorar objetos que estão a muito tempo sem ser acessados e dar um passivate neles, ou seja, tirar eles da memória e colocá-los em disco. Como EJB faz !!!

Será que vale a pena ir por aí ???

Um abraço,

Sergio Oliveira

Nao sei se vale a pena, por dois motivos:

  • Voce teria que colocar dynamic proxies pra todos os objetos no seu sistema, e os proxies em si podem ser as vezes mais gordos do que os proprios objetos, sem contar na memoria a mais que vc iria gastar mantendo uma LRU (least-recently-used) pra todos os objetos.

  • Quando “despassivar” um objeto? A resposta mais obvia (“quando ele for usado de novo, oras!”) pode trazer uma degradação de performance imensa (pense no que aconteceria ao iterar sobre uma lista de objetos grande, e ter que deserializar objeto por objeto… nada bom, nada bom).

Outro ponto é a inserção dos proxies, em si… haja factory! :smiley:

eu acho que se fosse implementada uma solução deste tipo, ai sim, seriam soluções perfeitas para persistir dados,
o problema para isto, é que eles não tem nenhuma relação com a maneira pela qual os dados são acessados, assim fica dificil de fazer um Lazy loading para carregar os dados depois, para tirar eles da memoria seria bem fácil, é só utilizar Week Reference (se não me engano é assim que se escreve, depois procuro no amança burro :slight_smile:

O problema não está em usar Weak References, urubatan… mas sim em onde colocar essas weakrefs, e como fazer o lazy loading direitinho…

Uma ideia que me veio à cabeça agora é ter um dynamic proxy realmente inteligente, que entende as chamadas Load Policies… ou seja, voce pode especificar pra ele grupos de objetos que devem ser carregados de uma vez soh…

…mas aí vc está implementando um bicho beeeeeeem grande, e bem complexo :? - nada contra, mas já fica avisado desde já que o esforço não é nada trivial! :smiley:

é verdade, não tinha pensado nisto, onde colocar eles,
da maneira como eu tinha pensado de inicio, seria implementado isto dentro do PrevalentSystem do usuário (acho que é este o nome da classe, não mexi muito com o prevayler ainda :slight_smile:

agora pior que isto, imagina só se o cara resolve utilizar por exemplo Ognl para pesquisas na arvore de objetos, como fazer o lazzy loading para isto, ja que o Ognl vai varrer todos provavelmente??

O esforço para implementar isso de forma eficiente me parece 10x maior que implementar 1 container EJB 2.1.

Uma forma de solucionar esse problema é se for feita uma separação dos dados do business logic. Algo semelhante ao CMP 2.0, mas sem todo o clutter e engeçamento com EJB.

Algo como vc definir suas classes como sem abstratas e todo acesso aos dados ser feito via getters/mutators.

Dai fica muito mais simples implementar politicas de passivation, lazy loading, smart collections, object handles, etc. Se bem que object handles é possivel implementar legal com 1 pouco de hacking em subclasses dos ObjectStreams.

Bom, já vi que a coisa é complexa ao extremo, mas tentando ser objetivo e simplista:

Não sei se estou viajando, mas minha idéia seria mais ou menos assim: Cada objeto poderia ser colocado num estado passivated de forma que ele só possuiria a sua chave primária e nenhum dado a mais. Então vamos supor que a gente tivesse um Map com 2 milhões de objetos User. Alguns desses Users poderiam estar passivated e isso seria transparente para qualquer busca, pois a nossa Map estaria completinha, ou seja, com os 2 milhoes de Users. Agora se alguém quiser fazer um fetch no usuário Sergio e esse usuário estiver passivated, ele vai ter que esperar a deserialização do disco.

Os LRU tomariam realmente mais espaço, mas talvez somando tudo sairíamos em vantagem, pois numa tabela com 2 milhões de usuários, quantos estão realmente sendo acessados ???

Não entendi o seu conceito de Proxy, poderia explicar melhor? Esse proxy seria a versão passivated do objeto ???

A versão passivated dos objetos resolveria o problema das iterações.

Kct, eu postei uma resposta na riojug de manhã e ainda não chegou :x
Vamos ver se consigo lembrar o que estava escrito…

Meu ponto de vista é o seguinte:

  1. é necessário
  2. é complexo pra kct, até alguém colocar um ovo em pé :idea: e deixar a gente com cara de babaca :roll:
  3. A performance [falando especificamente da mensagem do Elizário, an RioJug] não iria cair se só fosse utilizado quando houvesse risco de estouro de memória

Considerando que precisamos manter a estrutura de POJOs:

  1. como colocar um objeto em memória auxiliar sem saber quanta gente está “apontando” para ele?
  2. Ainda que saibamos, como colocar um proxy no local, falando especificamente em gerência de memória, ou como substituir todas as referências, que podem ser privadas?
  3. Ainda que consigamos colocar um proxy, este proxy estende o objeto serializado [afinal, deveria prover uma interface igual aos clientes do serializado], se sim, e se tivermos uma classe final?
  4. como saber se um objeto foi lido recentemente ou não? Poderíamos ter um objeto quase imutável, talvez que forneça uns parâmetros tipo “índice da taxa de juros COPOM”, que são modificados uma vez a cada mês/ano/década/nunca, mas que são cosntantemente acessados e precisam estar disponíveis. Limitar o acesso via algum wrapper ou através de DAOs, Commands, transactions, whatever iria limitar e muito nossas possibildiade sno uso de Prevalência

Situação em que seria necessário um passivate, siga os passos:

  1. Você tem um site.
  2. Este site registra um pequeno objeto com o IP e hora de cada visita.
  3. Você usa prevalência.
  4. Seu site é bom rpa cacete e aparece no slashdot.
  5. Sua JVM morre

Se a droga do mail chegar eu colo aqui, mas creio que era basicamente isso…

Não poderíamos ignorar isso? Se eu dei um passivate num objeto e tem um cliente com referencia pra ele, azar. O passivate não vai liberar nenhuma memória, e o cliente pode continuar usando o objeto normalmente. As chances disso acontecer seriam baixas, pois se há clientes com referencia para o objeto, isso significa que ele foi acessado recentemente e provavelmente não estará elegível a passivação.

Minha idéia não é colocar um proxy, mas uma versão passivated do objeto que vai possuir apenas a chave primária e mais nenhum campo.

Não escreva nenhum objeto final, do mesmo jeito que vc não pode escrever nenhum objeto não serialized.

Teríamos que ter um timestamp lastAccessTime

Essa situação é muito interessante !!! E acho que a única solução é:
Para coisas que crescem indefinidamente, como um log de acesso, vc precisa rotacionar a coisa. Não tem jeito !!! Esse log precisa ter um tamanho máximo !!! Esse problema tb acontece com um banco de dados relacional e com o log do Apache.

Nao vou me dar ao trabalho de quotar as referencias da conversa. Sim, eu sou um preguicoso maldito. Xinguem! :smiley:

Bom, seguinte… sobre a historia de “passivacao”… a gente ja pode comecar abolindo essa palavra, ja que ela traz muitas memorias ruins do pessoal que mexe(ia) com EJB :smiley:

A sugestao do Sergio de colocar uma versao do objeto que so tem chave primaria… bom, pode parar por ai… onde ja se viu objeto com chave primaria? :cry: Se voce vai obrigar o seu sistema prevalente a ter IDs nos objetos, pode ter certeza que o purismo OO foi por agua abaixo :smiley:

Pra trabalhar sem o uso de chaves primarias, e pra fazer o lance da “passivacao” da forma mais simples possivel, é só usar um dynamic proxy que contém uma weak reference pro objeto. Se a JVM precisar coletar a memoria da weakref, beleza… a referencia que o proxy tem vai, de repente, ficar null - e ele precisar do objeto de novo, é “só” buscar no disco. Só entre aspas, pq, como eu mencionei acima, fazer isso de forma performática eh bem chato.

PS: notem que eu estou falando de dynamic proxies. Podem esquecer essa historia de criar classes abstratas, ou de limitar classes final. java.lang.reflect.Proxy na cabeca, meu povo :smiley: - e, se vcs estiverem “moderninhos” hoje, a gente pode comecar a falar de CGLIB :smiley:

Sobre o exemplo do Phillip: concordo que os logs teriam que ser rotacionados, mas vamos imaginar que por algum motivo a gente nao pode (afinal, ta cheio de sistema por ai onde nao da pra rotacionar dados, e ponto). :wink:

[quote]Não poderíamos ignorar isso? Se eu dei um passivate num objeto e tem um cliente com referencia pra ele, azar. O passivate não vai liberar nenhuma memória, e o cliente pode continuar usando o objeto normalmente. As chances disso acontecer seriam baixas, pois se há clientes com referencia para o objeto, isso significa que ele foi acessado recentemente e provavelmente não estará elegível a passivação.
[/quote]

E como eu saberia que a classe foi acessada? E a liberdade do OO? [mais sobre isso abaixo]

Bom, o cv falou sobre isso, então vou me ater a dizer que isto me lembra este post aqui: http://www.jroller.com/comments/pcalcado/Weblog/can_prevalence_resist_bad_design

Uma vez eu li em algum lugar que utilizar qualquer coisa final era ruim. eu concordei e me perguntei porque raios existe a droga do final, se é só rpa constantes, sei lá. Aí eu li o Effective Java e percebi que realmente tudo tem seu lugar. Não vou comentar o livro aqui, a Bani tem um ótimo resuminho no blog dela, ams recomendo que realmente leiam, muito bom, mesmo. O que importa é que: sim, existem necessidades de se implemetar algo final, principalmente num projeto bem feito. Talvez objetos final possam ter que ser serializados “na mão”, mas algum jeito tem que ter!

I see dead POJOs.

[quote]
Essa situação é muito interessante !!! E acho que a única solução é:
Para coisas que crescem indefinidamente, como um log de acesso, vc precisa rotacionar a coisa. Não tem jeito !!! Esse log precisa ter um tamanho máximo !!! Esse problema tb acontece com um banco de dados relacional e com o log do Apache.[/quote]

Eu pensei neste exemplo a primeira vez há algum tempo, quando o wiki do Prevayler caiu. É só um exemplo, mas pense: você tem um site com mil acessos dia, você limpa sua “base de dados” de acessos todos os dias [tipo os referres do JRoller], aidna que a taxa de acessos triplique, seus vários GB de memória iriam aguantera fácil…só que você não pdoe prever a demanda de um dia assim. Ok, pdoe não acontecer nunca, mas se estamsof alando de alta disponibildiade e missão crítica, temos que pensar neste tipo de tragédia.

cv, vou dar uma olhada na Proxy, depois comento teu post.

E agora, com licença que minha linha é discada [não riam muito alto, por favor!] se tiver alguém acordado para comentar isso, talvez eu ainda leia.

[]s, galera

É longe de ser simples assim. Vale lembrar que vcs vão estar Serializando Objetos lotados de DP e References, isso é 1 dor de cabeça para fazer direito.

A chave para isso ser eficiente é serializar somente o POJO e os seus VOs, referencias para outros POJO’s persistences devem ser salvas da mesma forma.

Outra coisa que vcs esquecem é que usando DP ou CGLIB sempre existe o problema de reference leaking.

Tudo bem! Chave primária é coisa de banco de dados mesmo, mas não acredito ser esse um motivo suficiente para descartarmos a idéia. A tal da chave primária seria o ID único do objeto. Se estamos falando de usuários, cada um terá que ter obrigatoriamente um identificador único, que em banco de dados eles chamam de chave primária. A idéia é quando encontrarmos um objeto passivado (ou swapado, ou removido, ou desativado) e tivermos a necessidade de acessá-lo, teremos que trazer ele de volta do disco, e uma chave primária (desculpe, um identificador único) vai ser bastante útil pra isso.

Dei uma olhada nos Proxies, e apesar de nunca te-los utilizado na prática, entendi que eles retornam um novo objeto com a mesma interface do objeto proxiado. Se vc puder explicar qual seria a vantagem de um proxy+weak references aqui seria legal. Eu continuo achando que a idéia mais simples, apesar de provavelmente não a mais performática, seria carregar o objeto do disco no momento que alguém tentar acessá-lo.

Como vc controlaria os objetos que estão sendo passivados via Weak Reference? Não seria uma decisão do GC? Não seria melhor e bem mais simples controlar isso na mão com lastAccessTime ?

Estou apenas humildemente trocando idéias. Se aparecer um motivo sólido não vejo problema em abandonar as idéias acima.

Um abraço,

Sergio Oliveira
http://www.smartjava.com.br/
http://space4j.dev.java.net/

Hmm… bateu uma duvida agora: como vc imagina ser notificado de que o sistema esta querendo acessar um objeto qualquer?

A minha ideia de usar proxies é exatamente essa: voce pode fazer esse tratamento no proxy, e o proxy delega todo e qualquer acesso ao objeto “real”, entao ele tem controle sobre essas notificacoes. :smiley:

Vixi
Conversa de gente grande!
Vou meter meu humilde dedo no meio…
cv, esse proxy delega as chamadas ao objeto real… mas caso este objeto esteja serializado , o q acontece??? Como ele traz o objeto do disco? Onde ele procura???

Abraços

[quote=“cv”]
Hmm… bateu uma duvida agora: como vc imagina ser notificado de que o sistema esta querendo acessar um objeto qualquer?[/quote]

Este é o ponto. Não tem como, ao meu ver, sem substituir o conteúdo em memória por um proxy [falando, por enquanto do design pattern, não necessariamente de reflection] ou sacrificar a simplicidade obrigando que o objeto persistido implemente ou estenda algo :frowning:

O papo está ótimo. Vejam o excelente email do Phillip e meus comentários:

Fala Phillip !!!

Concordo com muitas coisas que vc falou abaixo, principalmente com o fim da liberdade de acessar os objetos diretamente.

Estou tentando ser simplista, pois já vi que o assunto abre margem para muitas complexidades, e não tenho ambições de fazer um JBoss de uma hora pra outra. :slight_smile:

Não é difícil imaginar um site com 3 milhões de cadastros. Para cada cadastro, vc tem um objeto User. Dependendo da quantidade de informações dos seus usuários, 3 milhões de objetos User já serão suficiente para estourar sua RAM.

O problema da liberdade a meu ver é:

Vc tem um Map users, e quando vc faz um users.get(new Integer(user_id)), vc pode receber um objeto passivado, ou seja, um objeto que não está em memória. Aí fudeu, o seu cliente vai ter que fazer algo do tipo:

if (myuser.isPassivated()) { ou if (myuser instanceof UserPassivated) {

myuser = getFromDisk(user_id);

}

Obrigar o cliente a fazer isso seria forçar demais a barra.

MAS…

Uma solução simplista não poderia ser implementar um Map com essa funcionalidade por trás de forma que quando vc dá um get vc vai receber o objeto de uma maneira ou de outra ???

Apenas idéias soltas. Aguardo comentários !!!

[]'s

Sergio Oliveira

Vc tah querendo colocar isso por exemplo, na implementacao do get do Map “implementado por trás”???

Sei lá, é como o pcalcado disse… o negocio de instanceof UserPassivated nao me agrada…

Kra, comecou a me dar umas viagens aki agora envolvendo AOP…
:twisted:
Vou tentar formular umas idéias (doidas) e posto aki…

Abraços

Teremos que pagar algum preço em prol da simplicidade. O desafio seria codificar uma coisa o menos dolorosa possível para quem depois vai estar escrevendo os POJOs. Se eu começar a explicar tudo isso para o pobre programador escrevendo os POJOs, o cara vai debandar na hora. :slight_smile:

Teria que ser algo mais ou menos assim:

“Se quer que seus POJOs sejam passivados, coloque eles nesse Map aqui: PassivationMap” e crie um objeto para representar o identificador único do seu POJO. E esqueça isso…" :slight_smile:

Mais ou menos como o cluster do Space4J. :slight_smile:

[]'s

Sergio

Acho mais facil exemplificar com codigo duma vez…

[code]public class MeuProxyFeliz implements InvocationHandler {

private static long lastGlobalID = 0;

private final WeakReference weakRef;

private final long id;

public MeuProxyFeliz(Object realObject) {
    this.weakRef = new WeakReference(realObject);
    this.id = lastGlobalID++;
}

/**
 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
 *      java.lang.reflect.Method, java.lang.Object[])
 */
public Object invoke(Object ignored, Method method, Object[] args) throws Throwable {
    Object proxy = weakRef.get();
    if (null == proxy) {
        // deserializa o bicho!
        
    } else if (descobreSeEuPrecisoSalvarEsteObjeto(id)) {
        // serializa o objeto!
    }

    return method.invoke(proxy, args);
}

}[/code]