4 Replies Latest reply on Sep 24, 2009 4:49 AM by chtimi2

    How to force eager loading? (not CacheLoader)

      I think i'm having a lazy-loading issue:

      I have to JVMs:
      -one with JBossPOJOCache (JBPC henceforth) in local mode (not replicated). On this JVM i have a class TracksTableImpl that implements its service interface by acting on an instrumented JBPC pojo TracksTableJbpcDelegate
      -one with a unit test that updates the remote state via a service call (remoting doesn't use JBPC), and then checks that the state is correct in case of a commit then a rollback

      Server-side service:

      public class TracksTableImpl implements TracksTable, TracksTableServices
      {
       private TracksTableJbpcDelegate delegate = new TracksTableJbpcDelegate ();
      
       @Override
       @Transactional
       public void updateXY ( double x , double y , boolean failOnUpdateY )
       {
       delegate.updateX ( x );
       if ( failOnUpdateY ) throw new TrackException ();
       delegate.updateY ( y );
       }
      
       @Override
       @Transactional
       public Position getPosition()
       {
       return delegate.getPosition();
       }
      }
      


      Server-side, the JBPC POJO:
      @org.jboss.cache.pojo.annotation.Replicable
      public class TracksTableJbpcDelegate
      {
       private List<ReadWriteTrack> tracks = new ArrayList<ReadWriteTrack> ();
      
       public void updateX ( double x )
       {
       ReadWriteKinematics k = getOrCreateReadWriteKinematics ();
       k.getReadWritePosition().setX ( x );
       }
      
       public void updateY ( double y )
       {
       ReadWriteKinematics k = getOrCreateReadWriteKinematics ();
       k.getReadWritePosition().setY ( y );
       }
      
       public Position getPosition()
       {
       Position ret = getUniqueTrack ().getKinematics().getPosition();
       //System.out.println ( "TracksTableJbpcDelegate/getPosition/ret.getX(): " + ret.getX() );
       //Navigating ret.getX: correct, up-to-date result
       //When commented: incorrect, rolled-back result
      
       return ret;
       }
      
       public ReadWriteTrack getUniqueTrack()
       {
       if ( tracks.size() != 1 ) throw new IllegalStateException ( "Cette methode ne peut etre appelee que si la liste contient un unique element" );
       ReadWriteTrack ret = tracks.get ( 0 );
       //System.out.println ( "TracksTableJbpcDelegate/getUniqueTrack/ret.getX(): " + ret.getKinematics().getPosition().getX() );
       return ret;
       }
      }
      

      Of course Position is also instrumented (@Replicable).

      Client side, the test:
      @Test
       public void atomiciteUpdate3 () throws Exception
       {
       table.createTrack ( false );
       assertEquals ( 1 , table.size() );
      
       final double X_OK=111d, Y_OK=222d, PRECISION=0.001d;
       table.updateXY ( X_OK , Y_OK , false );
       assertEquals ( X_OK , table.getPosition().getX() , PRECISION );
       assertEquals ( Y_OK , table.getPosition().getY() , PRECISION );
      
       try
       {
       final double X_KO=333d, Y_KO=444d;
       table.updateXY ( X_KO , Y_KO , true );
       fail ();
       }
       catch ( Exception e ) {}
       finally
       {
       assertEquals ( Y_OK , table.getPosition().getY() , PRECISION );
       assertEquals ( X_OK , table.getPosition().getX() , PRECISION );
       }
       }


      When the argument failOnUpdateY of method TracksTableImpl updateXY is true, the transaction is rolled back. This works correctly, that isn't the issue.

      The issue is that when the bold underscored log is uncommented, the test passes, while it doesn't if it's commented.
      Plus the same test with JBPC and the test in the same JVM passes.

      Thus, i think i'm having a lazy-loading issue: when i call getX server-side before sending the response to the client, it works because i have just forced the state to be reloaded by navigating the getX relation.
      When the test and JBPC are in the same JVM, the test passes because lazy-loading can work in this case (i'm not "detached" to use a Hibernate analogy)
      But when the test is remote AND i don't navigate the relation, i'm detached and lazy-loading can't work (unlike Hibernate i don't get LazyInitializationException though, i just silently get an incorrect state).

      So my question is: how do i specify to JBPC that i want eager-loading on serialization?

        • 1. Re: How to force eager loading? (not CacheLoader)

          Nobody? This bug seems quite severe to me (or my parametring is wrong but i'm stuck there).

          • 2. Re: How to force eager loading? (not CacheLoader)
            manik

            I'm not sure I understand what you mean by eager loading. Eager loading from where?

            • 3. Re: How to force eager loading? (not CacheLoader)

              I was doing an analogy with JPA and its infamous LazyInitialization. Here the state is not persistent, but a POJO instrumented by JBossPOJOCache:

              package hellotrackworld.impl;
              import com.thalesgroup.NAJA.domain.types.kinematics.ReadWriteKinematics;
              import com.thalesgroup.NAJA.domain.types.tracks.ReadWriteTrack;
              
              @org.jboss.cache.pojo.annotation.Replicable
              public class TracksTableJbpcDelegate implements TracksTable, TracksTableServices
              {
               private List<ReadWriteTrack> tracks = new ArrayList<ReadWriteTrack> ();
               private int scalaire;
              
               @Override
               public void createTrack ( ReadWriteTrack track )
               {
               tracks.add ( track );
               }
              
               @Override
               public void deleteAllTracks()
               {
               tracks.clear();
               }
              
               @Override
               public int size()
               {
               return tracks.size();
               }
              
               @Override
               public void updateX ( double x )
               {
               ReadWriteKinematics k = getKinematics ();
               k.getReadWritePosition().setX ( x );
               }
              
               @Override
               public void updateY ( double y )
               {
               ReadWriteKinematics k = getKinematics ();
               k.getReadWritePosition().setY ( y );
               }
              
               @Override
               public int getScalaire()
               {
               return scalaire;
               }
              
               @Override
               public void setScalaire(int i)
               {
               this.scalaire = i;
               }
              
               private ReadWriteKinematics getKinematics ()
               {
               ReadWriteTrack track = getUniqueTrack();
               ReadWriteKinematics k = track.getReadWriteKinematics();
               return k;
               }
              
               @Override
               public ReadWriteTrack getUniqueTrack()
               {
               if ( tracks.size() != 1 ) throw new IllegalStateException ( "Cette
               methode ne peut etre appelee que si la liste contient un unique
               element" );
               ReadWriteTrack ret = tracks.get ( 0 );
               ret.getKinematics().getPosition().getX();//see 3.4.3.4.4
               return ret;
               }
              }




              If we look at method getUniqueTrack, we see that before returning the ReadWriteTrack to the atomicUpdate test, we navigate its object graph up to the X attribute:
              @Override
              public ReadWriteTrack getUniqueTrack()
              {
               ReadWriteTrack ret = tracks.get ( 0 );
               ret.getKinematics().getPosition().getX();
               return ret;
              }

              X is the criterion used by atomicUpdate to test correct rollback behaviour:
              @Test
              public void atomiciteUpdate () throws Exception
              {
               [...]
               Position apresUpdateKO = tracksHolder().getUniqueTrack
               ().getKinematics().getPosition();
               assertEquals ( X_OK , apresUpdateKO.getX() , PRECISION );
              }


              We shouldn't have to do this, yet if we don't the test fails. Why? Let's sum up the behaviour of the atomicUpdate test:
              -Remote cache, no navigation to the X attribute: test fails
              -Remote cache, navigation to the X attribute: test passes
              -Cache co-localised with the test: test passes


              Hence the interpretation of the issue as analogous to lazy-loading issues in the JPA world:

              JPA:
              -Instrumented object: @Entity
              -Active state: Being attached to the persistence context
              -Passive state: Detaching from the persistence context
              -Navigating relations while in the active state: Lazy-loads the navigated relations
              -Navigating relations while in the passive state: LazyInitializationException

              JBossPOJOCache:
              -Instrumented object: @Replicable
              -Active state: Being instrumented
              -Passive state: Serializing (the server is remote with respect to the unit test; JBPC is LOCAL to the server)
              -Navigating relations while in the active state: Lazy-loads the navigated relations
              -Navigating relations while in the passive state: Stale value return silently (value returned to the client is NOT the rolled back value)

              • 4. Re: How to force eager loading? (not CacheLoader)

                So to answer your question "from where" directly, i don't know. I dubbed this problem "the lazy-loading problem" but it might have nothing to do with it. It is the fact that navigating relations server-side (or rather where the cache lives) solve it that made me name it that.