Discover v2009: Asynchronous requests with the BulkMutateJobService

Wednesday, October 28, 2009


Delegation of tasks: we use this concept in our everyday lives when we pay someone to make us coffee, ask a colleague to take care of something while we run to the dentist, or tell our kids or younger siblings to handle a chore. What's common in all these situations? Well, you assign someone a task, possibly consisting of several parts, and then you wait for it to be completed. You can focus on other things while the task is being worked on, but you can still periodically ask what the status is (not too often, or they'll think you're annoying). Eventually they'll tell you it's done, and let you know whether it was successful or not. It's a simple process, and we intuitively know what to expect from it.

Well, you can now delegate complex tasks via the AdWords API! v2009 introduced a unified interface across all services for performing changes: the mutate method. This unification allowed us to develop the BulkMutateJobService, where you can provide a list of mutate operations to be performed for a number of different services, by scheduling a job which gets executed asynchronously. Much like the v13 ReportService, you can then poll for the job status, and once you get back the signal that it's been completed, you can ask for the results.

Let's dive into the details. BulkMutateJobs consist of the following:

  • BulkMutateRequests: these are the different parts of the job. The first one you submit should include a count of how many parts your job has; you submit one at a time, and once the last one is submitted, the job automatically starts. This helps build very large jobs, by avoiding the sending of a single, gigantic request.
  • OperationStreams: a BulkMutateRequest contains one or more OperationStreams, which can be executed in parallel (only in certain conditions, namely if their scopes target different campaigns).
  • Operations: each OperationStream contains a list of Operations, which are performed sequentially.
So to create a job with two parts, you would simply do:
BulkMutateJob job = new BulkMutateJob();
job.setNumRequestParts(2);
and fill in the details for the first part:
BulkMutateRequest part1 = new BulkMutateRequest();
part1.setPartIndex(0);
Then create an operation stream, consisting of several operations (they can even use different operators) and use it in the part:
OperationStream adStream = new OperationStream();
adStream.setScopingEntityId(scopingEntityId);
adStream.setOperations(new Operation[] {
new AdGroupAdOperation(Operator.ADD, null, adGroupAd1, null),
new AdGroupAdOperation(Operator.ADD, null, adGroupAd2, null)});
part1.setOperationStreams(new OperationStream[] {adStream});
Once we're done with that, we can create the JobOperation and submit the first part to the service:
JobOperation jobOperation = new JobOperation();
jobOperation.setOperand(job);
jobOperation.setOperator(Operator.ADD);
job = bulkMutateJobService.mutate(jobOperation);
Long jobId = job.getId();
Submitting the second part is similar, but now we must use the SET operator, since we're adding to an existing job, not creating a new one. Also, we must provide the job ID, so that the system knows which job we're submitting a new part for.
BulkMutateRequest part2 = new BulkMutateRequest();
part2.setPartIndex(1);
// ... OperationStream code goes here ...
job = new BulkMutateJob();
job.setId(jobId); // Here we set the ID to the one we got from
// the response to the first part

job.setRequest(part2);
job = bulkMutateJobService.mutate(new JobOperation(Operator.SET, null, job));
Once all the parts are submitted, the system automatically starts the job. Querying the status is easy, just perform a get with the job ID:
BulkMutateJobSelector selector = new BulkMutateJobSelector();
selector.setJobIds(new long[] {jobId});
BulkMutateJob[] jobs = bulkMutateJobService.get(selector);
job = jobs[0];
Don't query too often, though, or you may run trigger a rate-limiting protection! We recommend 30 seconds as a good value to wait between queries. Once the response comes back with a COMPLETED status, we can retrieve the results for both parts, by setting the resultPartIndex:
selector.setResultPartIndex(0);
jobs = bulkMutateJobService.get(selector);
job = jobs[0];
OperationResult[] operationResults1 =
job.getResult().getOperationStreamResults()[0].getOperationResults();

selector.setResultPartIndex(1);
jobs = bulkMutateJobService.get(selector);
job = jobs[0];
OperationResult[] operationResults2 =
job.getResult().getOperationStreamResults()[0].getOperationResults();
The BulkMutateResult object in the result field of the response includes a list of OperationStreamResults, which themselves are an array of OperationResults. Going through this hierarchy allows you to see which operations were successful and which ones failed, and act accordingly.

Note: If you're looking for a more complete example of using the BulkMutateJobService, please check the examples/demo folder in the client library of your choice. Also check Performing Bulk Jobs with BulkMutateJobService in the API documentation.

With the BulkMutateJobService you can provide as much work as possible to the API in one go and let it do its processing, without worrying about timeouts or broken connections. It won't affect your error handling, either, since the responses detail exactly what happened to all of the operations you performed. It's just an efficient way of getting a lot of work done in a single task. Come in and have a seat, the AdWords API is ready to take your order!

--Sérgio Gomes, AdWords API Team

This is the first of a series of posts on the exciting features of AdWords API v2009. In next week's edition of Discover v2009, we'll tell you all about getting ideas from the new TargetingIdeaService.