Prevalência com Swap e/ou Passivation

Usando qualquer tipo de proxy resolve o problema de fazer lazy-loading do teu objeto. Agora tem outros problemas que também não são triviais: como descobrir quais objetos estão modificados? como lidar com concorrencia? como demaracar transações? Como lidamos com atualizações de schema?

Eu acho que sem muita manipulação de bytecode ou exigindo algum estupor como CMP exige, fica quase inviavel. Usando manipulação de bytecode, boas práticas como somente acessar atributos via getters/setters e acessar coleções pelas suas interfaces me parece viavel.

[quote=“louds”]

Eu acho que sem muita manipulação de bytecode ou exigindo algum estupor como CMP exige, fica quase inviavel. Usando manipulação de bytecode, boas práticas como somente acessar atributos via getters/setters e acessar coleções pelas suas interfaces me parece viavel.[/quote]

Tipo como alguns feitos por algumas ferramenta AOP???

Abraços

  1. bom, pode-se tratar toda chamada de metodo como uma modificacao… ja que a gente tah num proxy mesmo, fica facil descobrir isso (isDirty()! yeah! :?)

  2. hmmm… som de uma bigorna de 16 ton caindo na cabeca do cv

  3. o Nanning faz isso, e de um jeito bem legal alias :smiley:

  4. Ue, migracao de schema ja eh um inferno, e vai continuar sendo um inferno, onde ta o problema, se eh que vai ter mais algum sendo introduzido aqui? :smiley:

Bom, o meta-freak (eu) tem algo a dizer:

WeakReferences são referências que permanecem enquanto existir alguma referência forte pro mesmo alvo.

Ou seja, se eu tenho dois objetos, A e B, que tem referências prum P:

  class A {
    private P p;
    public P getP() { return p;}
  }

  class B {
    private WeakReference wrP;
    public P getP() { return (P) wr.get();}
  }

Posso fazer assim:

A a = new A();
B b = new B();
a.setP(new P());
b.setP(a.getP());
a.setP(null);
assert (b.getP() == null);

O que a gente tá querendo é uma SoftReference, que sobrevive caso o objeto não seja coletado. Ou seja, se o objeto passar pelo gc, e a única ref pra ele for uma SoftReference, ele morre.

Acho que um problema por si só interessante é lidar com a passivação (existe isso?) de objetos, independentemente de eu estar num sistema prevalente ou não.

Um Sistema Prevalente é um sistema que não pode cair. Que não vai cair. Dá pra quebrar assim: é só fazer um grafo de objetos grande demais pra caber na RAM, e um código que passeie comportadamente nele. O pobre do mecanismo de persistência vai ficar o resto da vida serializando proxy pra abrir espaço, deserializando pra acessar, e nunca retorna, e nunca dá exception!

Se a probabilidade de um objeto ser acessado é inversamente proporcional à idade dele, melhor nem passar pelo prevayler, bota em outro lugar! Nada te impede!

Volto a sugerir o Jisp pra isso.

[]s!!

Consegui algo descente !!! Parece que está funcionando bonito !!! Fiz um PhoneBookPassivation apenas herdando PhoneBook e trocando a Map.

Está totalmente transparente para o usuário !!!

Se vc quiser uma Map que faça por trás dos panos a passivação/ativação dos seus objetos para economizar RAM, tudo que vc tem que fazer é utilizar uma org.space4j.passivation.PassivationMap !!!

Um thread no background da PassivationMap fica checando de tempos em tempos o lastAccessTime dos objetos e se o objeto não é acessado a bastante tempo vai para o disco.

O programador não precisa fazer absolutamente nada, apenas colocar os seus POJOs nessa Map com put e acessá-los com get.

Os gets da PassivationMap são inteligentes para retornar o objeto desejado da memória ou do disco se estiver passivado.

Vc pode até snapshotear essa Map com os objetos Passivados que não tem problema.

Uma dúvida que estava martelando a minha cabeça é, como alterar um objeto passivado através de um comando ??? Mas se vc pensar bem, de que adianta vc alterar esse objeto se vc não colocar ele no seu Map ? Então o seu comando vai ter que dar um get ou um put no PassivationMap, o que vai gerar uma ativação do objeto e o problema está resolvido.

Comandos que não dão get na Map poderiam trazer problemas. Mas ainda não consigo imaginar como eu usaria um comando desses.

Acho que eu estou pensando um pouco database-oriented, como seu eu estivesse fazendo um update no Map (tabela). Mais ou menos assim: se eu quero alterar o usuário sérgio eu vou no map pego ele (get!) e altero.

Em breve coloco o código aqui para ouvir as críticas e sugestões de vcs.

Abraço,

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

Voce ta assumindo que NENHUM objeto no sistema alem do teu map mantem referencias pros Objetos dele. certo? Senão isso tem utilidade quaze zero.

ex:

class Opss implements Serializable {
    Object obj;
     public Opss(Map source) {
         obj = source.get("yahoo!");
     }
}

Agora oque acontece se o teu PassivationMap mandar pro disco o objeto referenciado pela classe acima???

Entendi, mas isso talvez não seja tão ruim assim !!!

Tecnicamente não há problema. Conceitualmente eu passivei a toa o objeto, pois ele vai continuar em memória.

Se as referencias eventualmente sumirem o GC vai terminar o serviço.

Note tb que isso não será muito comum e vai depender do PASSIVATION_TIME, ou seja, do intervalo de tempo que vc vai utilizar para passivar os objetos. Se alguém está com uma referência pra esse objeto, as chances são que ele foi acessado recentemente e não estará elegível a passivação !!!

Faz sentido?

Perai, Sergio… como “eh soh colocar no map”? Se voce esta colocando o objeto no map, mas mantem outras referencias a ele, ele nao vai ser tirado da memoria nunca - ele pode ateh ir pro disco, mas isso vai ser enfeite, afinal, ele ainda ta na memoria, e o GC nao removeu ele so pq vc colocou ele num map espertinho… como vc resolveu isso? :smiley:

O louds já tinha levantado essa bola !!! Veja se o que eu respondi mais acima no forum faz sentido?

Abraço,

Sergio

[quote=“saoj”]Se alguém está com uma referência pra esse objeto, as chances são que ele foi acessado recentemente e não estará elegível a passivação!

Faz sentido?[/quote]

Hmmm… nao muito… num sistema prevalente, os objetos vivem muito mais - logo, dizer que “se o GC nao conseguiu remover, é pq tem alguém usando” é incorreto :wink:

[quote=“cv”][quote=“saoj”]Se alguém está com uma referência pra esse objeto, as chances são que ele foi acessado recentemente e não estará elegível a passivação!

Faz sentido?[/quote]

Hmmm… nao muito… num sistema prevalente, os objetos vivem muito mais - logo, dizer que “se o GC nao conseguiu remover, é pq tem alguém usando” é incorreto ;)[/quote]

Hummmm. Acho que vcs podem ter razão. Mas deixa eu insistir só mais um pouquinho. :o Os objetos vivem muito mais em memória, mas isso não quer dizer que eles são acessados muito mais que em qualquer outro sistema.

Um problema que eu acabei de notar foi o seguinte: (f…)

Se um objeto User contem um objeto Account e vc passiva o User, o account vai junto. Aí quando depois vc ativar o User de novo, o Account pode estar inconsistente !!! (f…)

Não é muito natural alguem alterar o Account de um User por fora do User, mas pode acontecer. Para esses casos teríamos que colocar o objeto Account como transient e carregá-lo de novo quando o user for ativado.

Acho que o meu esquema pode funcionar, mas:

  1. Estou começando a colocar a responsabilidade de mais encima do programador. Se ele faz um design mal feito, o sistema de passivação vai se ferrar ou ser inútil.

  2. Estou tentando fazer uma analogia entre Maps e Tabelas de um banco de dados, onde vc faz update, insert e delete. Isso tem prós e contras que ainda não estão claros para mim.

Vou pensar mais sobre isso… Valeu pessoal !!!

Quanto a passivar objetos atoa, não é tão grave quanto a criar 2 copias do mesmo objeto no sistema, esse é o problema que eu apontei…

Entendi !!! Caiu a ficha !!! Veja o meu post mais acima…

Abraço

Peraí… se toda alteração no objeto persitido deve ser feita pelos COmmands, e eu tenho uma referência à objA, ele só pode ser lido por mim, correto? Quando eu alterar, passo prum Command, este command vai implicitamente despassivar o treco e substituir pelo objeto que eu passei, podendo retornar esta nova referencia.

Tipo:

1)a -> objeto em lugar X

2)coloque o objeto no lugar X em disco

3)como o lugar X é apotnado por A, não é coletado

4)a lê x na boa

5)a modifica x e passa para um command de update

6)o command recebe o x, traz o passivado do disco, coloca num lugar y

7)comand faz as alterações em y, ele fica igual ao objeto em x

8)command retorna y para o objeto a que o havia chamado [esse passo eu que inventei]

9)eventuais outros objetos que estivessem apontando para X se ferram com uma referência desatualizada

Falei alguma besteira grande ou foram só besteiras médias?

Todos os POJOs que contenham outros POJOs teriam que declará-los como transient para evitar esse problema. Quando o objeto voltar do disco ele pega a versão mais atualizada do sistema.

Assim fica foda mesmo !!! Nesses casos minha solução vai dançar… hehehe

A não ser que eu exija que o programador seja esperto para fazer um design sem furos… :cry:

[quote=“saoj”]Todos os POJOs que contenham outros POJOs teriam que declará-los como transient para evitar esse problema. Quando o objeto voltar do disco ele pega a versão mais atualizada do sistema.
[/quote]

Ou seja, estamos mandando pro vinagre a OO obrigando todos objetos possuirem alguma forma de OID para os objetos que eles referem.

…nesses casos que sao 99% das aplicacoes, por sinal :smiley:

[quote=“louds”]
Ou seja, estamos mandando pro vinagre a OO obrigando todos objetos possuirem alguma forma de OID para os objetos que eles referem.[/quote]

Eu tinha resolvido o problema do OID assim:

package org.space4j.passivation;

import java.io.*;

public class PassivatedObj implements Serializable {
    
    private String full_class_name = null;
    private String unique_id = null;
    private String extra_path = null;
    
    public PassivatedObj(Object obj) {
        this.full_class_name = obj.getClass().getName();
        this.unique_id = String.valueOf(obj.hashCode());
        this.extra_path = getExtraPath(unique_id);
    }
    
    public String getFullClassName() {
        return full_class_name;
    }
    
    public String getUniqueIdentifier() {
        return unique_id;
    }
    
    public String getExtraPath() {
        return extra_path;
    }
    
    private String getExtraPath(String s) {
        int x = s.length();
        return s.substring(x - 2, x);
    }
    
}

Quando eu passivava um objeto, eu tirava ele da memória e colocava um PassivatedObj no lugar dele. Pegava um hashCode e utilizava como OID. (Eu sei que o hashCode pode retornar o mesmo valor para dois objetos diferentes, mas o sistema está se protegendo disso. Se isso acontecer ele não passiva o Objeto com hashCode igual.)

Está funcionando bonitinho !!! Se não fosse pelo problema do objeto que contem outro objeto… :cry: :cry: :cry: :cry: :cry:

Voltamos então para a estaca zero. Tenho um site com 3 milhões de cadastros. Não dá para colocar tudo isso em memória. Desses 3 milhões, 200 mil são ativos. Não há porque não passivar os outros 2 milhões e 800 mil usuários em disco. Ou então esqueço prevalência.

Nesse caso acho que usando o PassivationMap com cuidado dá para solucionar o problema. Talvez os proxies sejam a solução mais segura mesmo. Vou dar uma estudada nesses proxies com weak references.

Abraço a todos !!!

SoftReferences :wink: