Tip for those of you using CruiseControl.Net (CC.NET) with Perforce - error: Request too large

I use CC.Net quite a lot now with Perforce and ClearCase and I was recently asked to help out a team who were having some problems.

A CI project had been setup on CC.NET and it "seemed" to be running fine.

Someone would checkin the file and the project would detect the changes. Unfortunately the files on the build server were not actually being updated :S

Looking at the log files revealed the following (replaced project specific details with general text):

First it detects the change:

2008-03-31 14:44:56,734 [PROJECTNAME:INFO] Project: 'PROJECTNAME' is first in queue: 'QUEUE.NAME' and shall start integration.

2008-03-31 14:44:56,734 [PROJECTNAME:DEBUG] Perforce plugin - running:FileName: [C:\Program Files\Perforce\p4.exe] -- Arguments: [-s -c workspacename -p perforceserver -u perforceaccount changes -s submitted "//depot/PathToProject/..."@2008/03/31:14:02:52,@2008/03/31:14:44:56] -- WorkingDirectory: [] -- StandardInputContent: [] -- Timeout: [2147483647]

2008-03-31 14:44:56,734 [PROJECTNAME:DEBUG] Starting process [C:\Program Files\Perforce\p4.exe] in working directory [] with arguments [-s -c workspacename -p perforceserver -u perforceaccount changes -s submitted "//depot/PathToProject/..." @2008/03/31:14:02:52,@2008/03/31:14:44:56]

2008-03-31 14:44:56,859 [PROJECTNAME:DEBUG] info: Change 189451 on 2008/03/31 by developer@projectbuild 'Build: revert previous change ('

2008-03-31 14:44:56,859 [PROJECTNAME:DEBUG] exit: 0

Then it describes the change (by specifying the changelist number):

2008-03-31 14:44:56,859 [PROJECTNAME:DEBUG] Perforce plugin - running:FileName: [C:\Program Files\Perforce\p4.exe] -- Arguments: [-s -c workspacename -p perforceserver -u perforceaccount describe -s 189451] -- WorkingDirectory: [] -- StandardInputContent: [] -- Timeout: [2147483647]

2008-03-31 14:44:56,859 [PROJECTNAME:DEBUG] Starting process [C:\Program Files\Perforce\p4.exe] in working directory [] with arguments [-s -c workspacename -p perforceserver -u perforceaccount describe -s 189451]

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text: Change 189451 by developer@projectbuild on 2008/03/31 14:40:44

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text:

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text: Build: revert previous change (cruisecontrol check)

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text:

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text: Affected files ...

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text:

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] info1: //depot/PathToProject/ChangedFile.cs#5 edit

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] text:

2008-03-31 14:44:56,968 [PROJECTNAME:DEBUG] exit: 0

So far so good.

CruiseControl.Net calls Perforce passing it the server, the user, the workspace and the view to check for changes. It then gets back the relevant changelist number(s). Using the return changelist number(s) it describes the changes.

Next step is to retrieve the changes:

2008-03-31 14:44:56,984 [PROJECTNAME:INFO] 1 modification detected.

2008-03-31 14:44:56,984 [PROJECTNAME:INFO] Building: intervalTrigger triggered a build (ForceBuild)

2008-03-31 14:44:56,984 [PROJECTNAME:INFO] Getting source from Perforce: C:\Program Files\Perforce\p4.exe -s -c workspacename -p perforceserver -u perforceaccount sync

2008-03-31 14:44:56,984 [PROJECTNAME:DEBUG] Perforce plugin - running:FileName: [C:\Program Files\Perforce\p4.exe] -- Arguments: -s -c workspacename -p perforceserver -u perforceaccount sync] -- WorkingDirectory: [] -- StandardInputContent: [] -- Timeout: [2147483647]

2008-03-31 14:44:56,984 [PROJECTNAME:DEBUG] Starting process [C:\Program Files\Perforce\p4.exe] in working directory [] with arguments [-s -c workspacename -p perforceserver -u perforceaccount sync]

2008-03-31 14:44:58,250 [PROJECTNAME:DEBUG] error: Request too large (over 200000); see 'p4 help maxresults'.

2008-03-31 14:44:58,250 [PROJECTNAME:DEBUG] exit: 1

 The Problem:

The problem is that CruiseControl.Net is passing Perforce the workspace and the view when it is checking for changes but only specifying the workspace when it is performing the update (to be fair the CC.NET documentation actually tells you that they won't use the view for synching or labelling). The team used one workspace for more than one project and restricted the view on that workspace for their CruiseControl.Net projects (you could argue that you should have one workspace per project but this approach made it easier for them to maintain). When it tried to synch the entire workspace it hit a limit and "....the computer says No..." was the result (as seen above). Unfortunately this doesn't cause the build to break so everything seems fine which is a bit worrying.

Possible fixes:

  • Update the Perforce code so that it specifies the view as well as the workspace when doing a synch
  • Tell people to create a workspace per project the reduce the risk of this happening again
  • Ask the Perforce admin to increase the limit so that Perforce doesn't return an error

The second choice would be hard to enforce (although you could try), the third option isn't a real fix (i.e. if people continue to use a single workspace you'll come across projects that would cross the limit again in the future and you don't really want project A synching an entire workspace and in doing so update the files for project B, C and D).

So I went with the first option (I'll be asking the CC.NET team why they don't use the view and point to this post as a possible change for them to use if they wish. If they can tell me a good reason why you shouldn't do this or indeed if anyone else knows why you shouldn't then I'll update this post with the reason).

So now you have two options (unless this change is added to the next CC.NET release):

  • Update the source directly and have your own customised version of CC.Net (actually it would be a customised version of ThoughtWorks.CruiseControl.Core.dll)
  • Create a plugin that provides the fix (it still requires you to maintain the Perforce related code but means you can easily pass the plugin to other teams and you're not as tied to the Core assembly).

I tried both and prefer option 2.

So where is the code you need to change (you need to download the sourcecode for CruiseControl.Net)?

  • CruiseControl.NET-1.3.0.2918.source\project\core\sourcecontrol\Perforce

What file actually needs the change?

  • CruiseControl.NET-1.3.0.2918.source\project\core\sourcecontrol\Perforce\P4.cs

Within this file there is a private method (line 248):

private string CreateSyncCommandLine()
{
         string commandline = "sync ";

         if (ForceSync)
         {
            commandline +=
"-f ";
         }

         return commandline;
}

The change that could be applied is as follows:

private string CreateSyncCommandLine()
{
         string commandline = "sync ";

         if (ForceSync)
         {
            commandline +=
"-f  ";
         }

          commandline += ViewForSyncCommandLine;

          return commandline;
}

ViewForSyncCommandLine was a new private method I created and added:

private string ViewForSyncCommandLine
{
         get { return View.Replace(",", " "); }
}

Great, so how do I create a plugin and how do I use that in a CruiseControl.Net project?

  • Start VS and create a C# Library project (call it ccnet.YourName.plugin. CruiseControl.Net looks for plugins matching the following ccnet.*.plugin.dll).
  • Add the Perforce folder (CruiseControl.NET-1.3.0.2918.source\project\core\sourcecontrol\Perforce) to your Library project.
  • Update the namespaces to reflect your project.
  • Add a reference to  the ThoughtWorks.CruiseControl.Core, NetReflector and ThoughtWorks.CruiseControl.Remote assemblies (resharper/vs should help add the relevant usings but reference your own implementation of the P4 classes/interfaces just to keep things simple).
  • Apply the changes mentioned to the P4.cs file above (also change the ReflectorType attribute to reflect your custom plugin e.g.:
             [ReflectorType("p4custom")]
            
    public class P4 : ISourceControl
  • Build the project.

Now you don't have to copy all the files across and there are different ways of doing this (you could try to inherit as much as possible from the Core assembly). This is just one possible way of doing it that gives me the flexibility of customising the P4 block further should I need to and reduce my dependency on the Thoughtworks assemblies (the hope is that support for this is added but I still need to find out why they decided not to do it this way, so be warned there may be a good reason for not doing this).

You should now have an assembly you can drop into the CruiseControl.NET\server folder.

The only other thing you would need to change is the sourcecontrol block entry type name (all the perforce settings stay the same) in your CruiseControl.Net project e.g.:

<!-- Perforce Source Control Entry -->
<
sourcecontrol type="p4custom">
<
view>//depot/PathToProject/...</view>
<
executable>pathtop4exe</executable>
<
client>workspacename</client>
<
user>perforceaccount</user>
<
port>perforceserver</port>
<
applyLabel>false</applyLabel>
<
autoGetSource>true</autoGetSource>
<
forceSync>false</forceSync>
<
p4WebURLFormat>perforceurl</p4WebURLFormat>
</
sourcecontrol>

Restart the CruiseControl.Net service and test the project that was giving you trouble.

I hope this helps anyone that comes across this problem. If you have <applyLabel>true</applyLabel> in your block then you need to take into account that labelling does not take into account the view (I have it set to false and use NAnt when I need to label anything but it is something to consider if you do use it).

John 

 

1 Comment

  • Thanks for sharing your research. It was very useful and this works as expected after following your instructions.

    You saved me a lot of time.

Comments have been disabled for this content.