Tuesday, October 22, 2013

Reproducing WebLogic Stuck Threads with ADF CreateInsert Operation and ORDER BY Clause

This is a second post related to WebLogic Stuck Threads. You can read the first one - Evil Behind ChangeEventPolicy PPR in CRUD ADF 12c and WebLogic Stuck Threads. In the previous post, I was describing how to reproduce WebLogic Stuck Thread in ADF 12c, with ChangeEventPolicy = PPR. As per Steve Muench follow up comment, PPR works well, if you are using auto populated primary key. Today post is about something different, reproducible across all ADF versions - WebLogic Stuck Thread in relation to CreateInsert operation and ORDER BY clause usage for VO.

Here you can download test case application - LargeFetchApp_v2.zip. Make sure to set FINEST ADF Logger level for Regions VO Impl class, this will print fetched rows:

Make sure to disable AM Pooling, this will simulate stress test environment, passivation/activation will be happening on every request:

Region ID is set with default value, this is used to make sure new row gets primary key always set:

We set ORDER BY clause for REGION_ID and use DESC operator. This will bring rows with higher numbers first and display them in the top of ADF table. Make sure to use populateTable method (described in the previous post mentioned above) to insert 10 000 rows into REGIONS table:

Press Create button - new row with ID = 7 will be created, not inserted into DB yet:

Use Save button - to finally insert record into DB:

This is important step, new record will be inserted as last record in DB table, however UI displays it in first position. There is ORDER BY DESC set for this VO.

Try to navigate to other tab or do any other request, after record was saved to DB:

You will see in the log 10 000 rows fetched, until row with ID = 7 is found:

This large fetch is so unexpected and causes WebLogic Stuck Threads when multiple users are using application. The reason it fetches suddenly so many records, because new record with ID = 7 is displayed as the first row, but really this row is not retrieved with first range size of 25 top rows, it comes much later by ORDER BY DESC clause logic.

If we would insert new record with a key 10 100, large fetch would not be reproduced, as this row would be qualified to come with first range size of 25 rows.

We change to ASC order and test it again. Remove newly inserted record with ID = 7 from DB:

ORDER BY clause is updated with ASC order, instead of previous DESC - records with lower ID number will appear first:

Repeat the same steps as before, create new row - ID for the key attribute will be set automatically and equal 7:

Save and insert new row to the DB:

New row with ID = 7 is displayed on top, as it was just inserted, but still it will made into range size of the first 25 rows. We can see it from the log, it will 5th row fetched for the first range size:

As new row will be located early in the first range size, there will be no large fetch happening - application will continue to run fast:

I will describe in my next post, how to implement effective CRUD in ADF, taking into account large fetches. Generally this behaviour should be improved in ADF, instead of fetching entire collection of rows to locate needed row - it could do single fetch by key for a row not located in the first range size.


codeplay said...

I hv similar experience, the activateCurrentRow method fetches all rows during VO activation (the row has generated fixed PK)

When this kind of prob occurs, we'll firstly be blamed not hving fixed PK (this is understandable), after we ensure we hv fixed PK, we'll be blamed not hving a view criteria to limit the row count. Since it's ADF designed approach, it should be highlighted in the developer guide which I believe is not, otherwise, the impression is that there're too many traps.

Andrej Baranovskij said...

Yes, I agree with you. ADF is huge framework designed for different use cases, its why not everything is documented, as in one use case such behaviour might be natural and for other cases - not. It all depends on case per case.

By the way, your described passivation/activation case is on my list for the next blog posts.