6 Replies Latest reply on Jul 26, 2007 12:17 PM by brian.stansberry

    Region management issues

    brian.stansberry

      Some unit test fixing Vladimir did has uncovered a change between 1.x and 2.0 in terms of region management. In 1.x there's no need to register a classloader with a region before you can activate it. I.e. the activation/inactivation feature did not require classloader registration. So, you could use the feature even if you didn't need to unmarshall using a special classloader.

      Let's discuss whether we care about that use case or not on a separate thread. :-) I just bring it up as an intro to a discussion about how region management works in 2.0:

      The requirement to register a classloader was introduced due to this in RegionManager.getReqion(Fqn, Region.Type, boolean) :

      Region r = regionsRegistry.get(fqnToUse);
      
      // this is a very poor way of telling whether a region is a marshalling one or an eviction one. :-(
      // mandates that class loaders be registered for marshalling regions.
      if (type == Region.Type.ANY
       || (type == Region.Type.MARSHALLING && r.getClassLoader() != null)
       || (type == Region.Type.EVICTION && r.getEvictionPolicyConfig() != null))
      {
       return r;
      }


      This testing is designed to make it easy discriminate between regions used for eviction and those used for marshalling.

      As an alternative, how about getting rid of Region.Type and instead implement nesting of regions? Pseudo-code:

      public Region(Fqn fqn, Region parent)
      {
       this.fqn = fqn;
       this.parent = parent;
      }
      
      public Region getParent()
      {
       return this.parent;
      }
      
      public ClassLoader getClassLoader()
      {
       ClassLoader result = this.classLoader;
       if (result == null && this.parent != null)
       result = parent.getClassLoader();
       return result;
      }
      
      // Same thing for getEvictionRegionConfig, etc.
      


      We could possibly get rid of Region.Type.

      One problem I see with this approach is CacheMarshaller200.extractRegionFqn() could end up finding a region below the one where the classloader is registered, with the result that the marshalling fqn included with the region is bigger than it needs to be. E.g.

      Region high = cache.getRegion(Fqn.fromString("/a"), true);
      high.registerContextClassLoader(someCL);
      ...
      Region deep = cache.getRegion(Fqn.fromString("/a/b/c/d/e/f"), true);
      deep.setEvictionPolicyConfig(someConfig);

      (I can see any easy solution to that though, if we keep Region.Type as a hint to the getRegion() method).)

      A benefit of nesting is the eviction code should be able to find the EvictionPolicy much faster when a node that's experienced an event is far below the eviction region. RegionManager.getReqion(Fqn, Region.Type, boolean) current looks for the fqn in the region map, and then checks if it's the correct type. If not, it moves up to the fqn parent and tries that. Multiple map lookups.

        • 1. Re: Region management issues
          brian.stansberry

          Sorry, my last paragraph about finding regions faster is semi-nonsense. You only get a benefit if there is a marshalling region at a depth below the eviction region. Not total nonsense though, as that can happen with the Hibernate use case.

          Related to the speed of finding regions, seems we lost the stuff Elias did in 1.4 where we tracked the depth of the deepest Fqn used to create a Region. Then for any getRegion() request we trim the passed-in Fqn to that depth, and only start looking at that level or higher.

          • 2. Re: Region management issues
            brian.stansberry

            Last monologue post...

            When we think about regions, keep in mind that PojoCache adds a 3rd use case beyond marshalling and eviction. For PojoCache, the presence of a region functions as a mechanism to scope reference sharing of pojos.

            • 3. Re: Region management issues
              vblagojevic

              Brian,

              I am not sure 100% what the problem is but it sounds like we have to decouple lookup mechanism from the region i.e each region type should have its own lookup mechanism, correct? Can you state the problem on a higher level?

              • 4. Re: Region management issues
                brian.stansberry

                The high level conceptual issue is we have one class for Region (IMO good). But, Region has 4 different use cases: 1) eviction, 2) marshalling/classloading, 3) region activation/inactivation and 4) PojoCache reference scoping. Marshalling and activation/inactivation are related, but there isn't a 100% conceptual overlap. Region activation/inactivation is also usable as a way to get partial state transfer, even if there are no classloader issues involved.

                In 2.0 we deal with the different use cases by sometimes passing a Region.Type into the getRegion() method. We scan through all registered regions looking for Fqn matches. Then based on the requested type, we check characteristics of the regions to see if they match. The code block in my first post shows that.

                Two problems with this:

                1) Somewhat inefficient, since if we're looking for eviction region /a and there's marshalling region /a/b/c, we do a map lookup for /a/b/c, reject the marshalling region, then do a map lookup for /a/b (miss) and then finally do a map lookup for /a and get our eviction region. Seems more efficient to do a map lookup for /a/b/c, and then call getParent() on the region until we find an eviction region.

                2) If we request a marshalling region, the current algorithm returns null if there is no region with a classloader registered. That's a new behavior added in 2.0; didn't work that way in 1.x. In 1.x you could create a "marshalling" region and never register a classloader. That's why you had to add a region registration to the unit test.

                The second problem can be solved without doing the nested region thing I've suggested on this thread. You just change getRegion such that if Region.Type.MARSHALLING is passed, you keep a ref to the lowest region you find. You then keep looking for one with a classloader registered. If you find one with a classloader, return it. If not, return the first one you found.

                • 5. Re: Region management issues
                  galder.zamarreno

                  +1 on nesting regions. If 1.x behaivour can be mantained, I'd go for it.

                  You just change getRegion such that if Region.Type.MARSHALLING is passed, you keep a ref to the lowest region you find. You then keep looking for one with a classloader registered. If you find one with a classloader, return it. If not, return the first one you found.


                  Looks a bit hacky, so i'd go for nesting regions.


                  • 6. Re: Region management issues
                    brian.stansberry

                    Agreed that this is hacky, but either option is an implementation detail that can be changed. The hack should be simple and allows us to avoid adding a behavior change vs 1.x in 2.0. We can then look at non-hacky approaches at greater leisure. If we don't restore the 1.x behavior in 2.0, we really shouldn't restore it at all.

                    Here's the hack impl. I haven't tried this; only quality guarantee I can give is that Eclipse doesn't give me any compile error warnings:

                    Index: RegionManager.java
                    ===================================================================
                    RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/RegionManager.java,v
                    retrieving revision 1.38
                    diff -u -r1.38 RegionManager.java
                    --- RegionManager.java 28 Jun 2007 16:53:35 -0000 1.38
                    +++ RegionManager.java 26 Jul 2007 16:08:42 -0000
                    @@ -139,6 +139,8 @@
                     {
                     Fqn fqnToUse = fqn;
                     if (DEFAULT_REGION.equals(fqnToUse)) fqnToUse = Fqn.ROOT;
                    +
                    + Region firstFound = null;
                     // first see if a region for this specific Fqn exists
                     if (regionsRegistry.containsKey(fqnToUse))
                     {
                    @@ -152,6 +154,12 @@
                     {
                     return r;
                     }
                    +
                    + if (type == Region.Type.MARSHALLING)
                    + {
                    + firstFound = r;
                    + }
                    +
                     }
                    
                     // if not, attempt to create one ...
                    @@ -195,11 +203,16 @@
                     {
                     nextBestThing = r;
                     }
                    +
                    + if (type == Region.Type.MARSHALLING && firstFound == null)
                    + {
                    + firstFound = r;
                    + }
                     }
                     if (nextFqn.isRoot()) break;
                     }
                    
                    - return nextBestThing;
                    + return (nextBestThing == null ? firstFound : nextBestThing);
                     }
                    
                     /**