Login

Upgrading a Running Squeak Image

One of the challenges you'll face when deploying an application in Seaside, especially if you're running headless on a Linux box somewhere, is how to upgrade your production application to the latest version of your code. For the longest time, I've done this the easy way, upgrade the production image off-line, upload it, and restart the services. It's simple and straight forward, but it blows away all active sessions and kicks off any users currently online. There are other more interesting ways.

Some people use VNC to connect to their production image, fire up Monticello, and hot upgrade the code. Personally, I've never been able to get a stable version of VNC going, there are always issues, the latest being that the client is messed up by the nice Squeak UI Enhancements hiding the menus. Too much hassle, I gave up, I don't trust VNC at all.

There's a simple web based method within Seaside under /tools/versionuploader, you can use the web version of Monticello to load packages into the running image and then save the image from there. It's simple and effective, if not a bit clunky, but it works great if you're willing to expose that to the internet wrapped with just basic http authentication. I've been using this, but I thought I could have a little fun hacking up something a little more interesting.

So what are we talking about here, just loading a few Monticello packages and saving the image, how hard could that be to automate? Turns out, not so hard if I use the Installer that comes in Damien's dev image (that my image is based upon). I decide to make my images upgrade themselves from a staging directory where I can just secure copy or ftp up the new versions plus an empty trigger file named "load" to kick the process off. I've been playing with it a few days now, seems to work pretty well. Here's what it takes to make a self upgrading image (assuming you use Monticello to package all your code, as I do).

It's just two methods really, one to load all the packages deleting them after each one is installed, assuming the load file exists...

loadChanges
    | dir |
    dir := (FileDirectory default directoryNamed: 'load-package') assureExistence.
    dir fileNames 
        select: [ : ea | ea endsWith: '.mcz' ]
        thenDo: 
            [ : fileName | 
            ((dir fileExists: fileName) and: [ dir fileExists: #load ]) ifTrue: 
                [Transcript show: (fileName , ': ').
                Installer installFile: (dir fullNameFor: fileName).
                Transcript cr.
                dir deleteFileNamed: fileName ] ].
    (dir fileExists: #load) ifTrue: 
        [ dir deleteFileNamed: #load.
        SmalltalkImage current saveSession]

And a second to kick off a background process that runs continually, say every few seconds, that kicks off #loadChanges.

watchForChanges
    [[ (Delay forSeconds: 5) wait.
    [ self loadChanges ] 
        on: Error
        do: [ : error | Transcript show: error ] ] repeat ] 
        forkAt: Processor userBackgroundPriority + 1
        named: 'code loader'

Then I call watchForChanges in a class side #initialize somewhere on a utility class so the background process is kicked off automatically in any image I load my code into. Seems pretty slick so far, just upload the new packages, upload an empty file named load, the image upgrades itself, deletes all the files in the staging directory, saves itself, and chugs along like nothing happened at all. No session are lost, no users booted, if anything they might notice a slight pause for a bigger package loading.

This all of course assumes your production image is basically read only, i.e. has no code changes in it, as mine are. Enjoy!

Comments (automatically disabled after 1 year)

cdrick 6125 days ago

thanks again Ramon...

Is 5s not too often? I'd prefer trigger the watch for changes from a minimal seaside interface. but maybe it's not handy when you use several images. Anyway nice stuff :)

Cédrick

ps: Seeing your last answer on seaside list, I thought you would have made a blog post on image configuration at startup based on profile. I quote you "I keep three profiles, dev, prod, test, with a file in the root that determines which profile is used (easily toggled). Inside the directory for a profile, each setting is stored in a separate file where the filename is the setting name and the file contents are the value." I'd love you post on that too :)]

Damien Cassou 6125 days ago

Hi Ramon,

it seems you do not deal with package dependencies, you only load mcz in a random order. Moreover, how will the web application react when Monticello has only load part of the new mcz files?

Ramon Leon 6125 days ago

@Damien, you're right, I don't deal with dependencies because I don't use this for loading new packages and setting up images, I use it for deploying upgrades to existing production images, bug fixes and such. If I did want to load a new package, I'd simply upload them one at a time in the correct order.

I'm not sure I follow the last part of your question, unless I'm mistaken, Seaside runs at a lower priority so it just hangs tight doing nothing until the package is finished loading.

@Cédrick, I hadn't planned on posting on that but I guess if you're interested maybe others are as well. I think I pretty much explained exactly what I do already, there's not much left but a small handful of lines of code.]

Brett Kosinski 6125 days ago

On the topic of VNC, why wouldn't you just run squeak inside an Xvnc instance? I agree, the Squeak-native VNC server is flakey, and doesn't work with most clients, but a good ol' copy of Xtightvnc works quite nicely, and is how I manage my little seaside image.

Granted, this precludes running on a Windows box (unless someone could hack together a cygwin/X version of the squeak VM, along with a cygwin version of Xvnc), but it works quite nicely. And in my case, it allows me to work on the running image remotely (it's not a production image, so I just hack away, and revert things if I break something horribly :).

cdrick 6125 days ago

"I think I pretty much explained exactly what I do already, there's not much left but a small handful of lines of code."

Sure, it will be a small post but something good to have and to know (I think) as it's a real practice that increases productivity (or at least increases the developer helpers that squeak is missing) and I found it's in the spirit of your blog... :)

Cédrick]

Ramon Leon 6125 days ago

@Damien, you're right, I need to run this at a higher priority than Seaside to make sure it works as I expect. Seaside runs at #userBackgroundPriority so I guess userBackgroundPriority + 1 would ensure requests were paused while code was loading.

@Bret, because I don't run X on my server, it's command line only, I do all management via ssh.

Brett Kosinski 6125 days ago

Ah, good point... using Xvnc would require that you have a bunch of X-related trappings installed (client libraries, fonts, etc). Obviously, you don't actually need to run an X server aside from Xvnc, but all that other stuff is overhead that one might wish to avoid on a production machine.

Bill 6125 days ago

Lovely!

Is there some need to temporarily "suspend" active sessions until the load is complete, to avoid callbacks getting executed with a partial set of packages loaded?

Ramon Leon 6125 days ago

That's what we were discussing above, the upgrade now runs at a higher priority than Seaside requests, so once it starts, Seaside should be paused by the scheduler until the higher priority upgrade is finished (which normally only takes a few seconds).

Sebastian 6122 days ago

Very interesting post. I just wonder about the save the image part. For deploy I use to set up the last code from a brand new image and the services always start in that state guaranteed. Saving the image will mean N sessions and temporary stuff in the image being saved. If the image being updated happen to be on a significative load at update time, then it could happen to save an image of 80MB or more instead of a fresh one of 37MB. If housekeeping should be made before saving, I can't think in how to do it without breaking current user sessions. I think the trade off is take that risk (of trusting in saved images) or to set up a programed maintenance to stop service in an adequate informed moment and quickly do it offline. It also could be automatized to some point.

Ramon Leon 6122 days ago

You're right, but that's simply a trade off one makes if you want to upgrade the app without taking it down. Quick bug fixes identified and fixed while dealing with the customer can be immediately pushed out and such responsiveness is very impressive to the customer.

That an image might take a few extra megs on disk due to active Seaside sessions is of little to no concern to me, I could always just schedule a cron job to create the empty load file at 2am to ensure another image save during low or no load to get back a smaller image.

Scott Herndon 6122 days ago

Wow, Smalltalk is still so readable after all these years. Sorry to interrupt. I used to work with Smalltalk from the mid-80s to mid 90's, haven't done any real Smalltalk coding in 6 years or so and it is still as readable as it was back then. I was a real expert and loved the language, even worked for ParcPlace-Digitalk. Then when the great demise came I was caught up with having to feed my family and moved to the dark side and Java. My loss, I can assure you.

I was reading you code snapshot (still called that?) and I am amazed at how easily I can still read and understand Smalltalk. It is also interesting how some problems are always being resolved. Please, I mean no offense at all, your solution is fine. It just seems that upgrading images is still the same issue it was back then, different circumstances and technology but still the same issue, how to upgrade a running image without bringing down everything to do it. I've looked at Squeak and Seaside a number of times and I am very happy to see that there are still people enthusiasticly learning the right way to code with objects.

I am currently working with Ruby and Rails, I like Ruby a lot, Rails maybe not so much, but you have to do what customer's want. I am always tempted to go back to Smalltalk. I don't have similar feelings about C or Pascal or Ada or any other language. Perhaps if you ever need a testomial that's one.

Smalltalk grows on you and some how you know it is the right way to program and you are changed forever. No matter how people try to tell you it is dead, and turn away when you just can't stop talking about how you did this and that in 3 months or prototyped this huge app in a few weeks. I loved the Workspace and interactive coding. And so, one issue with pure OO langs, such as Smalltalk or Ruby is they change the way you go about coding. With Smalltalk you can take bigger risks, because you didn't invest so much. So if some piece of code doesn't feel right you still have time to refactor before things get too complicated. I've written very large and complex systems in my time, and found out the hard way if I abandoned the Smalltalk way. XP all the way. And MVC, I love to remind people how we used to use MVC in Smalltalk back in the 80's especially when I was at Sun.

That's not to say it is all perfect, but It is easy to change and customize. This is one reason I like Ruby. One reason I don't is it is command line no IDE built-in and the IRB well good is not great. So, if I keep reading your blog and studying your code I might just be pulled in again --- my wife would kill me. Keep up the great work, may the Smalltalk Way live forever.

Ramon Leon 6122 days ago

Well Scott, sounds like you miss Smalltalk quite a bit. If it makes you feel better, I think it's coming back, the Seaside community continues to grow and I've never had more fun programming, I won't stop using it any time soon. Smalltalk does change you, in the OO arena, no other language will do for me. Oh, and welcome to the blog, lots of stuff to read if you dig back a bit.

about me|good books|popular posts|atom|rss