CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-api-grpc--proto-google-common-protos

Java Protocol Buffer classes for Google's common protos, providing type-safe access to core Google Cloud API structures and gRPC service definitions

Pending
Overview
Eval results
Files

longrunning-operations.mddocs/

Long-Running Operations

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.

Core Operation Types

Operation

Represents a long-running operation with status tracking and result handling.

class Operation {
  String getName();          // Unique operation identifier
  Any getMetadata();        // Operation-specific metadata
  boolean getDone();        // True if operation is complete
  Status getError();        // Error status if operation failed
  Any getResponse();        // Operation result if successful
  
  static Operation.Builder newBuilder();
  Operation.Builder toBuilder();
  static Operation parseFrom(byte[] data);
  boolean hasError();
  boolean hasResponse();
}

interface OperationOrBuilder {
  String getName();
  Any getMetadata();
  boolean getDone();
  boolean hasError();
  Status getError();
  boolean hasResponse();
  Any getResponse();
}

OperationInfo

Metadata about operation types and their expected response/metadata types.

class OperationInfo {
  String getResponseType();   // Fully qualified type name of response
  String getMetadataType();   // Fully qualified type name of metadata
  
  static OperationInfo.Builder newBuilder();
}

Request/Response Types

GetOperationRequest

Request to retrieve the current status of an operation.

class GetOperationRequest {
  String getName();  // Operation name to retrieve
  
  static GetOperationRequest.Builder newBuilder();
}

ListOperationsRequest

Request to list operations matching specified criteria.

class ListOperationsRequest {
  String getName();         // Collection name (e.g., "operations")
  String getFilter();       // Optional filter expression
  int getPageSize();        // Maximum operations to return
  String getPageToken();    // Token for pagination
  
  static ListOperationsRequest.Builder newBuilder();
}

ListOperationsResponse

Response containing a list of operations and pagination information.

class ListOperationsResponse {
  repeated Operation getOperationsList();
  String getNextPageToken();
  
  static ListOperationsResponse.Builder newBuilder();
  int getOperationsCount();
  Operation getOperations(int index);
}

CancelOperationRequest

Request to cancel a running operation.

class CancelOperationRequest {
  String getName();  // Operation name to cancel
  
  static CancelOperationRequest.Builder newBuilder();
}

DeleteOperationRequest

Request to delete an operation record.

class DeleteOperationRequest {
  String getName();  // Operation name to delete
  
  static DeleteOperationRequest.Builder newBuilder();
}

WaitOperationRequest

Request to wait for an operation to complete.

class WaitOperationRequest {
  String getName();
  Duration getTimeout();  // Maximum time to wait
  
  static WaitOperationRequest.Builder newBuilder();
}

Usage Examples

Creating and Monitoring Operations

import com.google.longrunning.Operation;
import com.google.longrunning.GetOperationRequest;
import com.google.protobuf.Any;
import com.google.rpc.Status;

// Create a new operation
Operation operation = Operation.newBuilder()
    .setName("operations/my-operation-123")
    .setDone(false)
    .build();

// Check operation status
public void checkOperationStatus(String operationName) {
    GetOperationRequest request = GetOperationRequest.newBuilder()
        .setName(operationName)
        .build();
    
    // This would typically be sent to the service
    Operation operation = operationsClient.getOperation(request);
    
    if (operation.getDone()) {
        if (operation.hasError()) {
            Status error = operation.getError();
            System.err.println("Operation failed: " + error.getMessage());
        } else if (operation.hasResponse()) {
            Any response = operation.getResponse();
            System.out.println("Operation completed successfully");
            // Process response based on expected type
        }
    } else {
        System.out.println("Operation still in progress: " + operation.getName());
    }
}

Polling Pattern

import java.util.concurrent.TimeUnit;

public class OperationPoller {
    private static final int MAX_POLL_ATTEMPTS = 100;
    private static final long POLL_INTERVAL_SECONDS = 5;
    
    public Operation waitForOperation(String operationName) throws InterruptedException {
        for (int attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
            GetOperationRequest request = GetOperationRequest.newBuilder()
                .setName(operationName)
                .build();
            
            Operation operation = operationsClient.getOperation(request);
            
            if (operation.getDone()) {
                return operation;
            }
            
            System.out.println("Operation " + operationName + " still running, attempt " + (attempt + 1));
            TimeUnit.SECONDS.sleep(POLL_INTERVAL_SECONDS);
        }
        
        throw new RuntimeException("Operation did not complete within timeout");
    }
}

Listing Operations

import com.google.longrunning.ListOperationsRequest;
import com.google.longrunning.ListOperationsResponse;

public void listAllOperations(String collectionName) {
    String pageToken = "";
    
    do {
        ListOperationsRequest request = ListOperationsRequest.newBuilder()
            .setName(collectionName)
            .setPageSize(50)
            .setPageToken(pageToken)
            .build();
        
        ListOperationsResponse response = operationsClient.listOperations(request);
        
        for (Operation operation : response.getOperationsList()) {
            System.out.println("Operation: " + operation.getName() + 
                             ", Done: " + operation.getDone());
        }
        
        pageToken = response.getNextPageToken();
    } while (!pageToken.isEmpty());
}

Filtering Operations

public void listPendingOperations() {
    ListOperationsRequest request = ListOperationsRequest.newBuilder()
        .setName("operations")
        .setFilter("done = false")  // Only show incomplete operations
        .setPageSize(100)
        .build();
    
    ListOperationsResponse response = operationsClient.listOperations(request);
    
    System.out.println("Pending operations:");
    for (Operation operation : response.getOperationsList()) {
        System.out.println("- " + operation.getName());
    }
}

Operation Cancellation

import com.google.longrunning.CancelOperationRequest;

public void cancelOperation(String operationName) {
    CancelOperationRequest request = CancelOperationRequest.newBuilder()
        .setName(operationName)
        .build();
    
    try {
        operationsClient.cancelOperation(request);
        System.out.println("Cancellation requested for operation: " + operationName);
        
        // Verify cancellation
        Operation operation = operationsClient.getOperation(
            GetOperationRequest.newBuilder().setName(operationName).build());
        
        if (operation.getDone() && operation.hasError()) {
            Status error = operation.getError();
            if (error.getCode() == Code.CANCELLED.getNumber()) {
                System.out.println("Operation successfully cancelled");
            }
        }
    } catch (Exception e) {
        System.err.println("Failed to cancel operation: " + e.getMessage());
    }
}

Working with Typed Responses

import com.google.protobuf.InvalidProtocolBufferException;

// Example response type
class ProcessingResult {
    private String resultId;
    private int itemsProcessed;
    // ... other fields
}

public ProcessingResult handleTypedOperation(Operation operation) 
    throws InvalidProtocolBufferException {
    
    if (!operation.getDone()) {
        throw new IllegalStateException("Operation not complete");
    }
    
    if (operation.hasError()) {
        Status error = operation.getError();
        throw new RuntimeException("Operation failed: " + error.getMessage());
    }
    
    if (!operation.hasResponse()) {
        throw new IllegalStateException("Operation completed but no response");
    }
    
    // Unpack the Any response to the expected type
    Any response = operation.getResponse();
    if (response.is(ProcessingResult.class)) {
        return response.unpack(ProcessingResult.class);
    } else {
        throw new IllegalArgumentException("Unexpected response type: " + response.getTypeUrl());
    }
}

Operation Metadata Handling

// Example metadata type
class ProcessingMetadata {
    private int totalItems;
    private int processedItems;
    private String currentPhase;
    // ... other fields
}

public void displayProgress(Operation operation) throws InvalidProtocolBufferException {
    if (operation.hasMetadata()) {
        Any metadata = operation.getMetadata();
        
        if (metadata.is(ProcessingMetadata.class)) {
            ProcessingMetadata progress = metadata.unpack(ProcessingMetadata.class);
            
            double percentComplete = (double) progress.getProcessedItems() / progress.getTotalItems() * 100;
            
            System.out.printf("Operation %s: %.1f%% complete (%s)\n",
                operation.getName(),
                percentComplete,
                progress.getCurrentPhase());
        }
    }
}

Delete Operations

import com.google.longrunning.DeleteOperationRequest;

public void cleanupCompletedOperations() {
    // First, list completed operations
    ListOperationsRequest listRequest = ListOperationsRequest.newBuilder()
        .setName("operations")
        .setFilter("done = true")
        .build();
    
    ListOperationsResponse response = operationsClient.listOperations(listRequest);
    
    // Delete operations older than a certain threshold
    long cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
    
    for (Operation operation : response.getOperationsList()) {
        // This example assumes operation names contain timestamps
        // In practice, you'd use metadata or other fields to determine age
        if (shouldDeleteOperation(operation, cutoffTime)) {
            DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
                .setName(operation.getName())
                .build();
            
            try {
                operationsClient.deleteOperation(deleteRequest);
                System.out.println("Deleted operation: " + operation.getName());
            } catch (Exception e) {
                System.err.println("Failed to delete operation " + operation.getName() + ": " + e.getMessage());
            }
        }
    }
}

private boolean shouldDeleteOperation(Operation operation, long cutoffTime) {
    // Implementation depends on how you track operation timestamps
    // This is just an example
    return true; // Replace with actual logic
}

Best Practices

Operation Naming

Operations should have unique, descriptive names:

// Good: includes service, resource type, and unique ID
String operationName = "operations/compute/instances/create-instance-abc123";

// Good: hierarchical naming for organization
String operationName = "projects/my-project/operations/backup-database-456789";

Error Handling

Always check for errors before processing results:

public void handleOperation(Operation operation) {
    if (!operation.getDone()) {
        // Operation still in progress
        return;
    }
    
    if (operation.hasError()) {
        // Handle error case
        Status error = operation.getError();
        handleOperationError(error);
        return;
    }
    
    if (operation.hasResponse()) {
        // Process successful result
        processOperationResult(operation.getResponse());
    }
}

Timeout Strategies

Implement appropriate timeout strategies for different operation types:

public class OperationConfig {
    // Short operations: file uploads, simple transformations
    public static final Duration SHORT_OPERATION_TIMEOUT = Duration.newBuilder()
        .setSeconds(300)  // 5 minutes
        .build();
    
    // Medium operations: data processing, image generation
    public static final Duration MEDIUM_OPERATION_TIMEOUT = Duration.newBuilder()
        .setSeconds(1800)  // 30 minutes
        .build();
    
    // Long operations: large data imports, model training
    public static final Duration LONG_OPERATION_TIMEOUT = Duration.newBuilder()
        .setSeconds(7200)  // 2 hours
        .build();
}

Resource Cleanup

Always clean up completed operations to avoid resource leaks:

public void performOperationWithCleanup(String operationName) {
    try {
        Operation result = waitForOperation(operationName);
        processOperationResult(result);
    } finally {
        // Clean up the operation record
        try {
            DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
                .setName(operationName)
                .build();
            operationsClient.deleteOperation(deleteRequest);
        } catch (Exception e) {
            System.err.println("Warning: Failed to clean up operation " + operationName);
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-google-api-grpc--proto-google-common-protos

docs

api-infrastructure.md

apps-cards.md

cloud-platform.md

common-types.md

index.md

longrunning-operations.md

rpc-status.md

tile.json