You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Malcolm Edgar <ma...@zurich.com.au> on 2006/03/27 04:58:13 UTC

DataContext scope in web apps

Hi All,

I am finding that with the Cayenne web app design pattern where the 
DataContext has session scope, it is easy to add objects to the 
DataContext when building up an object graph for display. If these objects 
aren't explicity committed by request it is easy to have them carried 
forward in the session DataContext, and have them committed in a 
subsequent but unrelated request.

While though careful programming I think I can avoid such issues, but I am 
looking for a more easier way of dealing with this. A couple of concepts 
include:

1. Using a request scope DataContext

With this I was thinking of a DataContext servlet filter which creates a 
new thread local DataContext for each request.  If the user does not 
explicitly commit changes in the data context, at the end of the request 
it will go out of scope and be garbage collected.

Issues with this appoarch could be performance cost of creating a 
DataContext for each request (I dont know if this is an issue), and the 
loss of the session scope  caching benefits the DataContext provides.

2. Using nested DataContext with a request scope

The other idea is to create a nested DataContext from the parent session 
DataContext and bind this current thread. I imagine this would give you 
the benefits of session scope caching, but still enable you to throw away 
uncommitted data context objects at the end of the request.  I don't know 
if it is any faster to create nested data contexts.

Does anyone have any experience with these patterns or have any comments?

regards Malcolm Edgar




----
This email is intended for the named recipient only. It may contain information which is confidential, commercially sensitive, or copyright. If you are not the intended recipient you must not reproduce or distribute any part of the email, disclose its contents, or take any action in reliance. If you have received this email in error, please contact the sender and delete the message. It is your responsibility to scan this email and any attachments for viruses and other defects.
To the extent permitted by law, Zurich and its associates will not be liable for any loss or damage arising in any way from this communication including any file attachments. We may monitor email you send to us, either as a reply to this email or any email you send to us, to confirm our systems are protected and for compliance with company policies. Although we take reasonable precautions to protect the confidentiality of our email systems, we do not warrant the confidentiality or security of email or attachments we receive.

Re: DataContext scope in web apps

Posted by Cris Daniluk <cr...@gmail.com>.
We also do this (automatically via a ServletFilter, interceptor, etc). In
the pre-nested DataContext world, if I have a page that spans multiple
requests, I create a new DataContext that does not automatically rollback
each request, and bind that to the thread, rather than the session default.
A token in the form links the flow to the new DataContext. Then, if the user
somehow gets objects in a bad state and goes on to other parts of the
application, the clean DataContext is used. A wrapper object for the Action
or whatever seemlessly takes care of this when a token is present.

I imagine with nested contexts, I would do something similar - it is even
conceivable to me to do all write operations in a nested DataContext,
regardless of how many requests it may take. I haven't explored the new code
in depth, but such a design should let you delegate the actual db
commit/rollback to a separate processor within your application, opening up
all sorts of interesting possibilities.

Cris

On 3/27/06, Bryan Lewis <br...@maine.rr.com> wrote:
>
> Andrus Adamchik wrote:
>
> > 3. Another possibility - if you never ever carry uncommitted state
> > across requests, you can setup a filter that does
> > DataContext.rollbackChanges() at the end of the request. This is a
> > variation of the request-scope context, only preserving caching
> > benefits.
>
> We do a similar thing in our Tapestry apps, mimicking what we used to do
> in WebObjects.  Every time a new page is requested -- our base Page
> class provides a getPage(name) method -- we do the rollbackChanges().
> We seldom have workflow that spans more than one page.
>
>

Re: DataContext scope in web apps

Posted by Bryan Lewis <br...@maine.rr.com>.
Andrus Adamchik wrote:

> 3. Another possibility - if you never ever carry uncommitted state 
> across requests, you can setup a filter that does 
> DataContext.rollbackChanges() at the end of the request. This is a 
> variation of the request-scope context, only preserving caching 
> benefits.

We do a similar thing in our Tapestry apps, mimicking what we used to do
in WebObjects.  Every time a new page is requested -- our base Page
class provides a getPage(name) method -- we do the rollbackChanges(). 
We seldom have workflow that spans more than one page.


Re: DataContext scope in web apps

Posted by Mike Kienenberger <mk...@gmail.com>.
On 3/27/06, Mike Kienenberger <mk...@gmail.com> wrote:
> I'm happy to post the code if anyone's interested.  It depends only on
> Cayenne and javax.servlet classes.

Since there's been some interest in this, here is a copy of my
DataContextManagerFilter.java class.

Re: DataContext scope in web apps

Posted by Mike Kienenberger <mk...@gmail.com>.
On 3/27/06, Andrus Adamchik <an...@objectstyle.org> wrote:
> 3. Another possibility - if you never ever carry uncommitted state
> across requests, you can setup a filter that does
> DataContext.rollbackChanges() at the end of the request. This is a
> variation of the request-scope context, only preserving caching
> benefits.

This is how I'm doing it as well.

I have a session-scoped DataContext and I assign it to thread-local in
my first servlet filter.   At the post-processing stage (insuring that
it's also the last piece of code executed per request), I remove the
thread-local, and I validate that the DataContext has no new objects,
no modified objects, and no deleted objects.   If it does, I log each
object as an error and rollback the context.   For now, I also throw
an error since this is still in the dev stages and it's a bug to be in
this state.

I'm happy to post the code if anyone's interested.  It depends only on
Cayenne and javax.servlet classes.

Re: DataContext scope in web apps

Posted by Andrus Adamchik <an...@objectstyle.org>.
Ok, makes sense.

> Adding an auto rollback option to the Cayenne web filter be a good, as
> would a topic should in the Wiki doco.

Agreed. We may add this to the standard Cayenne filter, with the  
rollback flag flipped on or off via the init-param in web.xml.

Andrus


On Mar 29, 2006, at 9:50 AM, Malcolm Edgar wrote:

>
> Hi Andrus,
>
> the use case is, I have a new User data object which has a Address  
> data
> object property:
>
> public Address extends CayenneDataObject {
>     ..
> }
>
> public User extends CayenneDataObject {
>     public getAddress(); { .. }
>     public setAddress(Address address) { .. };
>     ..
> }
>
> I am using a CayenneForm to create a new User and Address object in  
> one
> post. To add the Address object to the User object, the User object  
> must
> be registered with the DataContext for the relationship lookups.
>
> With an invalid post all the submitted values must be validated before
> creating a new User object, otherwise an invalid User object is  
> added to
> the session DataContext.
>
> I think the approach people advocate of always rolling back at the  
> end of
> each request sounds like a good practice for most use cases. The
> multi-page workflows could be handled by a separate DataContext  
> session.
>
> Adding an auto rollback option to the Cayenne web filter be a good, as
> would a topic should in the Wiki doco.
>
> regards Malcolm Edgar
>
>
>
>
> Andrus Adamchik <an...@objectstyle.org>
> 27/03/2006 05:32 PM
> Please respond to
> cayenne-user@incubator.apache.org
>
>
> To
> cayenne-user@incubator.apache.org
> cc
>
> Subject
> Re: DataContext scope in web apps
>
>
>
>
>
>
> Hi Malcolm,
>
> I understand your question is also somewhat related to CAY-483 issue
> that you opened recently.
>
>> I am finding that with the Cayenne web app design pattern where the
>> DataContext has session scope, it is easy to add objects to the
>> DataContext when building up an object graph for display.
>
> Can you give an example. How can you add a new *persistent* object
> that you don't want to commit? Is it only due to a programming error?
>
>> 1. Using a request scope DataContext
>>
>> With this I was thinking of a DataContext servlet filter which
>> creates a
>> new thread local DataContext for each request.  If the user does not
>> explicitly commit changes in the data context, at the end of the
>> request
>> it will go out of scope and be garbage collected.
>>
>> Issues with this appoarch could be performance cost of creating a
>> DataContext for each request (I dont know if this is an issue), and
>> the
>> loss of the session scope  caching benefits the DataContext provides.
>
> That's a possibility. There is very little overhead in new context
> creation. There may (or may not) be a performance degradation due to
> the loss of DataContext-level caching. This is application dependent.
> Context cache saves you from doing extra DB trips for previously
> resolved to-one and to-many relationships... also for the cached
> queries if you use them.
>
>
>> 2. Using nested DataContext with a request scope
>>
>> The other idea is to create a nested DataContext from the parent
>> session
>> DataContext and bind this current thread. I imagine this would give
>> you
>> the benefits of session scope caching, but still enable you to
>> throw away
>> uncommitted data context objects at the end of the request.  I
>> don't know
>> if it is any faster to create nested data contexts.
>
> As you know nested DataContexts is a new feature, so we don't have
> any empiric data on its performance (would love to get your
> feedback). Performance overhead it adds is due to the fact that
> select and commit operations have to travel through an extra
> processing point in the stack. I would say it is appropriate for an
> editor form that supposedly accesses/modifies no more than a few
> [dozens] of objects; and not appropriate for a search page that may
> access thousands of objects.
>
> 3. Another possibility - if you never ever carry uncommitted state
> across requests, you can setup a filter that does
> DataContext.rollbackChanges() at the end of the request. This is a
> variation of the request-scope context, only preserving caching
> benefits.
>
> Andrus
>
>
>
>
>
>
>
> ----
> This email is intended for the named recipient only. It may contain  
> information which is confidential, commercially sensitive, or  
> copyright. If you are not the intended recipient you must not  
> reproduce or distribute any part of the email, disclose its  
> contents, or take any action in reliance. If you have received this  
> email in error, please contact the sender and delete the message.  
> It is your responsibility to scan this email and any attachments  
> for viruses and other defects.
> To the extent permitted by law, Zurich and its associates will not  
> be liable for any loss or damage arising in any way from this  
> communication including any file attachments. We may monitor email  
> you send to us, either as a reply to this email or any email you  
> send to us, to confirm our systems are protected and for compliance  
> with company policies. Although we take reasonable precautions to  
> protect the confidentiality of our email systems, we do not warrant  
> the confidentiality or security of email or attachments we receive.


Re: DataContext scope in web apps

Posted by Malcolm Edgar <ma...@zurich.com.au>.
Hi Andrus,

the use case is, I have a new User data object which has a Address data 
object property:

public Address extends CayenneDataObject {
    ..
}

public User extends CayenneDataObject {
    public getAddress(); { .. }
    public setAddress(Address address) { .. };
    ..
}

I am using a CayenneForm to create a new User and Address object in one 
post. To add the Address object to the User object, the User object must 
be registered with the DataContext for the relationship lookups.

With an invalid post all the submitted values must be validated before 
creating a new User object, otherwise an invalid User object is added to 
the session DataContext.

I think the approach people advocate of always rolling back at the end of 
each request sounds like a good practice for most use cases. The 
multi-page workflows could be handled by a separate DataContext session.

Adding an auto rollback option to the Cayenne web filter be a good, as 
would a topic should in the Wiki doco.

regards Malcolm Edgar




Andrus Adamchik <an...@objectstyle.org> 
27/03/2006 05:32 PM
Please respond to
cayenne-user@incubator.apache.org


To
cayenne-user@incubator.apache.org
cc

Subject
Re: DataContext scope in web apps






Hi Malcolm,

I understand your question is also somewhat related to CAY-483 issue
that you opened recently.

> I am finding that with the Cayenne web app design pattern where the
> DataContext has session scope, it is easy to add objects to the
> DataContext when building up an object graph for display.

Can you give an example. How can you add a new *persistent* object
that you don't want to commit? Is it only due to a programming error?

> 1. Using a request scope DataContext
>
> With this I was thinking of a DataContext servlet filter which
> creates a
> new thread local DataContext for each request.  If the user does not
> explicitly commit changes in the data context, at the end of the
> request
> it will go out of scope and be garbage collected.
>
> Issues with this appoarch could be performance cost of creating a
> DataContext for each request (I dont know if this is an issue), and
> the
> loss of the session scope  caching benefits the DataContext provides.

That's a possibility. There is very little overhead in new context
creation. There may (or may not) be a performance degradation due to
the loss of DataContext-level caching. This is application dependent.
Context cache saves you from doing extra DB trips for previously
resolved to-one and to-many relationships... also for the cached
queries if you use them.


> 2. Using nested DataContext with a request scope
>
> The other idea is to create a nested DataContext from the parent
> session
> DataContext and bind this current thread. I imagine this would give
> you
> the benefits of session scope caching, but still enable you to
> throw away
> uncommitted data context objects at the end of the request.  I
> don't know
> if it is any faster to create nested data contexts.

As you know nested DataContexts is a new feature, so we don't have
any empiric data on its performance (would love to get your
feedback). Performance overhead it adds is due to the fact that
select and commit operations have to travel through an extra
processing point in the stack. I would say it is appropriate for an
editor form that supposedly accesses/modifies no more than a few
[dozens] of objects; and not appropriate for a search page that may
access thousands of objects.

3. Another possibility - if you never ever carry uncommitted state
across requests, you can setup a filter that does
DataContext.rollbackChanges() at the end of the request. This is a
variation of the request-scope context, only preserving caching
benefits.

Andrus







----
This email is intended for the named recipient only. It may contain information which is confidential, commercially sensitive, or copyright. If you are not the intended recipient you must not reproduce or distribute any part of the email, disclose its contents, or take any action in reliance. If you have received this email in error, please contact the sender and delete the message. It is your responsibility to scan this email and any attachments for viruses and other defects.
To the extent permitted by law, Zurich and its associates will not be liable for any loss or damage arising in any way from this communication including any file attachments. We may monitor email you send to us, either as a reply to this email or any email you send to us, to confirm our systems are protected and for compliance with company policies. Although we take reasonable precautions to protect the confidentiality of our email systems, we do not warrant the confidentiality or security of email or attachments we receive.

Re: DataContext scope in web apps

Posted by Andrus Adamchik <an...@objectstyle.org>.
Hi Malcolm,

I understand your question is also somewhat related to CAY-483 issue  
that you opened recently.

> I am finding that with the Cayenne web app design pattern where the
> DataContext has session scope, it is easy to add objects to the
> DataContext when building up an object graph for display.

Can you give an example. How can you add a new *persistent* object  
that you don't want to commit? Is it only due to a programming error?

> 1. Using a request scope DataContext
>
> With this I was thinking of a DataContext servlet filter which  
> creates a
> new thread local DataContext for each request.  If the user does not
> explicitly commit changes in the data context, at the end of the  
> request
> it will go out of scope and be garbage collected.
>
> Issues with this appoarch could be performance cost of creating a
> DataContext for each request (I dont know if this is an issue), and  
> the
> loss of the session scope  caching benefits the DataContext provides.

That's a possibility. There is very little overhead in new context  
creation. There may (or may not) be a performance degradation due to  
the loss of DataContext-level caching. This is application dependent.  
Context cache saves you from doing extra DB trips for previously  
resolved to-one and to-many relationships... also for the cached  
queries if you use them.


> 2. Using nested DataContext with a request scope
>
> The other idea is to create a nested DataContext from the parent  
> session
> DataContext and bind this current thread. I imagine this would give  
> you
> the benefits of session scope caching, but still enable you to  
> throw away
> uncommitted data context objects at the end of the request.  I  
> don't know
> if it is any faster to create nested data contexts.

As you know nested DataContexts is a new feature, so we don't have  
any empiric data on its performance (would love to get your  
feedback). Performance overhead it adds is due to the fact that  
select and commit operations have to travel through an extra  
processing point in the stack. I would say it is appropriate for an  
editor form that supposedly accesses/modifies no more than a few  
[dozens] of objects; and not appropriate for a search page that may  
access thousands of objects.

3. Another possibility - if you never ever carry uncommitted state  
across requests, you can setup a filter that does  
DataContext.rollbackChanges() at the end of the request. This is a  
variation of the request-scope context, only preserving caching  
benefits.

Andrus




Re: DataContext scope in web apps

Posted by Andrus Adamchik <an...@objectstyle.org>.
> But I mention the pattern because one thing I've been considering  
> doing in one particularly complicated workflow is to have an  
> accumulator object graph that never actually gets committed, but  
> when the user saves or changes something a new datacontext is  
> derived from it. A bit expensive on memory and processing, but  
> probably an easy way to achieve what would otherwise be quite a  
> complicated algorithm. Maybe this is something you could consider.

I am eager to explore nested contexts for complex workflows with the  
ability to roll back sub-flows in memory without rolling back the  
entire thing. In fact nested contexts were designed with such  
scenario in mind.

Andrus




On Mar 27, 2006, at 8:08 AM, Craig Turner wrote:
> Malcolm Edgar wrote:
>> I am finding that with the Cayenne web app design pattern where  
>> the DataContext has session scope, it is easy to add objects to  
>> the DataContext when building up an object graph for display. If  
>> these objects aren't explicity committed by request it is easy to  
>> have them carried forward in the session DataContext, and have  
>> them committed in a subsequent but unrelated request.
>
> In the past I have had problems where the user will fail validation  
> on one screen, move to another, attempt a save, and then run into  
> problems because objects from the old screen are still sitting in  
> the datacontext.
>
> I get around this these days by using different DataContexts for  
> different flows. Each subsystem in my app is associated with a  
> different 'workflow' in the engine, and each workflow has its own  
> DataContext. I've been very happy with this pattern. And if there  
> are situations where one updating can have flow-on-effects to  
> another, then I get that workflow to reset the affected workflows  
> so that they'll be built up from scratch again if the user returns  
> to those screens. As my app is a webapp this is acceptable usage -  
> in fact I've been very happy with this pattern.
>
> One of the advantages of cayenne over EOF (at least in my  
> experience) is that you can remove things from the graph after  
> adding them, which makes it nice as a hacky object-graph-on-demand  
> even for things that you'll never persist to the database. But I  
> find that using multiple datacontexts I rarely need to remove  
> things from the graph.
>
> But I mention the pattern because one thing I've been considering  
> doing in one particularly complicated workflow is to have an  
> accumulator object graph that never actually gets committed, but  
> when the user saves or changes something a new datacontext is  
> derived from it. A bit expensive on memory and processing, but  
> probably an easy way to achieve what would otherwise be quite a  
> complicated algorithm. Maybe this is something you could consider.
>
>   - C
>


Re: DataContext scope in web apps

Posted by Craig Turner <cr...@synect.com>.

Malcolm Edgar wrote:
> I am finding that with the Cayenne web app design pattern where the 
> DataContext has session scope, it is easy to add objects to the 
> DataContext when building up an object graph for display. If these objects 
> aren't explicity committed by request it is easy to have them carried 
> forward in the session DataContext, and have them committed in a 
> subsequent but unrelated request.

In the past I have had problems where the user will fail validation on 
one screen, move to another, attempt a save, and then run into problems 
because objects from the old screen are still sitting in the datacontext.

I get around this these days by using different DataContexts for 
different flows. Each subsystem in my app is associated with a different 
'workflow' in the engine, and each workflow has its own DataContext. 
I've been very happy with this pattern. And if there are situations 
where one updating can have flow-on-effects to another, then I get that 
workflow to reset the affected workflows so that they'll be built up 
from scratch again if the user returns to those screens. As my app is a 
webapp this is acceptable usage - in fact I've been very happy with this 
pattern.

One of the advantages of cayenne over EOF (at least in my experience) is 
that you can remove things from the graph after adding them, which makes 
it nice as a hacky object-graph-on-demand even for things that you'll 
never persist to the database. But I find that using multiple 
datacontexts I rarely need to remove things from the graph.

But I mention the pattern because one thing I've been considering doing 
in one particularly complicated workflow is to have an accumulator 
object graph that never actually gets committed, but when the user saves 
or changes something a new datacontext is derived from it. A bit 
expensive on memory and processing, but probably an easy way to achieve 
what would otherwise be quite a complicated algorithm. Maybe this is 
something you could consider.

   - C