Claude Fable 5.1 & GPT-6 Astra packages are live

Rpc

Free

gRPC and typed RPC — protobuf evolution rules, deadlines and cancellation, streaming, error models, and when RPC beats REST.

208 lines8.7 KB Qwen API
targetModels
Qwen3.8-MaxQwen3.8-Flash-NextQwen3.8-27BQwen3.8 FamilyFuture Qwen Models
name
rpc
category
API
description
gRPC and typed RPC — protobuf evolution rules, deadlines and cancellation, streaming, error models, and when RPC beats REST.
license
MIT
author
Agent.md maintainers
last-verified
reviewed-by
unreviewed
<!-- Generated from models/_canonical by scripts/build-model-variants.js. Edit the canonical source, not this file. Behavioural profile for Qwen: scripts/model-profiles.json -->

#Task boundary

  1. Implement only what the task names; no extra abstractions or files.
  2. English-only comments and identifiers.
  3. Stop when the checklist passes.

#Purpose

Rules for RPC-style APIs — gRPC, Connect, tRPC, Twirp. RPC models procedures, not resources: the unit is an operation with a typed request and response.

Use RPC for internal service-to-service traffic where you control both ends and want a schema-enforced contract, low overhead and streaming. Use REST for public APIs consumed by clients you do not control. → API/rest


#Schema evolution is the whole contract

Protobuf's wire format is positional: field numbers are the contract, names are not.

proto
message Order {
  string id            = 1;
  int64  total_cents   = 2;
  string currency      = 3;
  reserved 4;                        // was `amount`, removed 2026-08 — never reuse
  reserved "amount";
  OrderStatus status   = 5;
}
ChangeSafe
Adding a new field with a new numberYes
Renaming a field (same number)Yes on the wire; breaks JSON mapping and generated code
Removing a fieldOnly with reserved on its number and name
Reusing a field numberNever — old clients decode garbage into the new field
Changing a field's typeNo, except within the documented compatible sets
Changing optional/repeatedNo
Adding an enum valueWire-safe; clients must have a default branch
Renaming a service or methodBreaking — it is the wire path

Reusing a retired field number is the classic protobuf data-corruption bug: a stale client sends its old meaning, and the new server accepts it as the new field. reserved makes it a compile error instead.

Gate it in CI:

bash
buf breaking --against '.git#branch=main'
buf lint

Every enum reserves 0 as *_UNSPECIFIED. Proto3 cannot distinguish an unset scalar from its zero value, so 0 must never be a meaningful state.


#Deadlines and cancellation

Every RPC call sets a deadline. This is not optional and it is the single most common gRPC production failure: a call with no deadline waits forever, holds a connection and a goroutine, and cascades into a fleet-wide hang.

go
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
resp, err := client.GetOrder(ctx, &pb.GetOrderRequest{Id: id})
  1. Propagate the deadline through every downstream call. gRPC does this via context automatically — do not create a fresh context.Background() mid-chain, which severs cancellation.
  2. Give each hop less budget than its caller, leaving room for the response.
  3. Honour cancellation server-side: check ctx.Err() before expensive work and between loop iterations. A client that gave up should not still be costing you.
  4. Set a server-side maximum as a backstop against clients that omit deadlines.

Retries must only apply to idempotent methods, with backoff and jitter, and a budget (retryThrottling) so a struggling service is not retried into collapse. → System Design/resilience


#Errors

Use the standard status codes; they carry retry semantics that generated clients and service meshes act on.

CodeMeaningClient retries
INVALID_ARGUMENTRequest is malformed regardless of stateNo
FAILED_PRECONDITIONValid, but system state forbids itNo, until state changes
NOT_FOUNDAbsent, or hidden from this callerNo
PERMISSION_DENIEDAuthenticated, not authorizedNo
UNAUTHENTICATEDMissing or invalid credentialsAfter re-auth
RESOURCE_EXHAUSTEDQuota or rate limitWith backoff
ABORTEDConcurrency conflictYes, at a higher level
UNAVAILABLETransient — the only safely auto-retried codeYes
DEADLINE_EXCEEDEDRan out of timeOnly if idempotent
INTERNALServer bugNo

Attach machine-readable detail with google.rpc.ErrorInfo and BadRequest rather than encoding structure into the message string.

Never return INTERNAL for a client mistake — it is unretryable, alerts your on-call, and tells the caller nothing actionable.


#Streaming

PatternUse for
UnaryDefault. Use it unless you have a reason not to
Server streamingLarge result sets, live feeds
Client streamingBulk upload, telemetry
BidirectionalInteractive sessions

Streams are stateful and complicate load balancing, retries and deployments — a long-lived stream pins a client to one pod across a rollout.

  1. Bound stream lifetime and message size (grpc.max_receive_message_length).
  2. Apply flow control; an unbounded producer will exhaust the consumer's memory.
  3. Design reconnection with resume tokens, because streams will break.
  4. Server streaming is not a substitute for pagination when the client wants a page. → API/pagination

#Operational rules

  1. Connection-level load balancing fails with HTTP/2. gRPC multiplexes over one long-lived connection, so an L4 balancer pins all traffic to one backend. Use an L7 proxy (Envoy, Linkerd) or client-side load balancing with resolver updates.
  2. Implement the standard health checking protocol (grpc.health.v1.Health) and wire it to readiness probes.
  3. Enable reflection in development only; it exposes the full service surface.
  4. Use TLS everywhere, mTLS between internal services.
  5. Instrument with interceptors: request id propagation, structured logging, metrics by method and status code, tracing. → Backend/monitoring
  6. For browser clients, gRPC needs a proxy (grpc-web) — Connect speaks both protocols and is usually the better choice there.

#Anti-patterns

Anti-patternWhy it failsFix
Reusing a retired field numberOld clients corrupt new datareserved number and name
Removing a field without reservedNumber becomes reusable by accidentAlways reserve
Meaningful enum 0Indistinguishable from unset in proto3*_UNSPECIFIED = 0
No breaking-change gateIncompatible schemas shipbuf breaking in CI
Calls without deadlinesHangs cascade fleet-wideDeadline on every call
Fresh context.Background() mid-chainCancellation and deadline lostPropagate the context
Ignoring ctx.Err() server-sideWork continues for a gone clientCheck between steps
Retrying non-idempotent methodsDuplicate side effectsRetry only idempotent calls
Retries without a budgetRetry storm collapses the serviceBackoff, jitter, throttling
INTERNAL for bad inputUnretryable, pages on-call, unactionableINVALID_ARGUMENT
Error detail encoded in the messageClients parse proseErrorInfo details
L4 load balancer in front of gRPCAll traffic pins to one backendL7 proxy or client-side LB
Streaming where unary would doStateful, breaks rollouts and balancingUnary by default
Unbounded stream or message sizeMemory exhaustionExplicit limits and flow control
Reflection enabled in productionFull surface disclosedDevelopment only

#Checklist

  • Field numbers are never reused; removals use reserved for number and name
  • Every enum reserves 0 as UNSPECIFIED
  • buf lint and buf breaking run in CI against the merge base
  • Every client call sets an explicit deadline
  • Deadlines propagate through the call chain with a shrinking budget
  • Servers check for cancellation before and during expensive work
  • A server-side maximum deadline exists as a backstop
  • Retries apply only to idempotent methods, with backoff, jitter and a budget
  • Status codes are used correctly, with structured error details
  • Unary is the default; streaming is justified per method
  • Message size and stream lifetime are bounded; flow control is applied
  • Streams have a documented reconnection and resume strategy
  • Load balancing is L7 or client-side, not L4
  • The standard health-checking service is implemented and wired to probes
  • Reflection is disabled in production; TLS/mTLS is enforced
  • Interceptors provide request ids, structured logs, metrics and traces