Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit size of context cache in the TestContext framework [SPR-8055] #12710

Closed
4 of 5 tasks
spring-projects-issues opened this issue Mar 14, 2011 · 8 comments
Closed
4 of 5 tasks
Assignees
Labels
in: test Issues in the test module type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Mar 14, 2011

Gaetan Pitteloud opened SPR-8055 and commented

Overview

The cache of application contexts becomes too big when running a complete suite of tests that each load a full ApplicationContext with a JPA EntityManagerFactory. There are situations where a tiny difference in the configuration forces us to load a different context.

Since the context cache is only there for performance reasons, its maximum size should be configurable -- a system property might be the best choice, while no property means unbounded size -- so that the least recently used can be properly closed and evicted from the cache.

Deliverables

  1. Introduce a mechanism for configuring the maximum number of contexts that should be cached at any given time.
    • Possible mechanisms include: simple system property or a system property supported via SpringProperties.
  2. Redesign DefaultContextCache to implement an LRU cache eviction algorithm.
    • Ensure that evicted contexts get properly closed.
    • Consider internally delegating to a third-party implementation such as a Guava cache.

Attachments:

Issue Links:

Referenced from: commits e18d5b5, ebeba43

1 votes, 6 watchers

@spring-projects-issues
Copy link
Collaborator Author

Gaetan Pitteloud commented

Here is an implementation of the feature, that I have implemented and used in the meanwhile. Feel free to use it.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 18, 2015

Sam Brannen commented

Since contexts are now cached using SoftReferences (see #12343), I am resolving this issue as "Won't Fix".

However, if someone feels that the fix for #12343 is insufficient, we may consider revisiting the proposal to explicitly limit the cache size at a later date.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 19, 2015

Sam Brannen commented

As stated in the comments for issue #12343,

the use of the soft or weak references would prevent the TestContext framework from properly closing application contexts that get transparently evicted by the garbage collector.

I am therefore reopening this issue in order to investigate a more robust solution.

I have also added a Deliverables section to this issue's Description.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 19, 2015

Sam Brannen commented

This issue now supersedes #12343.

@spring-projects-issues
Copy link
Collaborator Author

Gaetan Pitteloud commented

The initially proposed patch is for Spring 3.2. In Spring 4, the ContextCache class is a bit different since the key of the cache is not a String anymore. I have attached the newest version.

Further, the CacheAwareContextLoaderDelegate needs to be modified as well : the loadContext() method first invokes loadContextInternal(), which loads the ApplicationContext, and then puts it in the cache. If the cache size is set to N (and is already full), there will be N+1 application contexts loaded in memory for a short period of time (actually until the newly created context replaces the oldest context in the cache); for us, this was already too much.

In order to prevent it, we first put a fake context in the cache (which may trigger a close of the oldest entry if the cache was full), then we load the context (loadContextInternal()), and finally put that new context in the cache. Something similar to this:

@Override
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {
    synchronized (contextCache) {
        ApplicationContext context = contextCache.get(mergedContextConfiguration);
        if (context == null) {
            try {
                // put a fake context in the ContextCache in order to possibly trigger the close of another context 
                // (and recover memory) before loading the new one.
                contextCache.put(mergedContextConfiguration, new StaticApplicationContext());

                context = loadContextInternal(mergedContextConfiguration);
                if (logger.isDebugEnabled()) {
                    logger.debug(String.format("Storing ApplicationContext in cache under key [%s].",
                            mergedContextConfiguration));
                }
                contextCache.put(mergedContextConfiguration, context);
            } catch (Exception ex) {
                throw new IllegalStateException("Failed to load ApplicationContext", ex);
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug(String.format("Retrieved ApplicationContext from cache with key [%s].",
                        mergedContextConfiguration));
            }
        }
        return context;
    }
}

@spring-projects-issues
Copy link
Collaborator Author

Gaetan Pitteloud commented

Spring 4 ContextCache with a possible maximum size

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

The ongoing work for this issue can be viewed here:

https://github.com/sbrannen/spring-framework/commits/SPR-8055

Currently, the only piece missing is automatic, transparent support for properly closing evicted contexts.

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Completed as described in the comments for GitHub commit e18d5b5:

Limit size of context cache in the TestContext framework

Prior to this commit, the size of the ApplicationContext cache in the
Spring TestContext Framework could grow without bound, leading to
issues with memory and performance in large test suites.

This commit addresses this issue by introducing support for setting the
maximum cache size via a JVM system property or Spring property called
"spring.test.context.cache.maxSize". If no such property is set, a
default value of 32 will be used.

Furthermore, the DefaultContextCache has been refactored to use a
synchronized LRU cache internally instead of a ConcurrentHashMap. The
LRU cache is a simple bounded cache with a "least recently used" (LRU)
eviction policy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants