0
# Long-Running Operations
1
2
Support for asynchronous operations that don't complete immediately, providing status tracking, cancellation, and result retrieval. This is essential for operations that may take minutes or hours to complete.
3
4
## Core Operation Types
5
6
### Operation
7
8
Represents a long-running operation with status tracking and result handling.
9
10
```java { .api }
11
class Operation {
12
String getName(); // Unique operation identifier
13
Any getMetadata(); // Operation-specific metadata
14
boolean getDone(); // True if operation is complete
15
Status getError(); // Error status if operation failed
16
Any getResponse(); // Operation result if successful
17
18
static Operation.Builder newBuilder();
19
Operation.Builder toBuilder();
20
static Operation parseFrom(byte[] data);
21
boolean hasError();
22
boolean hasResponse();
23
}
24
25
interface OperationOrBuilder {
26
String getName();
27
Any getMetadata();
28
boolean getDone();
29
boolean hasError();
30
Status getError();
31
boolean hasResponse();
32
Any getResponse();
33
}
34
```
35
36
### OperationInfo
37
38
Metadata about operation types and their expected response/metadata types.
39
40
```java { .api }
41
class OperationInfo {
42
String getResponseType(); // Fully qualified type name of response
43
String getMetadataType(); // Fully qualified type name of metadata
44
45
static OperationInfo.Builder newBuilder();
46
}
47
```
48
49
## Request/Response Types
50
51
### GetOperationRequest
52
53
Request to retrieve the current status of an operation.
54
55
```java { .api }
56
class GetOperationRequest {
57
String getName(); // Operation name to retrieve
58
59
static GetOperationRequest.Builder newBuilder();
60
}
61
```
62
63
### ListOperationsRequest
64
65
Request to list operations matching specified criteria.
66
67
```java { .api }
68
class ListOperationsRequest {
69
String getName(); // Collection name (e.g., "operations")
70
String getFilter(); // Optional filter expression
71
int getPageSize(); // Maximum operations to return
72
String getPageToken(); // Token for pagination
73
74
static ListOperationsRequest.Builder newBuilder();
75
}
76
```
77
78
### ListOperationsResponse
79
80
Response containing a list of operations and pagination information.
81
82
```java { .api }
83
class ListOperationsResponse {
84
repeated Operation getOperationsList();
85
String getNextPageToken();
86
87
static ListOperationsResponse.Builder newBuilder();
88
int getOperationsCount();
89
Operation getOperations(int index);
90
}
91
```
92
93
### CancelOperationRequest
94
95
Request to cancel a running operation.
96
97
```java { .api }
98
class CancelOperationRequest {
99
String getName(); // Operation name to cancel
100
101
static CancelOperationRequest.Builder newBuilder();
102
}
103
```
104
105
### DeleteOperationRequest
106
107
Request to delete an operation record.
108
109
```java { .api }
110
class DeleteOperationRequest {
111
String getName(); // Operation name to delete
112
113
static DeleteOperationRequest.Builder newBuilder();
114
}
115
```
116
117
### WaitOperationRequest
118
119
Request to wait for an operation to complete.
120
121
```java { .api }
122
class WaitOperationRequest {
123
String getName();
124
Duration getTimeout(); // Maximum time to wait
125
126
static WaitOperationRequest.Builder newBuilder();
127
}
128
```
129
130
## Usage Examples
131
132
### Creating and Monitoring Operations
133
134
```java
135
import com.google.longrunning.Operation;
136
import com.google.longrunning.GetOperationRequest;
137
import com.google.protobuf.Any;
138
import com.google.rpc.Status;
139
140
// Create a new operation
141
Operation operation = Operation.newBuilder()
142
.setName("operations/my-operation-123")
143
.setDone(false)
144
.build();
145
146
// Check operation status
147
public void checkOperationStatus(String operationName) {
148
GetOperationRequest request = GetOperationRequest.newBuilder()
149
.setName(operationName)
150
.build();
151
152
// This would typically be sent to the service
153
Operation operation = operationsClient.getOperation(request);
154
155
if (operation.getDone()) {
156
if (operation.hasError()) {
157
Status error = operation.getError();
158
System.err.println("Operation failed: " + error.getMessage());
159
} else if (operation.hasResponse()) {
160
Any response = operation.getResponse();
161
System.out.println("Operation completed successfully");
162
// Process response based on expected type
163
}
164
} else {
165
System.out.println("Operation still in progress: " + operation.getName());
166
}
167
}
168
```
169
170
### Polling Pattern
171
172
```java
173
import java.util.concurrent.TimeUnit;
174
175
public class OperationPoller {
176
private static final int MAX_POLL_ATTEMPTS = 100;
177
private static final long POLL_INTERVAL_SECONDS = 5;
178
179
public Operation waitForOperation(String operationName) throws InterruptedException {
180
for (int attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
181
GetOperationRequest request = GetOperationRequest.newBuilder()
182
.setName(operationName)
183
.build();
184
185
Operation operation = operationsClient.getOperation(request);
186
187
if (operation.getDone()) {
188
return operation;
189
}
190
191
System.out.println("Operation " + operationName + " still running, attempt " + (attempt + 1));
192
TimeUnit.SECONDS.sleep(POLL_INTERVAL_SECONDS);
193
}
194
195
throw new RuntimeException("Operation did not complete within timeout");
196
}
197
}
198
```
199
200
### Listing Operations
201
202
```java
203
import com.google.longrunning.ListOperationsRequest;
204
import com.google.longrunning.ListOperationsResponse;
205
206
public void listAllOperations(String collectionName) {
207
String pageToken = "";
208
209
do {
210
ListOperationsRequest request = ListOperationsRequest.newBuilder()
211
.setName(collectionName)
212
.setPageSize(50)
213
.setPageToken(pageToken)
214
.build();
215
216
ListOperationsResponse response = operationsClient.listOperations(request);
217
218
for (Operation operation : response.getOperationsList()) {
219
System.out.println("Operation: " + operation.getName() +
220
", Done: " + operation.getDone());
221
}
222
223
pageToken = response.getNextPageToken();
224
} while (!pageToken.isEmpty());
225
}
226
```
227
228
### Filtering Operations
229
230
```java
231
public void listPendingOperations() {
232
ListOperationsRequest request = ListOperationsRequest.newBuilder()
233
.setName("operations")
234
.setFilter("done = false") // Only show incomplete operations
235
.setPageSize(100)
236
.build();
237
238
ListOperationsResponse response = operationsClient.listOperations(request);
239
240
System.out.println("Pending operations:");
241
for (Operation operation : response.getOperationsList()) {
242
System.out.println("- " + operation.getName());
243
}
244
}
245
```
246
247
### Operation Cancellation
248
249
```java
250
import com.google.longrunning.CancelOperationRequest;
251
252
public void cancelOperation(String operationName) {
253
CancelOperationRequest request = CancelOperationRequest.newBuilder()
254
.setName(operationName)
255
.build();
256
257
try {
258
operationsClient.cancelOperation(request);
259
System.out.println("Cancellation requested for operation: " + operationName);
260
261
// Verify cancellation
262
Operation operation = operationsClient.getOperation(
263
GetOperationRequest.newBuilder().setName(operationName).build());
264
265
if (operation.getDone() && operation.hasError()) {
266
Status error = operation.getError();
267
if (error.getCode() == Code.CANCELLED.getNumber()) {
268
System.out.println("Operation successfully cancelled");
269
}
270
}
271
} catch (Exception e) {
272
System.err.println("Failed to cancel operation: " + e.getMessage());
273
}
274
}
275
```
276
277
### Working with Typed Responses
278
279
```java
280
import com.google.protobuf.InvalidProtocolBufferException;
281
282
// Example response type
283
class ProcessingResult {
284
private String resultId;
285
private int itemsProcessed;
286
// ... other fields
287
}
288
289
public ProcessingResult handleTypedOperation(Operation operation)
290
throws InvalidProtocolBufferException {
291
292
if (!operation.getDone()) {
293
throw new IllegalStateException("Operation not complete");
294
}
295
296
if (operation.hasError()) {
297
Status error = operation.getError();
298
throw new RuntimeException("Operation failed: " + error.getMessage());
299
}
300
301
if (!operation.hasResponse()) {
302
throw new IllegalStateException("Operation completed but no response");
303
}
304
305
// Unpack the Any response to the expected type
306
Any response = operation.getResponse();
307
if (response.is(ProcessingResult.class)) {
308
return response.unpack(ProcessingResult.class);
309
} else {
310
throw new IllegalArgumentException("Unexpected response type: " + response.getTypeUrl());
311
}
312
}
313
```
314
315
### Operation Metadata Handling
316
317
```java
318
// Example metadata type
319
class ProcessingMetadata {
320
private int totalItems;
321
private int processedItems;
322
private String currentPhase;
323
// ... other fields
324
}
325
326
public void displayProgress(Operation operation) throws InvalidProtocolBufferException {
327
if (operation.hasMetadata()) {
328
Any metadata = operation.getMetadata();
329
330
if (metadata.is(ProcessingMetadata.class)) {
331
ProcessingMetadata progress = metadata.unpack(ProcessingMetadata.class);
332
333
double percentComplete = (double) progress.getProcessedItems() / progress.getTotalItems() * 100;
334
335
System.out.printf("Operation %s: %.1f%% complete (%s)\n",
336
operation.getName(),
337
percentComplete,
338
progress.getCurrentPhase());
339
}
340
}
341
}
342
```
343
344
### Delete Operations
345
346
```java
347
import com.google.longrunning.DeleteOperationRequest;
348
349
public void cleanupCompletedOperations() {
350
// First, list completed operations
351
ListOperationsRequest listRequest = ListOperationsRequest.newBuilder()
352
.setName("operations")
353
.setFilter("done = true")
354
.build();
355
356
ListOperationsResponse response = operationsClient.listOperations(listRequest);
357
358
// Delete operations older than a certain threshold
359
long cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
360
361
for (Operation operation : response.getOperationsList()) {
362
// This example assumes operation names contain timestamps
363
// In practice, you'd use metadata or other fields to determine age
364
if (shouldDeleteOperation(operation, cutoffTime)) {
365
DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
366
.setName(operation.getName())
367
.build();
368
369
try {
370
operationsClient.deleteOperation(deleteRequest);
371
System.out.println("Deleted operation: " + operation.getName());
372
} catch (Exception e) {
373
System.err.println("Failed to delete operation " + operation.getName() + ": " + e.getMessage());
374
}
375
}
376
}
377
}
378
379
private boolean shouldDeleteOperation(Operation operation, long cutoffTime) {
380
// Implementation depends on how you track operation timestamps
381
// This is just an example
382
return true; // Replace with actual logic
383
}
384
```
385
386
## Best Practices
387
388
### Operation Naming
389
390
Operations should have unique, descriptive names:
391
392
```java
393
// Good: includes service, resource type, and unique ID
394
String operationName = "operations/compute/instances/create-instance-abc123";
395
396
// Good: hierarchical naming for organization
397
String operationName = "projects/my-project/operations/backup-database-456789";
398
```
399
400
### Error Handling
401
402
Always check for errors before processing results:
403
404
```java
405
public void handleOperation(Operation operation) {
406
if (!operation.getDone()) {
407
// Operation still in progress
408
return;
409
}
410
411
if (operation.hasError()) {
412
// Handle error case
413
Status error = operation.getError();
414
handleOperationError(error);
415
return;
416
}
417
418
if (operation.hasResponse()) {
419
// Process successful result
420
processOperationResult(operation.getResponse());
421
}
422
}
423
```
424
425
### Timeout Strategies
426
427
Implement appropriate timeout strategies for different operation types:
428
429
```java
430
public class OperationConfig {
431
// Short operations: file uploads, simple transformations
432
public static final Duration SHORT_OPERATION_TIMEOUT = Duration.newBuilder()
433
.setSeconds(300) // 5 minutes
434
.build();
435
436
// Medium operations: data processing, image generation
437
public static final Duration MEDIUM_OPERATION_TIMEOUT = Duration.newBuilder()
438
.setSeconds(1800) // 30 minutes
439
.build();
440
441
// Long operations: large data imports, model training
442
public static final Duration LONG_OPERATION_TIMEOUT = Duration.newBuilder()
443
.setSeconds(7200) // 2 hours
444
.build();
445
}
446
```
447
448
### Resource Cleanup
449
450
Always clean up completed operations to avoid resource leaks:
451
452
```java
453
public void performOperationWithCleanup(String operationName) {
454
try {
455
Operation result = waitForOperation(operationName);
456
processOperationResult(result);
457
} finally {
458
// Clean up the operation record
459
try {
460
DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
461
.setName(operationName)
462
.build();
463
operationsClient.deleteOperation(deleteRequest);
464
} catch (Exception e) {
465
System.err.println("Warning: Failed to clean up operation " + operationName);
466
}
467
}
468
}
469
```