TypeDB.GRPC.Error (TypeDB.GRPC v0.1.0)

Copy Markdown View Source

Turning gRPC failures into the %TypeDB.Error{} the sibling driver returns.

This module exists so that the two transports are interchangeable at the call site. An application that routes failures by kind, by code, or through TypeDB.Error.retryable?/1 keeps every branch it wrote when it switches, and that is the whole reason typedb_grpc depends on typedb rather than defining error types of its own.

Where a TypeDB error code lives on this transport

Nowhere obvious, which is why this is a module and not a function. A gRPC failure arrives as a GRPC.RPCError whose message is a category ("Unauthenticated", "Request generated error") rather than anything about the request. The useful parts are in details, as two google.rpc messages that TypeDB attaches by hand:

  • ErrorInfo.reason — the TypeDB code, "AUT1", "DBD1", "STC2". The same codes the HTTP API puts in its JSON body.
  • DebugInfo.stack_entries — the human messages, most specific first.

Measured against 3.12.1: deleting a database that does not exist arrives as gRPC status 3 with reason: "DBD1" and stack entries ["[DBD1] Cannot delete database since it does not exist.", "[SRV13] Unable to delete database."].

Why :status carries an HTTP status on a transport that has none

%TypeDB.Error{} documents :status as the HTTP status, and there is no HTTP here. It is filled anyway, with the standard gRPC-to-HTTP equivalence that grpc-gateway and every other bridge uses, because TypeDB.Error.retryable?/1 judges :server errors by status — and a nil there would quietly make the same failure retryable over one transport and terminal over the other. That divergence is exactly what sharing the struct is meant to prevent.

The gRPC status is not lost: it is in :reason, as {:grpc_status, integer}.

Summary

Functions

Converts anything else a gRPC call can fail with.

Converts a GRPC.RPCError into a %TypeDB.Error{}.

The HTTP status this driver reports for a gRPC status code.

Functions

from_reason(reason, context)

@spec from_reason(term(), String.t()) :: TypeDB.Error.t()

Converts anything else a gRPC call can fail with.

The adapter reports a connection that could not be established, or a stream that died, as plain terms rather than as GRPC.RPCError. They are transport failures in the sense %TypeDB.Error{} means: the request produced no answer, and trying again could produce one.

from_rpc_error(error, context)

@spec from_rpc_error(GRPC.RPCError.t(), String.t()) :: TypeDB.Error.t()

Converts a GRPC.RPCError into a %TypeDB.Error{}.

context is prepended to the message when the server gave nothing better — it names the operation, so that a bare "Request generated error" still tells the reader what was being attempted.

http_status(grpc_status)

@spec http_status(integer()) :: pos_integer()

The HTTP status this driver reports for a gRPC status code.

iex> TypeDB.GRPC.Error.http_status(16)
401

iex> TypeDB.GRPC.Error.http_status(14)
503

Unknown codes become 500: a gRPC status this driver has not seen is the server behaving in a way nobody predicted, which is what 500 means.