Saturday, September 7, 2013

Understanding ADF Task Flow Page Flow Scope Lifetime

I would say its a bit confusing to understand - how long objects stored in Page Flow Scope really reside in memory. This is a reason I implemented small sample application and did a test. Summary for the results (see detailed description below):

1. Every ADF task flow instance is granted with its own Page Flow Scope

2. Page Flow Scope is not destroyed when you navigate away from the task flow, unless you use Task Flow Return activity and finalizer is invoked

3. You can access previously left Page Flow Scope only by using Task Flow Return activity

This allows me to presume that is not really good to have many small ADF task flows in the system, as theoretically there will be a lot of wasted Page Flow Scope entries, especially when navigating without retuning back. On contrary, when using larger ADF task flows and less of them - there will be less Page Flow Scope memory wasted - since less different Task Flows opened. On other hand, you should not implement just one big task flow - there must be proper reuse on design time, proper architecture requires clever separation into task flows.

There is nothing wrong in using multiple small ADF task flows, in this post I'm pointing out about lifetime of Page Flow Scope. You should clearly understand - Page Flow Scope is not destroyed when navigating away from ADF task flow. You should use shared Data Control as much as possible and avoid storing too much of temporary data into Page Flow Scope and pass it across different ADF task flows.

This is very true for UI Shell type of architecture - many task flows are opened from the menu, without really closing them. You should be aware of this and store variables in Page Flow Scope when its necessary only.

Sample application is available for download - PageFlowScopeApp.zip. It implements two ADF task flows. Task flow A contains one fragment, task flow B call and return activity:


Task flow B contains one fragment, return activity. There is one trick here - task flow B contains a call to calling task flow A. This is done on purpose, to simulate second instance for the task flow A:


Task flow A defines input parameter - backRendered. This is needed to distinguish, if task flow instance is opened from task flow B or no:


Task flow A call from task flow B sets parameter value:


Based on this parameter value, we can render components conditionally in task flow A fragment. For example, Back button is hidden if parameter is not set (which means A task flow instance was created not through a task flow call from B):


Below are the steps for the test. Task flow A is loaded and fragment is rendered, here user types some text and saves it in Page Flow Scope:


Press Navigate button to open instance of task flow B:


Page Flow Scope for task flow B is created now. You are in the task flow B Page Flow Scope, set text here also:


Press Next to open second instance of task flow A, new Page Flow Scope is created:


In the second instance of task flow A - type different text, not the same you was typing while in the first instance initially:


You should notice Back button rendered, this is because of passed parameter. Now when different text was set, what would you expect to see when coming back to the first instance of the same task flow? As every task flow instance maintains its own Page Flow Scope - you should expect to see original text. Press Back button - this will navigate to calling task flow B:


Task flow B still displays previously set text - this means when you navigate away from task flow - page flow scope is not lost and remains in memory. You should keep this in mind when designing ADF task flows, implementing your forms - make sure you don't store garbage in Page Flow Scope - this may waste memory, especially when having many ADF task flows and navigating between them, without returning or opening from UI Shell. Page Flow Scope from task flow B keeps its value, press Back button to navigate to the first instance of task flow A:


Task flow A keeps its original Page Flow Scope - text value is saved and displayed:


This means - we can return back through task flow return activity and access previously initialised Page Flow Scope - it is not gone.

If you press Navigate button in the task flow A and go to task flow B - new instance of task flow B will be created with new Page Flow Scope:


If you open task flow A by task flow call from task flow B - new instance is created for task flow A, new Page Flow Scope is initialised:


Navigate back to the task flow B and from task flow B to the original task flow A instance:


You will see that Page Flow Scope of original first task flow A instance is available:

Summary - it seems like Page Flow Scope is nothing more as more advanced Session Scope extended with Hash Map. This Hash Map contains references to Task Flow instances, entries in the Hash Map can be cleared only when calling Task Flow Return activity.

21 comments:

Anonymous said...

Nice Post.

So if we are not returning back to previous task flow or if we don't need previous values in page flow scope at all, then is it enough to call task flow Finalizer and clear page flow scope ?

Thanks

Andrej Baranovskij said...

Thanks for your comment.

Not really - as TF finalizer is invoked only when TF return activity is executed. So, if you call another TF through TF call and never execute task flow return activity - page flow scope of such TF will not be cleared.

If you use UI Shell and open TF's from the menu, page flow scope will not be cleared either. Because loaded TF's are not really closed, when navigating between menu as task flow return activity not invoked.

Andrejus

Anonymous said...

Hi Andrejus
If we used UI Shell type of architecture how we can navigate to new menu and close opened TF ? Do you have solution for that?

Chris Muir said...

Hi Andrejus

Can I seek some clarifications on your post here please as the post appears to be addressing a preconceived concern and it's not clear where that comes from. My concern is our documentation misleads customers so I'd like to ask further questions to see if we've poorly communicated something that's lead to your post.

As such regarding your comment:

"2. Page Flow Scope is not destroyed when you navigate away from the task flow, unless you use Task Flow Return activity and finalizer is invoked"

When you define "navigate away" do you mean by a task flow call or some other mechanism? I assume you mean by a task flow call.

Based on this assumption is there a place the documentation says (incorrectly) pageFlowScope *is* destroyed on a task flow call? As you know task flows calling task flows is just like functions in Java calling functions. It's a stack and state is maintained in the stack.


On your further comment:

"This allows me to presume that is not really good to have many small ADF task flows in the system, as theoretically there will be a lot of wasted Page Flow Scope entries, especially when navigating without retuning back. On contrary, when using larger ADF task flows and less of them - there will be less Page Flow Scope memory wasted - since less different Task Flows opened."


If you had one large task flow that referred to pageFlowScope entries A and B, the total memory entries is 2.

If you then broke that task flow into 2 sequential task flows, X and Y, X calls Y. At the point task flow Y is reached at runtime, task flow X uses item A and task flow Y uses item B.

In both these task flow scenarios, one large task flow, vs two small task flows called in sequence, the same number of entries is referenced in both task flow scenarios. So how is *less* memory wasted? Arguably it's the same.

Further if during the execution of task flow X task flow Y is never called thanks to some logic decision, entry B for task flow Y is never instantiated. Relatively speaking to one large task flow, for our smaller task flow solution this results in a memory saving as B is never instantiated.

(Arguably the programmer of large task flow could ensure ensure entry B is never instantiated unless referenced).

So how is the smaller task flow scenario not more efficient than the large task flow scenario?

Finally how can we articulate this better in the documentation? Is there a location this could be stated?

Looking forward to your input.

Thanks & regards,

CM.

Andrej Baranovskij said...

Hi Chris,

Thanks for you feedback. I will follow up soon.

Andrejus

Andrej Baranovskij said...

Hi Chris,

1. I don't want to point out that something is working wrong with Page Flow Scope. I'm trying to define what it means to "close" task flow, and it seems like task flow is closed and page flow scope cleared only when Return activity is called. Still, I'm not sure if Page Flow Scope is really cleaned, when calling task flow Return activity without calling Finalizer.

2. About memory consumption. My point is when entries A and B must be copied across task flows X and Y. Imagine having large task flow using A and B across fragments (this happens quite a lot as I can see), we split it into two task flows X and Y, so we need to define both A and B inside two task flows and copy values across. Yes, there is Shared scope for Data Control, but I'm talking about situation when developer using Page Flow Scope to keep values, or when task flow is configured in Isolated Scope and developer needs to pass parameters. So, if navigating to Y and never coming back - values from X will be never used again and will stay in memory.

I'm not against smaller task flows at all, I want to point out that developers should think wisely when storing too much of variables into Page Flow Scope. I think there should be a statement in the docs, mentioning this. As there is misunderstanding that Page Flow Scope exists only until current task flow is open and destroyed automatically when navigating away (calling Return Activity and another Task Flow call)

Andrejus

Andrej Baranovskij said...

Hi Anonymous,

I dont think you can close task flow, when it is opened in UI Shell. You should avoid using too much of variables in Page Flow Scope.

Andrejus

Chris Muir said...

Hi Andrejus

1. "Still, I'm not sure if Page Flow Scope is really cleaned, when calling task flow Return activity without calling Finalizer."

I think what you meant to say here is I'm not sure Page Flow Scope bean is cleaned if we don't call the task flow return activity (and the finalizer by inference).

Assuming my interpretation of what you meant to say is correct, given that a timeout or prematurely terminated task flow should normally catch this scenario, resulting in the task flow(s) being finalized and in the resources being marked as candidate for cleanup by the GC, can you outline any problem scenarios where you've not seen this work? Again I'd like to log bugs if you have discovered this as it will result in a memory leak that is outside control of the developer and certainly wouldn't be desirable behaviour for ADF.

2. "I'm not against smaller task flows at all, I want to point out that developers should think wisely when storing too much of variables into Page Flow Scope."

Agreed. So it's really a post about what developers *store* in page flow scope as separate to the number of page flow scope beans themselves or the size of the BTFs.

I'd suggest that you might want to make that a little clearer in your original post in paragraph 5. It would be unfortunate if a bunch of customers took this at face value and started using large BTFs everywhere thinking there was a major inefficiency in using small BTFs.

Thanks & regards,

CM.

Andrej Baranovskij said...

Hi Chris,

1. Yes, Page Flow Scope bean or simply variable in Page Flow Scope. No bugs for this, we are testing it with JRockit Mission Control, but nothing suspicious. My goal was to point out that Page Flow Scope is not destroyed instantly, when navigating away from Task Flow, as some developers are under wrong impression - Page Flow Scope is destroyed when navigating away

2. Correct, we are on the same page here. I have updated this post to include clearer statement next to chapter 5.

Andrejus

Madhu Krishna said...

Is there an other way for not using the page flow scope variables?
We dont use shared data control for our application.
We use dynamic ui shell in our application.
We have seen the page flow scope variable getting lost between tabs.

Andrej Baranovskij said...

Sure it will be lost between tabs, page flow scope is per task flow - when you navigate to other tab, other tab will have its own page flow scope. if you return back to the first tab, previously initialized page flow scope will be available.

Andrejus

Unknown said...

Thanks Andrejus,

So can we understand it as PageFlowScope bean will cause memory leak if the task flow is not returned (and thus destroyed) ?

As we are using Portal for our application, it is very common case that user will simply navigate away from the taskflow without calling "Return activity".

Thank you very much.

Andrej Baranovskij said...

Yes, it could be a case, but it needs testing. You could test it in simple way - add task flow finalizer methods, put some println logger message into task flow finalizer. Run application and navigate across Task Flow, as your users are navigating. Check how many times finalizer will be invoked. If you will miss finalizer invocations, this means these Task Flows were simply navigated away and Page Flow Scope still remains in memory.

Andrejus

AM said...

If pageFlow is at taskflow instance level, will two instances of a taskflow on the same page have their own separate pageFlow scope maps? Also, can you suggest how we can test this scenario?

Thanks,
AM

Gary said...

Hi Andrejus,
Is there a way where in we can access taskflow A's pageflowscope varaibles in Taskflow B ?

Andrej Baranovskij said...

No, this is protected by design.

Andrejus

Unknown said...

Hi Andrejus

I think we just hit that issue. We have a BPM process with human task. The human task is "served" by a Taskflow. Let say we have 20 instances of this process. From the BPM Workspace (where you can see all human tasks) the user starts opening one human task after another (The workspace is NOT calling taskflow finalazer when switching tasks). This results in user session size growing (memory leak).
Are you aware of this kind of issue. We do not have access to BPM Workspace app in order to make it call the finalizer in that case.

Thanks!
Angel

Andrej Baranovskij said...

Possible. You can customize BPM Workspace Task Flows with MDS and change the logic.

Regards,
Andrejus

Arun said...
This comment has been removed by the author.
Unknown said...

hi Andrejus,

could you please upload the attached file again because its not working

Andrej Baranovskij said...

You can download all old samples from Google Archive: https://code.google.com/archive/p/jdevsamples/downloads

Andrejus