Issues with cache updating incorrectly

0 views
Skip to first unread message

Daniel Short

unread,
Jul 19, 2008, 11:57:45 PM7/19/08
to transfer-dev
Hi all,

During testing today I've had two instances where I ended up with
duplicate items in an arrary of subitems from the cahce. So I have
this in the database:

Group ID 10
No Items

I add two Items, and I have this:

Group ID 10
Item 1
Item 2

I go and add a third and a fourth, and I end up with something like
this:

Group ID 10
Item 1
Item 2
Item 2
Item 2
Item 3
Item 4


Does anyone know off the top of their heads what might cause this to
happen? The data is correct in the database, but it's getting output
badly. If I reinitialize my ColdBox framework and get the cache
cleared, everything starts rendering correctly:

Group ID 10
Item 1
Item 2
Item 3
Item 4


I'm going to work on getting a reproducible test case, but I'm hoping
someone has had the same experience and can get me to the answer
quickly.

Thanks,

Dan

Daniel Short

unread,
Jul 20, 2008, 9:49:10 AM7/20/08
to transfer-dev
Hi,

I've gone through and duplicate this error several times. So I've
documented my workflow below and provided code for all major methods
along the path.:

1. Load page with all items in a Group (starting with none).

2. Add a unique item, works fine, one added to cache (Testing)

3. Add another unique item, still fine, one added to cache (Testing 2)

4. Add a duplicate item, business logic skips save method and shows
error (Testing 2)

5. Add another duplicate item, business logic skips save method and
shows error (Testing 2)

6. Add another unique item (Testing 3), cache now contains two copies
of second item added (Testing 2)

7. Attempt to add three duplicates of Testing 3.

8. Add another unique item (Testing 4), cache now contains four copies
of Testing 3.

At the end of all of this the data output from the cache looks like this:

ID Label
7 Testing

8 Testing 2

0 Testing 2

9 Testing 3

0 Testing 3

0 Testing 3

0 Testing 3

10 Testing 4

The data in the database however, looks like this:

7 Testing 7/20/2008 8:38:37 AM
8 Testing 2 7/20/2008 8:38:59 AM
9 Testing 3 7/20/2008 8:40:11 AM
10 Testing 4 7/20/2008 8:41:23 AM


Now as far as my code works, I'm using a cfwindow to add via ajax
using the following code in the ajax/additem action:

<cffunction name="addItem" access="public" returntype="void" output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext">

<cfset var rc = Event.getCollection() />

<!--- Pass the group ID in in order to assign the item to the proper group --->
<cfset rc.Item =
variables.objectFactory.getBean("ItemService").getItem(rc.GroupID, 0)
/>

<!--- Even if a blank object came back, be sure to set the group ID
correctly --->
<cfset rc.Item.setParentGroup(variables.objectFactory.getBean("GroupService").getGroup(Session.UserID,
rc.GroupID)) />

<cfset rc.Success = False />
<cfif IsDefined("rc.do") AND rc.do EQ "additem">
<cfset LOCAL.errors = ArrayNew(1) />

<!--- Populate User with data --->
<cfset getPlugin("beanFactory").populateBean(rc.Item) />

<cfset LOCAL.result =
variables.objectFactory.getBean("ItemService").save(rc.Item) />

<cfif LOCAL.result.results>
<cfset getPlugin("messagebox").setMessage(type="info",
message="Item Added!") />
<cfset rc.Success = True />

<cfelse>
<cfset getPlugin("messagebox").setMessage(type="error", message=
ArrayToList(LOCAL.result.errors, "<br />")) />

</cfif>
</cfif>

<cfset Event.setView("ajax/additem") />

</cffunction>

So it looks like the objects are getting added to the groups Items
cache even if they haven't been saved, which doesn't seem right to
me... I would think that only saved data should be persisted into the
cache. What can I do to rectify this problem?

Dan

Matt Quackenbush

unread,
Jul 20, 2008, 1:25:11 PM7/20/08
to transf...@googlegroups.com
Dan,

From a brief glance, it appears that your issue may be related to not having your LOCAL struct var'd.


Daniel Short

unread,
Jul 20, 2008, 1:52:40 PM7/20/08
to transf...@googlegroups.com
> From a brief glance, it appears that your issue may be related to not having
> your LOCAL struct var'd.

Thanks for the catch, but unfortunately that doesn't fix the issue...
This is definitely at the Transfer level since it's ending up in the
Transfer cache. I went back through the code flow and ensured that I
hadn't left any other scopes un-vared and I get the exact same
behavior. Unsaved child items showing up in the parent cached object.

Dan

Matt Quackenbush

unread,
Jul 20, 2008, 2:33:47 PM7/20/08
to transf...@googlegroups.com
a) What "cache"?  The ColdBox cache?  That's what it seems like you're saying.

b) can you post the save() method from your ItemService?


Daniel Short

unread,
Jul 20, 2008, 6:12:05 PM7/20/08
to transf...@googlegroups.com
I believe it's the Transfer cache... I opened the cache monitor for
ColdBox and didn't see anything related to transfer there. I don't
even know how to check the Transfer cache... Here's the save method
from the ItemService:

<cffunction name="save" access="public" returntype="struct" output="false">
<cfargument name="object" type="any" required="true" />
<cfset var LOCAL = StructNew() />
<cfset var validateData = "" />
<cfset var retVal = structnew()>
<cfset retVal.results = false />
<cfset retVal.message = "Invalid Request." />
<cfset retVal.messageType = "error" />

<cfset validateData = Arguments.object.validate() />

<cfif validateData.results>
<!--- save new bean data to db --->
<cfset variables.oTransfer.save(Arguments.object) />
<cfset retVal.results = true />
<cfset retVal.message = "Item Saved" />
<cfset retVal.messageType = "info" />
<cfelse>
<cfset retVal.errors = validateData.errors />
</cfif>

<cfreturn retVal />
</cffunction>


Dan

Matt Quackenbush

unread,
Jul 20, 2008, 6:41:00 PM7/20/08
to transf...@googlegroups.com
Okay, I think I finally spotted it.  This bit right here:


<!--- Even if a blank object came back, be sure to set the group ID
correctly --->
       <cfset rc.Item.setParentGroup(
variables.objectFactory.getBean("GroupService").getGroup(Session.UserID,
rc.GroupID)) />

You're assigning an empty object to the parent (group).  I don't know the particulars of your use case, but I typically do not run setParent{Object}() on the child until I know that the child is valid.  That is actually something I do at save time.  Something like this...

{Group Decorator}
<cffunction name="addItem">
   <cfargument name="item" hint="The item (object) to add" />

   <cfscript>
      item.setParentGroup( this );
      item.save();
   </cfscript>
</cffunction>

If your particular use case requires the child be assigned prior to save, then on an unsuccessful save you will need to remove it by calling removeParentGroup().

Make sense?

Bob Silverberg

unread,
Jul 20, 2008, 8:04:20 PM7/20/08
to transf...@googlegroups.com
I think Matt's on the right track here. Although I cannot see it from
your code, I'll bet that rc.Item is an actual Transfer object, rather
than a clone. When you make a change to a Transfer object (e.g., by
calling setParentGroup()), that change is reflected in the cache
immediately, and persists in the cache regardless of whether you save
the object or not.

Matt is correct that you could removeParentGroup to fix the issue, but
another option would be to use a cloned object. This is precisely
what the clone() method is for. So instead of ItemService.getItem()
returning an actual Transfer object, have it return a clone of the
Transfer Object. Then your changes will not be reflected in the cache
until you actually save the object. This also prevents another user
from seeing the "bad data" in the cache between the time that you call
setParentGroup() and decide whether or not to save the object.

Bob

--
Bob Silverberg
www.silverwareconsulting.com

Daniel Short

unread,
Jul 21, 2008, 11:47:59 AM7/21/08
to transf...@googlegroups.com
Thanks for the tip guys. I changed the two lines to do with object
initiation and the problem has gone away:

<!--- Pass the group ID in in order to assign the item to the proper group --->
<cfset rc.Item =
variables.objectFactory.getBean("ItemService").getItem(rc.GroupID,

0).Clone() />

<!--- Even if a blank object came back, be sure to set the group ID
correctly --->
<cfset rc.Item.setParentGroup(variables.objectFactory.getBean("GroupService").getGroup(Session.UserID,

rc.GroupID).Clone()) />


I'm getting a clone of the item, and then assigning it to a clone of
it's parent group, and all is well on save and errors.

Thanks,

Dan

Daniel Short

unread,
Jul 21, 2008, 3:13:21 PM7/21/08
to transf...@googlegroups.com
Just following up on this one... I wrote a blog post today detailing
my experience with this. Hopefully it will save someone else from
asking you guys the same questions :)

http://dansshorts.com/?day=7/21/2008#blog368

Dan

Reply all
Reply to author
Forward
0 new messages