Jul 25, 2007

Integration Queues in CruiseControl.NET

CruiseControl.NET version 1.3 was recently released and apart from being a native .NET 2.0 application it also included a new feature called integration queues.

So how does this work? Let's say you have a number of projects in CC.NET with dependencies on other projects. Maybe one is a set of libraries common to a number of projects, others might be data layers for different data storage providers (XML files, SQL, etc), others might be web services or user interfaces for different platforms, etc. It's not an uncommon situation.

When this is the case it's common to want to ensure that if ProjectA is built that any projects that use the output from ProjectA are also built. In CruiseControl this is managed via either a ForceBuildPublisher or a ProjectTrigger. The ForceBuildPublisher relies on ProjectA triggering a build on all the projects that depend on it.

In your ccnet.config project definition you'd have a publishers section that looks something like the following:
    <publishers>
<
statistics />
<
xmllogger />
<
forcebuild>
<
project>ProjectB</project>
</
forcebuild>
<
forcebuild>
<
project>ProjectD</project>
</
forcebuild>
</
publishers>


Now, when ProjectA finishes building, CruiseControl will force a build on projects B and D. This is not a problem but it relies on projectA knowing which projects rely on it.

An alternative to the build publisher is to use a ProjectTrigger. In this scenario ProjectB monitors the build status of ProjectA. When ProjectA successfully completes, ProjectB triggers a build. To set up a projectTrigger, edit your project definition an ccnet.config and create a trigger block as follows:

    <triggers>
<
multiTrigger>
<
triggers>
<
intervalTrigger seconds="30" />
<
projectTrigger project="ProjectA" />
</
triggers>
</
multiTrigger>
</
triggers>

In this way ProjectB now polls for changes to the source respository every 30 seconds and also keeps an eye on ProjectA's build status. To me at least, this seems a lot cleaner in that the build dependency is defined by the project with the dependency, not by the one that is depended upon.

Now let's say we have multiple developers where one is working on projectA and the other on projectB. Both developers commit their changes at the same time. What will happen?

As you would guess, CC.Net detects changes in the source for both projectA and projectB and kicks of builds for each project, concurrently. Remebering that projectB relies on the output of projectA in the build process, it's very likely that projectB will be using an older version of projectA's output in it's build process. In situations where you clean out projectA at the start of it's build then projectB may fail as projectA's output is not likely to have been built yet.

This is where integration queues come in handy. An integration queue ensures that only one build occurs at a time, avoiding all the issues that can occur with concurrent builds of dependant projects. In the example about all we need do is tell projectA and projectB to use the same integration queue and the issue disappears. In the CruiseControl.NET project definitions add the queue and, optionally, the queuePriority parameters as shown here:

  <project name="ProjectA" queue="MyBuildQueue" queuePriority="1">


The queuePriority is not really that relevant when there's just 2 projects that rely on each other, but in situations where you have a chain of 3 or more interdependent projects it can be very useful.

For example, if projectC depends on the output of projectA and projectB what happens when a commit is made on projectA?

Initially the build queue will be:
  1. projectA (building)
ProjectC then hits a timer interval and wants to check if projectA has built. The queue then looks like
  1. projectA (building)
  2. projectC (pending)
ProjectB then hits it's timer and also goes into the queue, which becomes
  1. projectA (building)
  2. projectC (pending)
  3. projectB (pending)
This is obviously undesirable as projectC will build twice, the first build against an incorrect version of projectB and the second time triggered by the successful build of projectB. If we use a queue priority then we can ensure that projectB builds before projectC. Note that for this to work, the queue priority needs to be a non-zero number and the lowest number gets the earliest queue position.

So lets say projectA is priority 1, projectB is priority2 and projectC is priority 3. Now, when the projectB timer fires it will be placed in the queue before projectC as it has a higher priority. The queue would then become
  1. projectA (building)
  2. projectB (pending)
  3. projectC (pending)
This way we ensure that projectC always compiles against the latest versions of projectA and projectB as desired.

2 comments:

  1. Love this article, very useful. Thank you!

    Our build process involves building a set of libraries, then many projects which rely on them - meaning that the dependency tree is quite large and moderately complex.

    Do you know if there's a way to allow one project to be in multiple queues? So if two programs depend on one common library, you could put both programs into separate queues, and put the library into both.

    ReplyDelete