Re: Avoiding page loading

2023-07-12 Thread Thiago H. de Paula Figueiredo
Hello, Ben!

Your suggestion is great and I've implemented it in
https://github.com/apache/tapestry-5/commit/d6c4324215337b3bfb9ed8e62dd7174e0427410d
. It's some code that will be called a low number times during the
application runtime, so I decided to not try to optimize the
just-1-page-name case.
Cheers!

Th

On Sun, Jun 18, 2023 at 7:33 AM Ben Weidig  wrote:
>
> Hi Thiago,
>
> I like your implementation as a general solution better, as it solves the
> problem in a simple and straightforward way, whereas my approach would only
> provide the tools to build a solution by yourself.
>
> One thing I might have added is a helper method in the ReferenceType to
> create the most common contribution easier, maybe something like this
> pseudo/untested code:
>
> public enum ReferenceType {
> SOFT,
> STRONG;
>
> public PageCachingReferenceTypeService forPages(String...
> canonicalPageNames) {
> // Handle simplest case without allocating a HashSet
> if (canonicalPageNames.length == 1) {
> return pageName ->
> canonicalPageNames[0].equalsIgnoreCase(pageName) ? this : null;
> }
>
> // Prepare the page names for case-insensitive lookup
> Set pageNames = new HashSet<>(canonicalPageNames.length);
> for (String pageName : canonicalPageNames) {
> pageNames.add(pageName.toLowerCase());
> }
>
> return pageName -> pageNames.contains(pageName.toLowerCase()) ?
> this : null;
> }
> }
>
> configuration.add("StrongPages", ReferenceType.STRONG.forPages("VeryLarge",
> "AndTheOtherLargePage"));
>
> Cheers
> Ben
>
> On Sun, Jun 18, 2023 at 12:11 AM Thiago H. de Paula Figueiredo <
> thiag...@gmail.com> wrote:
>
> > Hello!
> >
> > So I've just implemented what I suggested in Tapestry 5.8.3:
> > https://issues.apache.org/jira/browse/TAP5-2756. Example:
> >
> > public static void contributePageCachingReferenceTypeService(
> >   OrderedConfiguration
> > configuration) {
> > configuration.add("VeryLarge", p -> p.equals("VeryLarge") ?
> > ReferenceType.STRONG : null);
> > }
> >
> > This would cause the page named VeryLarge to be cached using a
> > regular, strong, non-garbage-collectable reference while leaving all
> > other pages cached with a soft, garbage-collectable reference.
> >
> > Ben, I find your idea interesting, but I believe it's a bit orthogonal
> > to what I did, both being able to coexist.
> >
> > On Thu, Jan 5, 2023 at 10:23 AM Thiago H. de Paula Figueiredo
> >  wrote:
> > >
> > > Hello, everyone!
> > >
> > > I prefer Ben's idea of a thread or cron job to keep it fresh other
> > > than overriding a service, especially now that I'm working on
> > > something (smarter page invalidation, which is actually smarter
> > > invalidation of some key Tapestry caches) which changes that
> > > PageSourceImpl a bit and this override would likely break live class
> > > reloading. I'd have the cron/thread call ComponentSource.getPage()
> > > instead, since PageSource is an internal service and ComponentSource
> > > isn't.
> > >
> > > Another possibility is to introduce a configuration to tell whether
> > > PageSource should use regular references or soft ones, defaulting to
> > > current behavior. Or a new service to tell whether a specific page
> > > class should use a regular reference instead of a soft one. This would
> > > be more flexible.
> > >
> > > On Wed, Dec 28, 2022 at 6:55 AM Ben Weidig  wrote:
> > > >
> > > > 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);
> > > > 

Re: Avoiding page loading

2023-06-18 Thread Ben Weidig
Hi Thiago,

I like your implementation as a general solution better, as it solves the
problem in a simple and straightforward way, whereas my approach would only
provide the tools to build a solution by yourself.

One thing I might have added is a helper method in the ReferenceType to
create the most common contribution easier, maybe something like this
pseudo/untested code:

public enum ReferenceType {
SOFT,
STRONG;

public PageCachingReferenceTypeService forPages(String...
canonicalPageNames) {
// Handle simplest case without allocating a HashSet
if (canonicalPageNames.length == 1) {
return pageName ->
canonicalPageNames[0].equalsIgnoreCase(pageName) ? this : null;
}

// Prepare the page names for case-insensitive lookup
Set pageNames = new HashSet<>(canonicalPageNames.length);
for (String pageName : canonicalPageNames) {
pageNames.add(pageName.toLowerCase());
}

return pageName -> pageNames.contains(pageName.toLowerCase()) ?
this : null;
}
}

configuration.add("StrongPages", ReferenceType.STRONG.forPages("VeryLarge",
"AndTheOtherLargePage"));

Cheers
Ben

On Sun, Jun 18, 2023 at 12:11 AM Thiago H. de Paula Figueiredo <
thiag...@gmail.com> wrote:

> Hello!
>
> So I've just implemented what I suggested in Tapestry 5.8.3:
> https://issues.apache.org/jira/browse/TAP5-2756. Example:
>
> public static void contributePageCachingReferenceTypeService(
>   OrderedConfiguration
> configuration) {
> configuration.add("VeryLarge", p -> p.equals("VeryLarge") ?
> ReferenceType.STRONG : null);
> }
>
> This would cause the page named VeryLarge to be cached using a
> regular, strong, non-garbage-collectable reference while leaving all
> other pages cached with a soft, garbage-collectable reference.
>
> Ben, I find your idea interesting, but I believe it's a bit orthogonal
> to what I did, both being able to coexist.
>
> On Thu, Jan 5, 2023 at 10:23 AM Thiago H. de Paula Figueiredo
>  wrote:
> >
> > Hello, everyone!
> >
> > I prefer Ben's idea of a thread or cron job to keep it fresh other
> > than overriding a service, especially now that I'm working on
> > something (smarter page invalidation, which is actually smarter
> > invalidation of some key Tapestry caches) which changes that
> > PageSourceImpl a bit and this override would likely break live class
> > reloading. I'd have the cron/thread call ComponentSource.getPage()
> > instead, since PageSource is an internal service and ComponentSource
> > isn't.
> >
> > Another possibility is to introduce a configuration to tell whether
> > PageSource should use regular references or soft ones, defaulting to
> > current behavior. Or a new service to tell whether a specific page
> > class should use a regular reference instead of a soft one. This would
> > be more flexible.
> >
> > On Wed, Dec 28, 2022 at 6:55 AM Ben Weidig  wrote:
> > >
> > > 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 pageCache =
> > > CollectionFactory.newConcurrentMap();
> > >
> > > public Page getPage(String canonicalPageName)
> > > {
> > > var selector = selectorAnalyzer.buildSelectorForRequest();
> > >
> > > var key = new CachedPageKey(canonicalPageName, selector);
> > >
> > > while (true)
> > > {
> > >

Re: Avoiding page loading

2023-06-17 Thread Thiago H. de Paula Figueiredo
Hello!

So I've just implemented what I suggested in Tapestry 5.8.3:
https://issues.apache.org/jira/browse/TAP5-2756. Example:

public static void contributePageCachingReferenceTypeService(
  OrderedConfiguration
configuration) {
configuration.add("VeryLarge", p -> p.equals("VeryLarge") ?
ReferenceType.STRONG : null);
}

This would cause the page named VeryLarge to be cached using a
regular, strong, non-garbage-collectable reference while leaving all
other pages cached with a soft, garbage-collectable reference.

Ben, I find your idea interesting, but I believe it's a bit orthogonal
to what I did, both being able to coexist.

On Thu, Jan 5, 2023 at 10:23 AM Thiago H. de Paula Figueiredo
 wrote:
>
> Hello, everyone!
>
> I prefer Ben's idea of a thread or cron job to keep it fresh other
> than overriding a service, especially now that I'm working on
> something (smarter page invalidation, which is actually smarter
> invalidation of some key Tapestry caches) which changes that
> PageSourceImpl a bit and this override would likely break live class
> reloading. I'd have the cron/thread call ComponentSource.getPage()
> instead, since PageSource is an internal service and ComponentSource
> isn't.
>
> Another possibility is to introduce a configuration to tell whether
> PageSource should use regular references or soft ones, defaulting to
> current behavior. Or a new service to tell whether a specific page
> class should use a regular reference instead of a soft one. This would
> be more flexible.
>
> On Wed, Dec 28, 2022 at 6:55 AM Ben Weidig  wrote:
> >
> > 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 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);
> >
> > 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> 

Re: Avoiding page loading

2023-01-05 Thread Ben Weidig
Hello!

Your're right, overriding an internal service is usually an invitation for
trouble with any bigger Tapestry update.
Still, personally, I prefer a service override over cron jobs triggering
things behind the scenes.
Especially since it's one of Tapestry's greatest features to replace almost
any part of it as needed, so I can break all the things :-D

Making the caching style more configurable might be a nice addition to
provide more freedom.
However, my first thought was a little bit more detached from
"SoftReference or not".

Something trivial like this:

interface PageCacheService {
Page  getPage(String canonicalPageName, ComponentResourceSelector
selector);
void setPage(Page page, String canonicalPageName,
ComponentResourceSelector selector);
}

This way, any type of caching could be used, like a dedicated caching
library like EhCache.

The org.apache.tapestry5.internal.services.PageSourceImpl.CachedPageKey
could be exposed to be used instead of dedicated arguments for
canonicalPageName and selector.

The default implementation could be a simple wrapper around a
Map>, just like the one used now.

Cheers

On Thu, Jan 5, 2023 at 2:23 PM Thiago H. de Paula Figueiredo <
thiag...@gmail.com> wrote:

> Hello, everyone!
>
> I prefer Ben's idea of a thread or cron job to keep it fresh other
> than overriding a service, especially now that I'm working on
> something (smarter page invalidation, which is actually smarter
> invalidation of some key Tapestry caches) which changes that
> PageSourceImpl a bit and this override would likely break live class
> reloading. I'd have the cron/thread call ComponentSource.getPage()
> instead, since PageSource is an internal service and ComponentSource
> isn't.
>
> Another possibility is to introduce a configuration to tell whether
> PageSource should use regular references or soft ones, defaulting to
> current behavior. Or a new service to tell whether a specific page
> class should use a regular reference instead of a soft one. This would
> be more flexible.
>
> On Wed, Dec 28, 2022 at 6:55 AM Ben Weidig  wrote:
> >
> > 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 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);
> >
> > pageCache.put(key, cacheValue);
> >

Re: Avoiding page loading

2023-01-05 Thread Thiago H. de Paula Figueiredo
Hello, everyone!

I prefer Ben's idea of a thread or cron job to keep it fresh other
than overriding a service, especially now that I'm working on
something (smarter page invalidation, which is actually smarter
invalidation of some key Tapestry caches) which changes that
PageSourceImpl a bit and this override would likely break live class
reloading. I'd have the cron/thread call ComponentSource.getPage()
instead, since PageSource is an internal service and ComponentSource
isn't.

Another possibility is to introduce a configuration to tell whether
PageSource should use regular references or soft ones, defaulting to
current behavior. Or a new service to tell whether a specific page
class should use a regular reference instead of a soft one. This would
be more flexible.

On Wed, Dec 28, 2022 at 6:55 AM Ben Weidig  wrote:
>
> 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 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);
>
> 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
> >
> >



-- 
Thiago


Re: Avoiding page loading

2023-01-03 Thread JumpStart
Thank you Ben. I may have to go this route if I can’t find a way to make the 
page much lighter to load.

Geoff

> On 28 Dec 2022, at 5:55 pm, Ben Weidig  wrote:
> 
> 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 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);
> 
>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
>> 
>> 


-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org



Re: Avoiding page loading

2022-12-28 Thread Ben Weidig
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 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);

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
>
>