Replacing the parent beanFactory in a continuous deployment scenario

41 views
Skip to first unread message

Brian G

unread,
Jul 10, 2014, 12:30:30 AM7/10/14
to model...@googlegroups.com
As the list may be aware, I've been struggling for some time to push code repeatedly with zero interruptions to users while under heavy load as we inch closer to the holy grrrrrrrrrrail of continuous deployment.  To recap, my objectives are:

* To be able to refresh the beanfactory/model (application.cs, a model-glue parent bean factory) independent of model-glue
* To be able to refresh model-glue (including sub apps) independently of the beanfactory

I saw something in some code from Doug Hughes which led me to experiment today and I believe I've discovered something important and would like to get some confirmation/feedback on the approach.

First, this is the code I saw (from https://github.com/dhughes/ColdBooks/blob/master/Application.cfc):

<cfif structKeyExists( application, this.realName ) AND structKeyExists( application[this.realName], "framework" ) >
<cfset application[this.realName].framework.getBeanFactory().setParent( application.cs ) />
</cfif>

Woah!  Swapping out the parent beanfactory without deleting and recreating Model-Glue??  Now THAT is interesting.  I've always wanted to be able to reinit my bean factory but ModelGlue always just plods along, blissfully ignorant of my shiny new model.  This code snippet got me suspicious it might be possible. That code is pretty old (and undocumented) MG syntax but this works in MG 3.1.299:

   <cfset application[this.realName].getInternalBeanFactory().setParent(application.cs) />

I can do the reverse and getParent() and (by adding in a unique value to my application.cs) I have confirmed that it is indeed getting updated.  But here's the rub, my controllers don't pick up any of the changes because they are autowired by Coldspring at init time so they are holding on to references to the old model.  SO, I tried three different approaches to accessing my service layer from inside my controllers:

1. autowire (set{beanname})
2. beans scope (defined in the modelglue.xml)
3. getModelGlue().getBean("beanname") in the controler

And after refreshing application.cs and running the setParent(), guess what?  #3 returns the updated model bean!!  Unless I'm missing something, I believe by changing all service layer references in my controllers to use syntax #3, I can seamlessly recreate coldspring and replace it in Model-Glue without anyone being the wiser!  This almost seems too good to be true which is why I'm writing you here:

Question: getModelGlue().getBean() appears to be deprecated in favor of the beans scope but is it just because it's shorter to write?  Technically it's one less method call because they are cached in the beans scope but are there any other downsides to using getModelGlue().getBean()?

In small apps, I recognize that you can just delete the application key for the MG app and let it re-init which would accomplish a similar thing.  However, I have a pretty massive app.  Hundreds of beans in ColdSpring and three MG apps with dozens of controllers and hundreds of methods.  Combined with Transfer, which absolutely hates restarting under any kind of load, I'm trying to tease apart and reduce the requirement to refresh everything all at once.  Even on beefy production hardware, my app takes 60s to reinit from coldspring to model-glue, thus, my desire to march closer to continuous deployment where nobody is the wiser that I'm upgrading code in real-time.

Hope this all makes sense - I'm excited in a very nerdy way tonight so I appreciate your feedback.


Brian

Brian G

unread,
Jul 31, 2014, 2:09:58 PM7/31/14
to model...@googlegroups.com

Guys - any thoughts on whether this will work or not?

Dan Wilson

unread,
Jul 31, 2014, 2:15:55 PM7/31/14
to model...@googlegroups.com
There is a lot there, Brian. Can you clarify your questions?

DW

Thursday, July 31, 2014 2:09 PM

Guys - any thoughts on whether this will work or not?


On Wednesday, July 9, 2014 9:30:30 PM UTC-7, Brian G wrote:
--
--
Model-Glue Sites:
Home Page: http://www.model-glue.com
Documentation: http://docs.model-glue.com
Bug Tracker: http://bugs.model-glue.com
Blog: http://www.model-glue.com/blog
 
You received this message because you are subscribed to the Google
Groups "model-glue" group.
To post to this group, send email to model...@googlegroups.com
To unsubscribe from this group, send email to
model-glue+...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/model-glue?hl=en
---
You received this message because you are subscribed to the Google Groups "model-glue" group.
To unsubscribe from this group and stop receiving emails from it, send an email to model-glue+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Thursday, July 10, 2014 12:30 AM
As the list may be aware, I've been struggling for some time to push code repeatedly with zero interruptions to users while under heavy load as we inch closer to the holy grrrrrrrrrrail of continuous deployment.  To recap, my objectives are:

* To be able to refresh the beanfactory/model (application.cs, a model-glue parent bean factory) independent of model-glue
* To be able to refresh model-glue (including sub apps) independently of the beanfactory

I saw something in some code from Doug Hughes which led me to experiment today and I believe I've discovered something important and would like to get some confirmation/feedback on the approach.

First, this is the code I saw (from https://github.com/dhughes/ColdBooks/blob/master/Application.cfc):

<cfif structKeyExists( application, this.realName ) AND structKeyExists( application[this.realName], "framework" ) >
<cfset application[this.realName].framework.getBeanFactory().setParent( application.cs ) />
</cfif>

Woah!  Swapping out the parent beanfactory without deleting and recreating Model-Glue??  Now THAT is interesting.  I've always wanted to be able to reinit my bean factory but ModelGlue always just plods along, blissfully ignorant of my shiny new model.  This code snippet got me suspicious it might be possible. That code is pretty old (and undocumented) MG syntax but this works in MG 3.1.299:

   <cfset application[this.realName].getInternalBeanFactory().setParent(application.cs) />

I can do the reverse and getParent() and (by adding in a unique value to my application.cs) I have confirmed that it is indeed getting updated.  But here's the rub, my controllers don't pick up any of the changes because they are autowired by Coldspring at init time so they are holding on to references to the old model.  SO, I tried three different approaches to accessing my service layer from inside my controllers:

1. autowire (set{beanname})
2. beans scope (defined in the modelglue.xml)
3. getModelGlue().getBean("beanname") in the controler

And after refreshing application.cs and running the setParent(), guess what?  #3 returns the updated model bean!!  Unless I'm missing something, I believe by changing all service layer references in my controllers to use syntax #3, I can seamlessly recreate coldspring and replace it in Model-Glue without anyone being the wiser!  This almost seems too good to be true which is why I'm writing you here:

Question: getModelGlue().getBean() appears to be deprecated in favor of the beans scope but is it just because it's shorter to write?  Technically it's one less method call because they are cached in the beans scope but are there any other downsides to using getModelGlue().getBean()?

In small apps, I recognize that you can just delete the application key for the MG app and let it re-init which would accomplish a similar thing.  However, I have a pretty massive app.  Hundreds of beans in ColdSpring and three MG apps with dozens of controllers and hundreds of methods.  Combined with Transfer, which absolutely hates restarting under any kind of load, I'm trying to tease apart and reduce the requirement to refresh everything all at once.  Even on beefy production hardware, my app takes 60s to reinit from coldspring to model-glue, thus, my desire to march closer to continuous deployment where nobody is the wiser that I'm upgrading code in real-time.

Hope this all makes sense - I'm excited in a very nerdy way tonight so I appreciate your feedback.


Brian

--
--
Model-Glue Sites:
Home Page: http://www.model-glue.com
Documentation: http://docs.model-glue.com
Bug Tracker: http://bugs.model-glue.com
Blog: http://www.model-glue.com/blog
 
You received this message because you are subscribed to the Google
Groups "model-glue" group.
To post to this group, send email to model...@googlegroups.com
To unsubscribe from this group, send email to
model-glue+...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/model-glue?hl=en
---
You received this message because you are subscribed to the Google Groups "model-glue" group.
To unsubscribe from this group and stop receiving emails from it, send an email to model-glue+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jared Rypka-Hauer

unread,
Aug 1, 2014, 1:11:46 PM8/1/14
to model...@googlegroups.com
Random thoughts quickly emailed off the top of my head:

That’ll reload the parent for MG’s default bean factory… the one that actually runs the application.

Since the parent/child relationship resolves differences between beans that have the same ID, I’m not 100% sure where the cache resides… if it resides in the child (which is actually entirely possible, now that I think of it), then setParent() will need to clear any relevant cached beans or CS will just return the cached version and won’t reload them from the new parent.

I think. I’d have to go look at the code and I don’t really have time at the moment. Check out the code for setParent(), getParent() and getBean()… but beware, getBean() is a MONSTER METHOD and does some really blow-your-mind black magic/code voodoo to resolve bean ID conflicts with the parent AND deal with circular dependencies.

Hope that gives you some sort of direction to go…

Laterz,
J

Brian G

unread,
Sep 5, 2014, 9:27:09 PM9/5/14
to model...@googlegroups.com

Jared - thanks for the thoughts; I looked at getBean() in MG/CS - it doesn't appear MG caches it and the CS getBean() method looks to see if a bean exists in the local factory and if not it passes the request to the parent.  So, I believe using setParent() on the internal MG factory will indeed make subsequent calls to the beanfactory access beans from a newly-updated beanfactory from setParent().

Dan, here's the questions I'm looking to understand before investing non-trivial time in reworking my app for continuous deployment.  My objective is to refresh the beanfactory/model (application.cs, a model-glue parent bean factory) independent of model-glue while live in production and under load.

I've confirmed to my satisfaction that the following code will update my application's parent beanfactory in MG 3.1.299:


   <cfset application[this.realName].getInternalBeanFactory().setParent(application.cs) />

The remaining question at this point is:  Are there any downsides to getModelGlue().getBean()?  It appears to be deprecated in favor of the beans scope but is it just because it's shorter to write?  Technically the beans scope is two less method calls but are there any other downsides to using getModelGlue().getBean()?

If not, I believe a Coldspring/Model-Glue application can support true continuous deployment with two conditions:

1. No cached references to the service layer in persistent scopes (e.g., don't do <cfset session.obj = application.cs.getBean("MySvc")>)
2. Exclusive use of getModelGlue().getBean(beanname) in Controllers and NO autowiring of service layer objects


Brian

Dan Wilson

unread,
Sep 7, 2014, 8:04:13 AM9/7/14
to model...@googlegroups.com
The beans scope is preferred for 2 reasons:

Annotation/Documentation
The beans referenced in a particular controller using the beans reference would all the documented in a single place at the controller definition. This would make it easier at a glance to understand which beans are used in which controllers.

MG Caching
Naturally, the beans are injected into the controller and will live inside of the controller state for the lifetime of the controller.

Summary
The documentation part is important and I do prefer it to be this way when I write my code. However, a continuous integration scenario may rate higher on the priority scale for you.

The caching part is negligible since ColdSpring will be serving references to your beans from a cached state. Thus, there is likely no performance advantage worth speaking about.



Please share your findings

DW








Brian G wrote:

Brian G

unread,
Sep 10, 2014, 1:43:09 PM9/10/14
to model...@googlegroups.com

On Sunday, September 7, 2014 5:04:13 AM UTC-7, Dan Wilson - sipa...@gmail.com wrote:
The documentation part is important and I do prefer it to be this way when I write my code. However, a continuous integration scenario may rate higher on the priority scale for you.

The caching part is negligible since ColdSpring will be serving references to your beans from a cached state. Thus, there is likely no performance advantage worth speaking about.

Please share your findings


I changed a single controller from autowired set/getServiceObject() to use getModelGlue().getBean() and was successful.  I can change my service layer, restart my beanfactory and the next reload of my MG app picks up the changes without reinitializing the MG app.  I haven't done any tests under load, but that's very exciting.

The next test is to verify if doing this successfully allows the beanfactory to be garbage collected.  By overwriting the original coldspring instance in the application scope, it *should* be collectable.  However, my experience with that actually happening is less than 100%.  Otherwise what I've seen is that refreshing the beanfactory multiple times eventually leads to a PermGen error and crashes the instance.

The ability to restart an app without crashing during a request is definitely worth the tradeoff of the documentation of the beans scope.  Personally I centralize the calls behind a getServiceObject() in the Controller and that method was previously returning variables.ServiceObject (when auto-wired by CS) and now is calling getModelGlue().getBean().


Brian

Brian G

unread,
Feb 10, 2015, 8:43:54 PM2/10/15
to model...@googlegroups.com

Just an update that I've done this to my entire application in all the controllers and have not suffered any ills as far as I can tell.  This still doesn't solve the beanfactory getting GC'd - there is a leak/reference somewhere that I have yet to track down - but it does make hot deploying the app easier.

Along the lines of another thread, I think I'm going to add RAM and bump the permgen waaaaaaay up so I can support more reloads before permgen gets into the danger range.  Ultimately we need to eliminate sticky sessions and externalize the session scope into redis or something outside of CF and then I think we'll be all set.


Brian
Reply all
Reply to author
Forward
0 new messages