On Tue, Apr 16, 2013 at 10:31 AM, Christopher Schultz <
ch...@christopherschultz.net> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> Howard,
>
> On 4/15/13 4:02 PM, Howard W. Smith, Jr. wrote:
> > On Mon, Apr 15, 2013 at 1:08 PM, Christopher Schultz <
> > ch...@christopherschultz.net> wrote:
> >
> > Howard,
> >
> > On 4/14/13 9:53 PM, Howard W. Smith, Jr. wrote:
> >>>> I am definitely relying on  user HttpSessions, and I do
> >>>> JPA-level caching (statement cache and query results cache).
> >>>> pages are PrimeFaces and primefaces = xhtml, html, jquery,
> >>>> and MyFaces/OpenWebBeans to help with speed/performance.  And
> >>>> right now, the app handles on a 'few' simultaneous
> >>>> connections/users that do small and large fetches/inserts
> >>>> from/into relational database. :)
> >
> >> You can tune the JPA caching, etc. to meet your environmental
> >> needs, etc., so you don't *need* a huge heap. If you find that
> >> you need to be able to improve your performance, you might be
> >> able to increase your cache size if it in fact improves things.
> >
> > doing this, and just made some code changes to tap a little more
> > into JPA caching, but one of my endusers just did a user operation
> > on one of the pages, and he sent me a screen capture of the nasty
> > eclipselink error that he experienced. evidently, i need to tweak
> > caching or do not use the cache at that point in the app. :)
>
> Just remember that caching isn't always a worthwhile endeavor, and
> that the cache itself has an associated cost (e.g. memory use,
> management of the cache, etc.).


Noted, and per my experience (already), I have definitely recognized that.
Thanks.


> If your users don't use cached data very much


Smiling... um, well, the endusers don't 'know' that they 'are' using the
cache, but I did enlighten the one enduser, yesterday, that reported that
eclipselink issue (that was most likely caused by my use of the 'readonly'
query hint). And for the record, they 'are' using the cache, since there
are common pages/data that they access and/or request, multiple times,
daily (and throughout the day), and even multiple times, most likely,
throughout each session.


> or, worse, make so many varied requests that the cache is thrashing the
> whole time, then you are actually hurting performance:
> you may as well go directly to the database each time.
>

They definitely make varied requests, 'too', throughout the day and during
each session, and since I like to monitor performance via jvisualvm, I am
recognizing a lot of 'eclipselink' code that is executed, since i commonly
use readonly and query-results-cache query hints, but performance seems
worse when readonly and/or query-results-cache are not used (when I look at
the times in jvisualvm).

just today, i recognized a query, such as following which was performing
very poorly, even though the JOIN was on a primary/foreign key, and ORDER
BY on primary key (which 'should' be fast):

@NamedQuery(name = "OrderCostDetails.findByOrderId", query = "SELECT ocd
FROM OrderCostDetails ocd JOIN ocd.orders o WHERE o.orderId = :orderId
ORDER BY ocd.costDetailsId"),


so, I commented out that named query, and replaced it with the following,

@NamedQuery(name = "OrderCostDetails.findByOrderId", query = "SELECT
o.orderCostDetails FROM Orders o WHERE o.orderId = :orderId")


also, parameterized the use of query hints (see code below) in the
@Stateless EJB that uses the named query to select data from database,

q = em.createNamedQuery("OrderCostDetails.findByOrderId")
      .setParameter("orderId", id)
      .setHint("eclipselink.query-results-cache", "true");
if (readOnly) {
    q.setHint("eclipselink.read-only", "true");
}
list = q.getResultList();
if (list == null || list.isEmpty()) {
    return null;
}


and added the following in the @Stateless EJB after query results are
retrieved from the database,

// ORDER BY ocd.serviceAbbr, ocd.nbrOfPassengers
Collections.sort(list, new Comparator<OrderCostDetails>() {
        @Override
        public int compare(OrderCostDetails ocd1, OrderCostDetails ocd2) {
            String ocd1SortKey = ocd1.getServiceAbbr() +
ocd1.getNbrOfPassengers();
            String ocd2SortKey = ocd2.getServiceAbbr() +
ocd2.getNbrOfPassengers();
            return ((Comparable)ocd1SortKey).compareTo(ocd2SortKey);
        }
    });


and now, this query, is 'no longer' a hotspot in jvisualvm; it doesn't even
show up in the 'calls' list/view of jvisualvm.

Why did I target this query? because this query seemed as though it should
be fast, but the eclipselink code was executing some 'twisted' method and a
'normalized' method, etc..., so I said to myself, I need to refactor this
query/code, so all that eclipselink code will not hinder performance.

I think the performance improved because of the following: Orders has
OrderCostDetails (1 to many), search Orders via primary key (OrderId) is
much easier than searching OrderCostDetails JOIN(ed) to Orders WHERE
Orders.OrderId = :orderId. So, I am 'sure' that eclipselink is NOT calling
some 'twist' (or normalize) method anymore, and I'm sure use of
Collections.sort(list, ...) is sorting the list in memory...much faster
than the database can... but feel free to correct me on this assumption of
mine. :)


> (This is why many people disable the MySQL query cache which is
> enabled by default:


your use of the phrase, 'query cache' = query statements cache OR query
'results' cache?

I'm using Apache Derby, and default = no query results cache or statements
cache, but I have configured query statements cache in persistence.xml, and
using query results cache 'query hint' at various times throughout the
app...

if you aren't issuing the same query over and over again, you are just
> wasting time and memory with the query cache).
>

it is very 'possible' that queries will be the same over and over again (at
least 2+ times) per user, but of course, queries will vary per user session
as well.


> > i explained to him that i did some major changes in the app,
> > related to caching... and i told him that it was for 'performance
> > improvement', and told him the same as Mark just told me, Google is
> > your friend (and told him that 'wiki' keyword in the search is your
> > friend, too).  :)
>
> You should probably monitor your cache: what's the hit rate versus
> miss rate, and the cache turnover. You might be surprised to find that
> your read-through cache is actually just a churning bile of bytes that
> nobody really uses.
>
> It also sounds like you need a smoke test that you can run against
> your webapp between hourly-deployments to production ;) I highly
> recommend downloading JMeter and creating a single workflow that
> exercises your most-often-accessed pages. You can a) use it for
> smoke-testing and b) use it for load-testing (just run that workflow
> 50 times in a row in each of 50 threads and you've got quite a load,
> there).
>

Interesting. i will have to do that, thanks.


>
> > i have some things in mind what I want to do with that large
> > session scoped data. I am considering caching it at application
> > level and all users have ability to update that huge List<> and
> > extract data. I was thinking of using @Singleton Lock(READ) to
> > control access. it takes no time at all to search the List<> for
> > the information that it needs, and it takes no time at all to
> > re-populate the List<>.
>
> If you are searching your List<>, perhaps you don't have the right
> data structure.


Hmmm, good point, but the data structure (or 'object') is a very small
POJO, just containing 2 members, Integer orderId, and Integer orderNumber.


> What is the algorithmic complexity of your searches?
> If it's not better than O(n), then you should reconsider your data
> structure and/or searching algorithm.
>

    public Integer getOrderNumberForOrder(Orders order) {
        Integer orderNumber = 0;
        if (orderNumberList != null && order != null && order.getOrderId()
!= null) {
            for (OrderNumber oNumber : orderNumberList) {
                if (oNumber == null || oNumber.getOrderId() == null ||
oNumber.getOrderNumber() == null) {
                    continue;
                }
                if (oNumber.getOrderId().equals(order.getOrderId())) {
                    orderNumber = oNumber.getOrderNumber();
                    break;
                }
            }
        }
        return orderNumber;
    }



>
> Does the list need re-population? How often?
>

the list is re-populated when ORDERS are marked as 'confirmed', 'canceled',
and/or when ORDERS are 'deleted'.

This is customized transportation/reservation software for tour bus company
(my family business). So, usually, endusers are modifying ORDERS/data on
single-date 'and' single-year basis, so the List<> contains 'all' ORDER IDs
for the 'selected' year of the 'selected' date that they are viewing and/or
working on. Each ORDER is assigned an ORDER #, which is basically assigned,
sequentially, to all confirmed-and-not-cancelled ORDERS from beginning of
year.

Do you see why now, this is a List<> that I can move at application scope
instead of maintaining it in session scope? I just haven't taken the time
to move it to application scope yet.

Some may say/ask, why don't you use OrderID as the Order#. Nope, I am using
that as a 'Quote #' (as requested by one of the endusers), and the
President/CEO wanted/demanded the ORDER # (ever since the birth of the
legacy version of the app...developed back in 1994/1995).  :)

Moving this list to application scope and controlling access via @Singleton
bean 'will' improve performance... I'm quite confident of that. Most of my
performance enhancements made within the last 6 months have proven to be
effective and noticeable. :)



> > Since we discuss GC a lot on this list, i wonder if you all
> > recommend to set the 'list' to null, first, and then List<> ... =
> > new ArrayList<>(newList), or new ArrayList<>(newList) is sufficient
> > for good GC.
>
> Setting the reference to null is a waste of time as far as the GC is
> concerned. When I write code that re-uses the same identifier a lot in
> a particular method (e.g. a single PreparedStatement identifier in a
> long JDBC transaction executing many statements), I will close the
> statement and then set explicitly it to null before continuing. I do
> that to ensure that the statement is no longer re-usable due to bad
> coding in my part. But it does not really help anything at runtime.
>

Interesting, just last night (or early this morning), I went through
members of the most-frequently-used @SessionScoped bean (OrdersController),
and added a @PreDestroy method that will set member (or instance variable)
= null, hoping that this will help GC a bit/lot. I really have not checked
jvisualvm to see if that code change improved GC or not. I'm still novice
at monitoring GC performance and/or writing code that promotes/helps GC.



>
> On the other hand, if you have a large data structure that you use
> during a part of a method but not all of it, then explicitly setting
> the reference to null can help collect the garbage sooner. A
> completely contrived example:
>
>
> List<Id> itemIds = ...;
> Map<Id,Item> map = createEnormousMap();
> List<Item> items = new ArrayList<>;
> for(Id id : itemIds)
>   items.add(map.get(id));
>
> // Marker for discussion
>
> for(some long time)
> {
>   // do some long operation
>   // that does not require the "enormous map"
> }
>
> return items;
>
> In the above method, if the second loop runs for a long time (relative
> to the rest of the method), then explicitly setting "itemIds" to null
> in the middle of the method will cause the object pointed to by "map"
> to become unreachable and therefore collectable by the GC (assuming
> that the map isn't referenced anywhere else, of course) before the
> method returns. So, in this case, setting a variable to null *can*
> help the GC.
>

hmmm interesting. thanks for sharing. I did read somewhere (either on this
list or some question/answer on stackoverflow) about this. Noted, and will
have to try to remember to do/use this approach. Honestly, I don't think I
'ever' do that, but it is in the back of my mind (or 'up my sleeve') to do
it. :)


>
> >> I've written a Filter and HttpSession wrapper that can do that
> >> kind of thing transparently to the application code. I don't
> >> actually use it right now -- it was just a proof-of concept --
> >> but it's a quick and dirty way to get caching but also save a
> >> safety valve.
> >
> > that's nice proof of concept! I guess i've heard so much bad about
> > people not cleaning up threadlocals, that I try to avoid usage of
> > threadlocal, but it's interesting, so much talk on this list about
> > threadlocals, but they threadlocals seem to be used by many
> > implementations/software out there. Not naming any names. :)
>
> It's not a ThreadLocal, it just wraps the request so that accesses of
> the HttpSession can have WeakReferences transparently wrapped-around
> the actual objects. I did it to a) avoid having to re-write a bunch of
> code when I wanted to use WeakReferences and b) avoid having to have
> that ugly code visible in my actual application code. Since it's an
> orthogonal feature (e.g. the application code doesn't have to know
> there is an expirable-cache in play... it just knows that it's
> recoverable for an object *not* to be available), it's a perfect job
> for a Filter an associated wrapper objects.
>
>
very interesting! i will have to consider doing that at some point. i'm
taking in a lot what you and others recommend and try to apply to my app,
at my earliest convenience, or slowly-but-surely. :)



> - -chris
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
> Comment: GPGTools - http://gpgtools.org
> Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
>
> iQIcBAEBCAAGBQJRbWC/AAoJEBzwKT+lPKRY88kP/1jOt9yEHNNJy0b4fcmrNcK8
> 1mB4DADmvNoW5F+xI56YxjZ3wLP8GK4hkOHRz82eED9qpiCnIvSEfO4mdSFNnVfq
> CJOFYNnBmdbDxPea9K52VjJ6lenjN5+gOggrJB1LuImP359pmkW3Xdv/q89OSrph
> Q9xf1VPeohv6ANv2eOWZ4zC5L+28LLmkWqJXajfw940MOvBTiSmKi1/Zz9hCuGOQ
> XG+QkSxcGUnP2cWqQKkkuIUGOR8iTcwy00STI/i9QxruBooUcMqTBQ4v9YcqTGoN
> FXV1mLiIM3oG36XickblLyCAK60Qtvx1PaKAvmFM30XeKAcS2gRmglkDM6yYG311
> 2TUzHytYVEnZm/6Wet+2VmMdEVqjhwRFOTcegs5VhC6KBoUSJ6W/xNugfJam0DHv
> 3dF9mZy/6ecL3KKY3c+7hJcQ6b9F79J7MksCvf8YGff+k/Br3Vme9VVzUfEcy572
> i6CuAXb9X5uSC/vr1yincyqfNAZoPrabkNExqW8/tGd1YGky92DG/bRxMCxEUlMn
> S6k8av3ZXv+neF3kNfkLgynvxD/2MWyK1cO5Q61VZ7jkRx3AqWFcHx3sImh9FW5N
> vwVTd7jb8N3gBYegTfeWFN8fGaMp4bLWlpSAoyuvB/M0OXQ+GW3GDasVqt0zxBct
> 0a25Y6XUHv7bxLPKGNWK
> =nLK3
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
>
>

Reply via email to