The OpenSim lightweight Release Cycle
This is the current (as of the 0.6.5 release) OpenSim lightweight Release Cycle recipe.
The goal is to run a cycle that churns out recommended code snapshots with reasonably regular intervals, and with a minimum of work for any one human resource. It should be a community effort, and anyone should be able to move the cycle forward.
If you're not acquainted with svn revision numbering schemes, see "On revisions, tags and branches" for additional info.
The Cycle:
- Developers and Testers work on /trunk ("testers" being defined as "users feeding off trunk", also lovingly known as "trunkheads")
- Developers and Testers identify suitable release candidate revisions, from the repository history. (for example, people on osgrid.org discuss this on their weekly meet-ups)
- This revision is branched off as a "release candidate" (to /branches/0.6.5-rc1 in this case), and the version number is upped and committed only to this branch. We don't want to up the version number in trunk until we know we have a release.
- Testers now switch their focus to running the rc until they can give feedback on whether "rc sux" or "rc rox".
Key issues:- Has all version numbers been updated?
- Does it play well with last version? Do we need to up the interface version as well?
- Does it feel better or worse than last version?
- Any critical errors that needs fixing first?
- If any critical but fixable errors are found, these are fixed in trunk and the fixing revision is merged into the release candidate branch. (or vice versa, if that works out better)
- If, even after this, testers agree that "rc sux" the project goes back to 1) to wait and look for the next rc (thusly named rc2)
- If testers agree that "rc rox" the current rc revision is tagged as "-release" (to /tags/0.6.5-release in this case)
- The "-rc1" branch is renamed to "-post-fixes" (to /branches/0.6.5-post-fixes in this case) for continued service awaiting the next cycle. The svn rename lets us keep the revision history back thru the rc branch history.
- The version number (and optionally interface version) uppage is merged back into trunk. (this is a minor merge which will probably always succeed without conflict)
- The project goes back to 1)
The output of this would be named revisions for each of these user categories:
- Grid/Region owners running services in production
Feed off "-post-fixes" if they want stability first, or off "-release" if they want a known 'upgrade track'. - Developers
Feed off /trunk, or off -rc or -post-fixes if they want some stability while hunting bugs or doing protoyping. - Testers
Testers are committed to helping the devs find bugs at the prize of instabilities, crashes and loss of data. They therefore feed off the /trunk development branch or -rc depending on whether there is an RC pending release or not. (ie, is the latest version an RC, use that, if not, use /trunk)
Everyone feeding off /trunk are collectively labeled 'testers', and any installation running on anything but a 'release' or 'post-fixes' revision is considered a 'test installation'.
On Revisions, tags and branches
In light of some confusion in the wake of the 0.6.5 release of OpenSimulator, I just thought I'd share a couple of good-to-know points about SVN versioning;
In an SVN repo, the 'revision' is a discrete version of the software. But; the revision number is not an indication of sequential functional increment.
Revisions are based on a revision before it, but that does not have to be the revision immediately before it.
The revision number counter is global to the svn repository, hence it is upped whenever somebody does something within the repo, regardless of what branch the thing happens to.
An example:
- I commit something to trunk, the rev number is upped to, say, 1337, and my new version is given that number.
- I now branch trunk into /branches/mybranch. I do this by doing a remote copy of trunk. This copy of trunk, although identical to 1337, is a new version and is thus given the revision number 1338, the first revision on the new branch.
- Now I commit a number of changes to trunk, spinning past 1339, 1340, 1341 and 1342 - each being separate versions but on the branch called /trunk (think of it as paths)
Now, if I commit some minor change to /branches/mybranch, what revision number will it get? You guessed it - 1343.
If I now commit something to /trunk again, it will get 1344. I then commit something to the really really old branch /branches/reallyoldbranch that was based on, say, revision 666. It will now get 1345.
So, my point is: you should never talk about revision numbers when your're trying to convey an idea of some kind of sequential advancement of state.
Ie, in my example, 1344 is probably the one reflecting the most development, 1343 is essentially based on 1337 and 1345, currently the highest rev number, could basically be six months old, with just a minor modification.
Now on to tags:
Trunk, branches and tags are really all one thing: paths. The only real difference is by convention: there is a certain path called /trunk that serves a certain purpose, namely to be the focal point for developers. There is a certain name used for paths other than trunk, and that is "branches". Some branches by convention aren't meant to be modified, but serve as snapshots of a point in time - they are called "tags".
But to the server, they are all revisions along differerent link paths. There is really nothing stopping you from committing to a tag - it's just the svn client will be reluctant to do so, as it's aware of the convention.
Keep this in mind, when referring to a 'revision' - it might be on a totally different path, so be sure to mention what path you're really talking about. specifying "trunk, between revision 1343 and 1780" or "0.6.2 post-fixes, between revision 1764 and 2006" again lets you compare revision numbers to indicate advancement of state.
Also, don't tell people to check a certain revision out, unless you mean exactly that code snapshot. If you want them to check out a certain version, tell them the logical path to it instead.
I hope this has cleared any potential confusion up.
Tribes and Tribulations
Tribal Media has gone out of the Virtual Worlds business. Quite simply, we never could get a sustainable income out of it so finally we decided to close shop.
Darren and I have now reverted to being 'just' lbsa71 and MW, and we will both continue working with the OpenSim community.
I would like to extend my heart-felt gratitute to the prospects, clients, peers and partners we've had that made the last two years such an exciting ride - and I'm especially thankful for all the 3D webnauts that joined us in Tribal Net. You guys rocked!
Also, I'd like to extend a big thank you to Darren for working so hard with me to make our visions come to life - as head of R&D he produced some pretty amazing stuff that one can only hope will see the light of day some day - and to Jim, who believed in us and our vision so deeply he put his money where our mouth was.
Together, we wrote what will surely be a chapter in the metaverse saga.
Oracle 11gR2 taking 100% CPU utilization
Firts of all, this can have an array of explanations, but I just wanted to share one of them and its solution. (Here's a related problem.)
Oracle has a "Provisioning Daemon" that runs jobs that are pre-populated in the oracle install. Now, apparently, if there is a mismatch in date settings (say, from having the wrong time settings when setting up Oracle) this job template will always be in the past, and consequently, the daemon will try to start a new job, until it consumes 100% and croaks. Fixing the time issue and restarting the server won't help, as the 'bad' jobs are still waiting there when the server comes up.
The solution is to
- Stop the console
- Run
create table mgmt_job_bad as select * from sysman.mgmt_job where job_name = 'PROVISIONING DAEMON';
delete from sysman.mgmt_job where job_name = 'PROVISIONING DAEMON';
commit; - Restart the installation.
- Review the mgmt_job_bad to see what needs to be done.
Caveat: Those jobs were put there for a reason - you should get to know exactly what went wrong before doing this in a production environment.
lbsa71: wb
So, I moved the blog off a standard web host blog to an wordpress installation of my own. Hopefully, this should mean I can get a bit better control over the site content appearance. Thank you for your patience.
Top ten ways to know you are not doing agile
Alistair Cockburn ('cooburn') is one of my heroes; here's a thought-provoking list to match your team against:
http://alistair.cockburn.us/Top+ten+ways+to+know+you+are+not+doing+agile
Refactoring Tactics Primer
Introduction
In this article I will show how to employ some relatively advanced code refactoring tactics to change a reasonably hairy piece of spaghetti code into something a bit more structured.
Basic familiarity with the JetBrains ReSharper tool is required.
Background
The code in this sample reflects an actual piece of code that a colleague of mine got handed to him. Amongst other abominations, it employed the 'transferring values in and out of Windows Forms by getting and setting public fields on an global static class' anti-pattern. He realized that the code actually introduced a bug (since the static fields would retain their values between subsequent calls) so I thought I'd show him how to wrestle out of some of the more nasty bits of the code. After doing that, I figured I'd write this article so that the world brain could benefit from it.
Instructions
Download the RefactoringPrimer.zip and extract and load the Visual Studio 2008 solution Seminar1.sln, make sure you have ReSharper handy, and follow the walkthrough below.
The solution is split into six steps, Step 1 being the original spaghetti code, and Step 6 the final result.
The Scenario is built around an application (Program) that calls up a (faked) Windows Form (FakeForm), and uses a static utility (Util) class to pass initial values and fetch returned data.
Each solution project corresponds to the starting point in each step, and builds a console project that when run tries to call up forms twice, each time emulating a user entering some data, and reports 'wrong: using tainted maxValue' or 'correct: using default maxValue'.
Points of Interest
This walkthrough is really only a primer to what can be done with taking a "automated refactoring tactics" approach; "tactics" referring to laying out a 'strategy', a chain of automated refactorings to get from a code state a to a code state b.
To create this particular set of refactoring tactics, I made extensive use of scratch refactoring, which is something I've found to be very useful.
Walkthrough
Step1 - Deciding what to break out
- Manually create a public inner class called 'FormData' inside the Utils class.
static class Utils { public class FormData { } private const int DEFAULT_MAX_VALUE = 5; public static int MinValue = 1; public static int MaxValue = DEFAULT_MAX_VALUE; public static int MeanValue = 10; - Right-click on the MinValue member field, choose "Refactor", then "Move". Check static fields MinValue, MeanValue and MaxValue, and specify Step1.FormData as the target. Press "Next". After the move, note that DEFAULT_MAX_DATA is accessible, since FormData is an inner class. Also, inspect
FakeFormandProgramto see the updated references. - Run and show wrong result
From now on, I will not write out the 'right-click', "Refactor" or "Click Next" et c - if the istructions aren't obvious to you, I recommend you spend some time familiarize yourself with ReSharper. Either that, or place a request for clarification in the comments.
Step2 - Breaking out of the static cage
- "Copy Type"
Utils.FormDatato InstanceFormData - it will also be an inner class. - Remove all static declarations on the copied type -this will become our 'new' FormData.
public class InstanceFormData { public int MinValue = 1; public int MaxValue = DEFAULT_MAX_VALUE; public int MeanValue = 10; } - "Move" InstanceFormData to "Outer scope" giving it the new name "FormData"
- The refactoring tool will complain that
DEFAULT_MAX_VALUEwill no longer be accessible. TurnDEFAULT_MAX_VALUEpublic and hit 'Refresh' - Change 'internal' on new FormData to "public" (or you will get a build error when we do the manual switch later)
public class FormData { public int MinValue = 1; - Manually remove inner FormData type definition and replace with a static Singleton field initializer:
static class Utils { public static FormData FormData = new FormData(); public const int DEFAULT_MAX_VALUE = 5; public static bool IsDefaultMaxValue( int val ) { return val == DEFAULT_MAX_VALUE; } public static bool TestMinGreaterThan3() { return FormData.MinValue > 3; } } public class FormData { public int MinValue = 1; public int MaxValue = Utils.DEFAULT_MAX_VALUE; public int MeanValue = 10; } - Run and show wrong result - we're still running on the same instance.
Step3 - Passing the temporary singleton around
- "Introduce field" m_formData on FakeForm, initialized in constructor.
- "Introduce Parameter" formData on m_formData initializer.
- Manually change FakeForm constructor member initializations to
m_min = formData.MinValue; m_mean = formData.MeanValue; m_max = formData.MaxValue;
(remember that the member fields are faking textboxes)
- "Safe Delete" all unused (greyed out) constructor parameters
- "Introduce Parameter" formData on TestMinGreaterThan3
static class Utils { public static FormData FormData = new FormData(); public const int DEFAULT_MAX_VALUE = 5; public static bool IsDefaultMaxValue( int val ) { return val == DEFAULT_MAX_VALUE; } public static bool TestMinGreaterThan3(FormData formData) { return formData.MinValue > 3; } } - "Move" static method TestMinGreaterThan3 to FormData
- "Make method non-static" on TestMinGreaterThan3
public class FormData
{
public int MinValue = 1;
public int MaxValue = Utils.DEFAULT_MAX_VALUE;
public int MeanValue = 10;
public bool TestMinGreaterThan3()
{
return MinValue > 3;
}
}
Step4 - Getting rid of the temporary singleton
- "Introduce variable" formData on first Utils.FormData in Run()
FormData formData = Utils.FormData; FakeForm a = new FakeForm(formData);
- Manually Change Utils.FormData to new FormData();
FormData formData = new FormData(); FakeForm a = new FakeForm(formData);
- Manually add another initializer.'
FormData formData = new FormData(); FakeForm a = new FakeForm(formData); bool result = a.RunCase1(3, 4); // Setting min and max formData = new FormData(); FakeForm b = new FakeForm(formData); - Run and revel in succesful result
Step5 - Some final cleanup
While we've fixed the main issue, there is still some simple refactoring that can be done to give that "cleaner, fresher feeling".
- "Inline method" SetMeanValue() in FakeForm
- "Rename" FakeForm method SetStatics() to SetFormData
- "Move" FormData to its own file FormData.cs
Step6 - The final result
We have now moved from a spaghetti code mess to nice decoupled code, and hopefully you have gotten a glimpse of what chaining automated refactorings can do for your code.
If you've done this excercise and found it worthwhile, please consider giving me your feedback so I can make this article even better - also, illustrative screenshots taken along the way would be greatly appreciated!
Happy tinkering!