8 Replies Latest reply on Jun 14, 2017 7:12 AM by jacobilsoe

    The cache is per session?

    mfed

      I would have expected the in-memory cache to be cross session but it pretty clearly appears to be per session. Each time I create a new session, I incur DB transaction costs even if I've already loaded the given node and it hasn't changed in any way. Is this configurable? Consider a web app where i'm serving up read only content. It would be pretty desirable to not go to the DB for each request but it also is sensible/desirable to have a session per request right?

       

      Is the recommendation that I need to build out my own cache on top of what modeshape offers if I want cross session caching? That seems like something which I should get out of the box.

        • 1. Re: The cache is per session?
          hchiorean

          The only in-memory cache ModeShape uses explicitly is called a workspace cache and is a read-only cache shared by all the sessions that belong to a workspace. So multiple sessions share the same in-memory cache.

          Each time I create a new session, I incur DB transaction costs even if I've already loaded the given node and it hasn't changed in any way.

          If you create a new JCR session for a particular workspace and a node has been loaded (i.e. read) by another session and it has has not been evicted by the workspace cache (the eviction algorithm is a simple LRU algorithm) then this node will be read from the in-memory workspace cache and not from the DB.

           

          However, when you refer to DB transactions I assume you refer to write-transactions: each time a JCR session performs a #save operation it

          1. loads a fresh copy of all the changed nodes from the DB after exclusively locking the nodes

          2. evicts these nodes at the end of the #save method from the workspace cache

           

          Both (1) and (2) are mandatory to ensure the equivalent of strong serializable consistency between JCR sessions which is required in order to be JCR compliant. This means that whenever there are multiple writing sessions which change the same nodes, the "caching function" of the workspace cache is limited as entries will be frequently evicted.

          • 2. Re: The cache is per session?
            mfed

            Thanks for the reply. That's definitely not the behavior I'm seeing though - i must be doing something wrong. I'm not doing any write operations. When I run my code, the DB already has all of the data. I have the modeshape logs set to trace and I've set my cacheSize in my config to be much larger than needed. Each time that I load the node with a new session, I clearly see in the logs something like this:

             

            DEBUG [15:31:52,226]: New transaction '300b1747-8e66-42b6-91f3-3142091f2b67' started by ModeShape...

            DEBUG [15:31:52,275]: New DB connection allocated for tx '300b1747-8e66-42b6-91f3-3142091f2b67'

            TRACE [15:31:52,276]: Created & stored new ModeShape synchronized transaction 'NestableThreadLocalTransaction[id='300b1747-8e66-42b6-91f3-3142091f2b67']'

            TRACE [15:31:52,276]: Begin transaction org.modeshape.jcr.txn.LocalTransaction@c1f871a9

            TRACE [15:31:52,276]: Save #5 (part of transaction 'org.modeshape.jcr.txn.LocalTransaction@c1f871a9') by session <anonymous>(fe8e0463f) is persisting the following changes:

            TRACE [15:31:52,276]: Save #5 (part of transaction 'org.modeshape.jcr.txn.LocalTransaction@c1f871a9') by session <anonymous>(fe8e0463f) completed persisting changes to 0 nodes

            DEBUG [15:31:52,276]: Altered 0 node(s)

            TRACE [15:31:52,276]: Commit transaction 'org.modeshape.jcr.txn.LocalTransaction@c1f871a9'

            DEBUG [15:31:52,276]: Received committed notification for transaction '300b1747-8e66-42b6-91f3-3142091f2b67'

            DEBUG [15:31:52,276]: Committing the active connection for transaction 300b1747-8e66-42b6-91f3-3142091f2b67 with the changes: {}

            DEBUG [15:31:52,292]: Releasing DB connection for transaction 300b1747-8e66-42b6-91f3-3142091f2b67

             

            I'm doing something roughly equivalent to this:

             

            val engine: ModeShapeEngine = new ModeShapeEngine

            engine.start()

            engine.deploy(config)

            ....

            val repository = engine.getRepository(repositoryName)

             

            ...

            for(i <- 1 to 15) {

                 val session = repository.login(workspace)

                 session.getNode("/foo")

                 session.save()

                 session.logout()

            }

             

            Given what you said above, I removed the session.save() and boom i stopped going to the DB. This behavior seems odd to me. If I have no changes to persist, I would expect save to be logically equivalent to a no-op, thus not hitting the DB or making any changes to the cache state if it is. For now, I'll just be smarter with my saves. Thanks for the help.

            • 3. Re: The cache is per session?
              hchiorean

              This behavior seems odd to me. If I have no changes to persist, I would expect save to be logically equivalent to a no-op, thus not hitting the DB or making any changes to the cache state if it is.

              That's a fair point and this might very well be a bug. Please open a JIRA and link this conversation and I'll investigate this issue some more. Thanks.

              • 4. Re: The cache is per session?
                mfed
                • 5. Re: The cache is per session?
                  mfed

                  I also seem to be getting this for each node on each read:

                   

                  LOGGER.trace("Node '{0}' is not found in the '{1}' workspace cache; looking in store", key, workspaceName);

                   

                  Do i need to do something special to mark nodes as cacheable? It looks like it should default to true but I don't understand why I'd get this each time.

                  • 6. Re: The cache is per session?
                    hchiorean

                    No, as explained above unless there are #saves() (or nodes are not evicted from the ws cache as per LRU), nodes should be found in the workspace cache from multiple sessions (or the same read-only session)

                     

                    There are several ways in which you can test this:

                    1. use a single session that does several #getNode calls on the same node (without saving). Only the first call should load the node from the DB

                    2. use several sessions to the same workspace (again, without saving either of them). Only one of them should load the node from the DB while the other should find the node in the cache

                     

                    If either (1) or (2) are not working in your local environment then there's a bug somewhere so please attach the test case to MODE-2655.

                    • 7. Re: The cache is per session?
                      hchiorean
                      • 8. Re: The cache is per session?
                        jacobilsoe

                        I have a similar issue where I read nodes, never call save() and still the nodes are reread in each new session.

                         

                        I did some debugging and found that TransactionalWorkspaceCache is used instead of WorkspaceCache because I run my code in a user transaction. Thus, a new cache is used for each transaction. Is there any way to work around this except from not running in a transaction?

                         

                        Also it seems that RepositoryCache.createSession will create a ReadOnlySessionCache unless the current user has the READWRITE or ADMIN role. Is there any other way of "marking" the transaction as readonly?

                         

                        Furthermore I tried using a READONLY user and now I get an exception when trying to read ACLs from a node due to the session being read-only. AccessControlManagerImpl.getApplicablePolicies throws an exception in case of a read-only session. Why is this the case?