Thursday, October 17, 2013

Evil Behind ChangeEventPolicy PPR in CRUD ADF 12c and WebLogic Stuck Threads

With this post I'm starting to prepare for our UKOUG'13 conference sessions. You can attend two of our sessions on UKOUG'13 Super Sunday, December 1st. These sessions are scheduled to run immediately one after another, so we are going to have two straight hours to discuss topics around ADF - ADF Anti-Patterns: Dangerous Tutorials, ADF Development Survival Kit. You should stop by to say hi, me and my colleague Florin Marcus, will be happy to answer any technical question about ADF.

Today post topic will be covered in the first session - ADF Anti-Patterns: Dangerous Tutorials. If you know how to implement CRUD functionality, you might be surprised - there are more things to know. I will describe one issue specifically related to ADF 12c, in the next post will present scenario reproduced in ADF 11g.

In your production application, you may experience WebLogic Stuck Threads. This is usually related to the large fetching, in most cases it happens unexpected and is not reproduced easily. In ADF 11g this is related to AM passivation/activation behaviour (will be described more in the next post), in ADF 12c I found another reason for unexpected large fetch in CRUD - ChangeEventPolicy = PPR setting usage for ADF iterator in Page Definition.

Here you can download fixed CRUD application for ADF 12c - LargeFetchApp.zip. This applications provides to methods, to insert 10000 rows into Regions table and remove the same rows. This is important to reproduce the error - it is easily reproduced when there are more records in the DB. Run populateTable method to insert 10000 rows into Regions table:


After running populateTable method, around 10000 rows should be available in the DB:


Regions VO implementation class contains overridden method for createRowFromResultSet. This method tracks every row fetched through this VO and reports it to the log:


Double check Regions iterator properties in Page Definition:


You will see ChangeEventPolicy = ppr, is set by default:


Run test application (make sure ChangeEventPolicy = ppr), press Create button - you will experience 15 to 30 seconds delay, before blank row will show up:


This is easily reproduced, if table is large enough (for example 10000 rows or more), as it was generated with populateTable method. To see ADF log output for Regions VO implementation class, make sure to set FINEST level in ADF logger config:


You will see lots of rows fetched during CreateInsert operation invocation, around 40000 rows fetched (means duplicates, as there are 10000 rows only in the table). This explains why Create is so slow, and this is by default in ADF 12c. It makes so many findByKey method calls:


In production system, when multiple users are doing same operation and even more data in the table - this generates Stuck Threads on WebLogic server and finally application stops. Simply because it consumes too much memory to create huge rowsets for all the data fetched from the DB.

You should change ChangeEventPolicy = none, this prevents unexpected large fetch on CreateInsert:


New row will be inserted instantly:


General recommendation - you should avoid using default setting for ChangeEventPolicy = ppr, it creates too many side effects usually. Seems like this functionality is not well tested yet.

14 comments:

  1. Great Information Andrejus.

    But changeEventPolicy needs to be PPR if we want backend/BC changes to be reflected in UI. So this should be good only when iterator is used for create operation and not always Right?

    ReplyDelete
  2. No, its not needed at all. You should set AutoSubmit=true and PartialTrigger dependency in the fragment/page directly, without using changeEventPolicy=PPR for iterator in Page Def. This is how sample application is created.

    This is good for all cases, not only limited for create. In my opinion changeEventPolicy=PPR is not well tested and should not be used at all.

    Andrejus

    ReplyDelete
  3. Thanks for valuable information Andrejus.

    ReplyDelete
  4. In combination with AutoSubmit=true, having ChangeEventPolicy=ppr is the only way your UI can be automatically updated based on backend data changes. If you have the luxury of knowing every possible combination of which field-value modifications will result in which other field's values changing, then you could theoretically account for all of those combinations using manually-configured partial targets, however in general your UI layer won't know what backend logic may result in what data changes. The ChangeEventPolicy=ppr leverages the built-in data-change-notification in ADFBC to automatically cooperate with ADF Faces and ADF Model binding layer to add the UI component id's to the partial targets list for any UI components bound to data that gets modified, no matter how it gets modified.

    Furthermore, consider a future scenario where the application you developer and deliver (perhaps in the cloud) is able to be customized by a special "business admin" user at each customer site. They might be able to add custom business logic, custom calculated fields, or other artifacts that would result in attributes being changed in a way that your own out-of-box application logic could not possibly have foreseen. So, ChangeEventPolicy=ppr is really your friend in these cases.

    For this issue, given that ADF Faces tables are a bit grumpy and ill-behaved with row-data that has null or changing primary key values, I'd recommend ensuring that the RegionId in your example app is populated at row create time so that ADF Faces will always have a well-defined primary key, and futher I suggest that in any ADF Faces table you don't allow the end-user to edit the primary key. Use a system-generated key and allow the user to edit some alternative primary key value that is not the real primary key attribute under the covers.

    ReplyDelete
  5. Hi Steve,

    Thanks for your clear technical answer. This is clear now, empty row key for the reason for PPR mode strange behaviour.

    I like your strong message about auto-generated primary key (not allowing users to edit it), I seen many issues related to this (and one described in this post, is one of such issues).

    I will use it as argument against user editable primary keys.

    Regards,
    Andrejus

    ReplyDelete
  6. Just a quick one, is there a chance the changeeventpolicy value can be globally set at one point in an application? Say the adfc-config, web.xml or databinding file? So all pages with default changeeventpolicy inherit such settings?

    ReplyDelete
  7. Yes, you can do this in adf-config.xml file - check in ADF META-INF folder. By default Change Event Policy is set to PPR.

    Andrejus

    ReplyDelete
  8. Hi Andrejus, I think I am misisngg a very basic point here since otherwise this question would have been raised by now. createRowFromResultSet is a callback for executeQuery(), can you clarify why/how it is being called when you create a new row? Is it because the iterator binding changeEventPolicy was ppr? Using jdev 12c, I tried a simple, similar app for HR.regions table with createInsert, and when I add a new row as expected createRowFromResultSet wasn't called in the flow (didn't change changeEventPolicy default value of ppr).

    Regards,
    Amitabh

    ReplyDelete
  9. Hi,

    createRowFromResultSet is called to create new row in the rowset collection. Whenever new row is fetched/inserted into rowset - this method should be called automatically.

    Andrejus

    ReplyDelete
  10. I have tested a bit, and found the following.

    It seems createRowFromResultSet is called when changeEventPolicy is ppr, but not when it is none (as ppr happens). Apparently it's not called for the new row being inserted into the rowset. Has your observation been the same?

    Also, with changeEventPolicy 'ppr', when new row is created this method is not called 40000 odd times if we set the RegionId during row creation as Steve suggested; but it's called when we save the new row (and I found nearly 25 (rangeSize) records being fetched).

    Regards,
    Amitabh

    ReplyDelete
  11. Hi,

    I would need to test your 1st statement. For the 2nd statement yes, you need to pre-set key attribute - see my answers above. In line with Steve's answers.

    Andrejus

    ReplyDelete
  12. Correction in my previous comment: The first line should have read : "it seems createRowFromResultSet is called when changeEventPolicy is ppr, but not when it is none (as ppr DOES NOT happen)".

    Thanks,
    Amitabh

    ReplyDelete
  13. OK, I plan to test it tomorrow.

    Regards,
    Andrejus

    ReplyDelete
  14. I know, it is a long time since the original post, but this might be a possible solution for some cases: http://padora.blogspot.de/2016/04/handling-very-slow-execution-of.html

    ReplyDelete