Hi Geoff,

I've read through the SoftReference documentation and as far as I
understand it the references do only get garbage-collected in case of
memory-pressure.
However, the behavior to keep recently used objects is only encouraged, not
explicitly required.

Looking over the source code, you mabye can replace PageSource with a
custom implementation that uses another caching implementation.
Something like this (untested) code maybe?

package tapestry;

import java.lang.ref.SoftReference;
import java.util.Map;

import org.apache.tapestry5.commons.util.CollectionFactory;
import org.apache.tapestry5.internal.services.PageLoader;
import org.apache.tapestry5.internal.services.PageSourceImpl;
import org.apache.tapestry5.internal.structure.Page;
import
org.apache.tapestry5.services.pageload.ComponentRequestSelectorAnalyzer;
import org.apache.tapestry5.services.pageload.ComponentResourceSelector;

public class CustomPageSourceImpl extends PageSourceImpl {

    private PageLoader                       pageLoader;
    private ComponentRequestSelectorAnalyzer selectorAnalyzer;

    public CustomPageSourceImpl(PageLoader pageLoader,
ComponentRequestSelectorAnalyzer selectorAnalyzer) {
        super(pageLoader, selectorAnalyzer);
        this.pageLoader = pageLoader;
        this.selectorAnalyzer = selectorAnalyzer;

    }

    private static final record CachedPageKey(String pageName,
ComponentResourceSelector selector) {
    }

    private final Map<CachedPageKey, Object> pageCache =
CollectionFactory.newConcurrentMap();

    public Page getPage(String canonicalPageName)
    {
        var selector = selectorAnalyzer.buildSelectorForRequest();

        var key = new CachedPageKey(canonicalPageName, selector);

        while (true)
        {
            Object cachedObject = pageCache.get(key);

            Page page = null;
            if (cachedObject instanceof SoftReference<?> ref) {
                page = ref == null ? null : (Page) ref.get();
            } else {
                page = (Page) cachedObject;
            }

            if (page != null)
            {
                return page;
            }
            // In rare race conditions, we may see the same page loaded
multiple times across
            // different threads. The last built one will "evict" the
others from the page cache,
            // and the earlier ones will be GCed.

            page = pageLoader.loadPage(canonicalPageName, selector);

            // TODO: Decide here if how you want to store the Page

            Object cacheValue = new SoftReference<Page>(page);

            pageCache.put(key, cacheValue);
        }
    }
}

I'm not sure what the implications are if a page is kept forever, but as a
SoftReference isn't guaranteed to be garbage-collected, I don't see an
immediate downside, except needing more memory.

Alternatively you could trigger the page with a cron job to keep it
"fresh", but an overriden service is the more robust solution in my opinion.

Cheers
Ben

On Tue, Dec 27, 2022 at 3:24 PM JumpStart <
geoff.callender.jumpst...@gmail.com> wrote:

> Hi,
>
> I have one page in my production app which takes a long time to load -
> minimum 18 seconds. Once loaded, it is very quick to request. But from time
> to time throughout the day, when this page is requested Tapestry decides it
> has to reload it. I presume this is because the page cache uses
> SoftReference (PageSourceImpl.pageCache) and the page has been garbage
> collected. For the unlucky user, they have to wait an unbearably long time.
> Sometimes when the system is under load the request can even time out. Is
> there a simple, reliable, safe way to prevent it being garbage collected?
> Or have I misunderstood what’s going on?
>
> Cheers,
>
> Geoff
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
> For additional commands, e-mail: users-h...@tapestry.apache.org
>
>

Reply via email to