In our previous blog post, we discussed how v2009 error handling works in synchronous APIs. In this blog post, we cover asynchronous error handling in BulkMutateJobService. If you are new to BulkMutateJobService, check out our introductory blog post and code.google.com article.
Due to its asynchronous nature, error handling in BulkMutateJobService is slightly different than in other services. Instead of throwing an ApiError object, BulkMutateJobService returns FailureResult or BatchFailureResult objects as its operation results. These objects in turn contain the relevant ApiErrors. BulkMutateJobService processes the operations within an operation stream in multiple atomic batches. If there is even one faulty operation in a batch, it causes the other non-faulty operations in the batch to fail as well. When a batch of n operations fails, you will get one FailureResult and n - 1 BatchFailureResults as results for the failed batch of operations. The FailureResult object will always be first in the batch, and will contain one ApiException object. The ApiException contains all the errors for the entire batch of operations, just like for other services. This will be followed by n - 1 BatchFailureResults, which can be viewed as placeholders, one for each operation in the batch.
For instance, assume that you are trying to create the following 9 text ads in a single OperationStream. For the purposes of this example, assume that the system processes operations in batches of 3. The errors are highlighted in red.
Index | Ad | Destination Url | Error |
0 | New York Budget Hotel Clean and close to subway. Students save 20%! www.example.com/NewYork | http://www.example.com/NewYork | |
1 | Book New York Hotel Deals Research your New York Stay. Deals up to 70% from example.com. www.example.com/NewYork | http://www.example.com/NewYork | |
2 | Hotels In New York Book your hotel room online at a lower price via example.com!! www.example.com/NewYork | http://www.example.com/NewYork | Excessive punctuation in description2 |
3 | Manhattan Best Hotel Experience the Best of Manhattan For Rates As Low As $239/Night. www.example.com/NewYork | www.example.com/NewYork | Missing schema in destination url. |
4 | New York City Hotels Discount Discounted Rooms in New York City. Get Lowest Rates Available Today. www.example.com/NewYork | http://www.example.com/NewYork | |
5 | New York City Hotels Stay Smart in New York NY. Free internet & breakfast bar. www.example.com/NewYork | http://www.example.com/NewYork | |
6 | 70% Off New York Hostels Why Pay More? From $10 We Guarantee Lowest Prices - From 1 Night www.example.com/NewYork | http://www.example.com/NewYork | |
7 | Stylish New Hotel in NYC Enjoy Four-Star Amenities & Service Chelsea/Fashion District. Book Now! www.example.com/NewYork | http://www.example.com/NewYork | |
8 | Budget Lodging in NYC Comfortable, Clean Modern Economy Hotel.Great Downtown Location! www.example.com/NewYork | http://www.example.com/NewYork |
When BulkMutateJobService processes these operations, it will return the following results:
Result[0]: FailureResult.
ApiException.errors has one ApiError
PolicyViolationError @ operations[2].operand.ad.description2
Result[1]: BatchFailureResult, operationIndexInBatch = 1
Result[2]: BatchFailureResult, operationIndexInBatch = 2
Result[3]: FailureResult.
ApiException.errors have 1 ApiError
AdError.URL_NO_SCHEME @ operations[0].operand.ad.url
Result[4]: BatchFailureResult, operationIndexInBatch = 1
Result[5]: BatchFailureResult, operationIndexInBatch = 2
Result[6]: ReturnValueResult, successfully created Ad.
Result[7]: ReturnValueResult, successfully created Ad.
Result[8]: ReturnValueResult, successfully created Ad.
As shown in the results above, there is a 1:1 correspondence between the number of operations and number of results. You can get the successful operations by scanning the return value for results which are not FailureResult or BatchFailureResult (operations 6, 7 and 8 in this case). Then, scan through the results to locate FailureResult objects (which always appear at the beginning of the failed batch) to get the operation that actually failed (operations 2 and 3 in this case). You can now pick the failed operations that do not correspond to the ones obtained from FailureResult, and make a new job for them (0, 1, 4, 5). Note that the OGNL path in FailureResult is relative to the batch and not the operationstream, so you have to offset it by the index of FailureResult to get the failed operation (e.g. Results[3] is a FailureResult and its OGNL path is operations[0].operand.ad.url, so the failing operator in the original list is 3 + 0 = operations[3]).
A BulkMutateJob may also return the following error results, even though they're not common:
LostResult: This happens when BMJS has lost results for some of a job's operations. You can recover from this error by checking numFailedOperations and numUnprocessedOperations for the job. If both are zero, or if the number of errors in ApiError matches numFailedOperations, then the operations were successful, but the results are missing. In any other case, you have to retrieve the entities that were targeted by the operations and see if the operation actually succeeded or not.
UnprocessedResult: This indicates that the mutate operation was not processed. This could happen because the workflow implementation prevented the operation from being processed (e.g. the operations exceed API quota). You can recover from this issue by checking the job status, making sure that you have sufficient API quota left, and simply retrying the operations in a new job.
In an upcoming blog post, we will cover the most common errors you may get in v2009, and how to address them. We've included support for error handling in all of our client libraries to help get you started, so please try it out and share your feedback with us on the forum or the projects' issue trackers.
-- Anash P. Oommen, AdWords API Team