Long-running operations
Some API operations can take a relatively long time to complete, such as rebooting a virtual machine or performing speech recognition on a large file. Here, a "long time" is generally "anything over about 10 seconds". (It may not seem a long time in human terms, but it's longer than you want an individual RPC to last.)
The long-running operation pattern in APIs
The long-running operation pattern is designed to avoid clients having to worry about having too many pending RPCs, or what happens if a network connection is dropped while a long-running operation is taking place.
The pattern is simple:
- The client makes an RPC request in the normal way, representing their intent ("reboot this VM" or "recognize text from this audio data").
- Assuming the request is valid, the server responds with an
Operation
response which contains an operation name. - The client can poll using that operation name, to determine whether the long-running operation has completed, whether it's perhaps failed, any metadata indicating progress if it's still running, etc.
In many ways, long-running operations are the API equivalent of the asynchronous Task<T>
type in .NET.
.NET library support for long-running operations
The Google Cloud Libraries for .NET are designed to make the pattern easy to work with from client code. Any RPC which returns a long-running operation is represented as a method with a return type of Operation<TResponse, TMetadata>
with suitable type arguments for TResponse
(the response type) and TMetadata
(the metadata type). The response and metadata types are always protobuf messages.
The Operation<TResponse, TMetadata>
type has useful members:
PollOnce()
/PollOnceAsync()
: makes an RPC to retrieve the latest state of the long-running operation. Note that the fresh state is returned as a newOperation<TResponse, TMetadata>
; these methods do not modify the object it is called on.Exception
: the exception represented in the operation, if it has failed.IsCompleted
: indicates if the operation has completed; this will return true even if it has failed.IsFaulted
: indicates if the operation has completed with a failure.Metadata
: retrieves the metadata extracted from the underlying operation protobuf message.Name
: the resource-name of the operation, which can be persisted and then used to retrieve the current state of the operation at another time.Result
: retrieves the result of the underlying operation protobuf message, indicating the operation's final result, or throws an exception if the operation has not completed or has faulted.PollUntilCompleted()
/PollUntilCompletedAsync()
: makes multiple RPCs to retrieve the long-running operation, until it has completed (either successfully or ending in failure). The polling rate and other settings can be configured via a parameter of typePollSettings
. The final state is returned as a newOperation<TResponse, TMetadata>
; these methods do not modify the object it is called on.Client
: obtains theOperationsClient
used to make RPCs relating to long-running operations. (Most user code does not need to access this, as using thePollOnce()
andPollOnceAsync()
methods is typically simpler.)
Sample code for polling:
var client = ImageAnnotatorClient.Create(); var textDetectionFeature = new Feature { Type = Feature.Types.Type.DocumentTextDetection, Model = "builtin/latest" }; var request = new AsyncAnnotateFileRequest { Features = { textDetectionFeature }, InputConfig = { GcsSource = new GcsSource { Uri = "gs://..." } }, OutputConfig = { GcsDestination = new GcsDestination { Uri = "gs://..." } } }; var batchRequest = new AsyncBatchAnnotateFilesRequest { Parent = "...", Requests = { request } }; var operation = client.AsyncBatchAnnotateFiles(batchRequest); // Almost certainly False at this point Console.WriteLine(operation.IsCompleted); // Poll until the operation is completed. Note that this // can block *forever* with the default poll settings. operation = operation.PollUntilCompleted(); // Print out the final result (or throw an exception if it failed). Console.WriteLine(operation.Result);