Documentation
¶
Overview ¶
Package errs defines SQLFlow's error codes and the error type that carries them.
Codes are public API. A provider shows them to its own customers and writes automation against them, so a code never changes meaning and never disappears. See registry.go for the rule and the test that enforces it.
A code has three dot-separated parts, class.domain.reason:
user.sql.bind_failed system.sink.unreachable
The class says whose fault it is. A consumer that has never seen a code still matches its class prefix and knows whether to show the customer a message or page an operator. That guarantee is what lets new codes ship without breaking anyone, so match on the prefix rather than on an exhaustive list.
Index ¶
Constants ¶
const ( // ExitOK is a clean shutdown, including a drain on SIGTERM. ExitOK = 0 // ExitInternal is a failure we could not classify. Retryable, because a // bug we have not characterized may well be transient. ExitInternal = 1 // ExitUserError marks a failure only the user can fix: config, SQL, // credentials. Terminal. A supervisor that retries it loops forever. ExitUserError = 10 // ExitSourceUnreachable and ExitSinkUnreachable mark a dependency that // was not there. Retryable: the dependency may come back. ExitSourceUnreachable = 11 ExitSinkUnreachable = 12 // ExitResourceLimit marks a declared limit the pipeline exceeded. // Retryable, though it repeats until someone raises the limit or the // workload shrinks. ExitResourceLimit = 13 // ExitStateCorrupt marks a state file that cannot be read. Terminal: a // restart reads the same bytes. An operator has to look at it. ExitStateCorrupt = 14 )
Exit codes tell a supervisor what to do next. They are a coarse projection of the error codes, and they live here so the two classifications cannot drift apart: #161 consumes this table rather than restating it.
The distinction that matters is Retryable. Restarting a pipeline whose config is wrong burns a restart and fails the same way, forever. That is the crash loop a supervisor has to be told to avoid.
Variables ¶
This section is empty.
Functions ¶
func ExitCode ¶
ExitCode maps an error to the code the process should exit with.
An unknown code still resolves, because it falls back to the class prefix. A future system.limit.disk_exhausted exits ExitInternal rather than crashing the mapping, and gains its own entry when someone adds it.
Types ¶
type Class ¶
type Class string
Class is the first part of a code. Two exist, and no third will be added: every failure is either something the user can fix or something we can.
type Code ¶
type Code string
Code identifies one failure. Use the constants in registry.go rather than building a Code from a string.
const ( // Config: the pipeline file itself. CodeConfigNotFound Code = "user.config.not_found" CodeConfigParseFailed Code = "user.config.parse_failed" CodeConfigInvalid Code = "user.config.invalid" // Data: the messages themselves, as opposed to the pipeline definition. // A malformed record is the producer's problem, never ours. CodeDataMalformed Code = "user.data.malformed" CodeDataInvalid Code = "user.data.invalid" // SQL: the handler's query and the schema it binds against. CodeSQLBindFailed Code = "user.sql.bind_failed" CodeSQLTypeUnsupported Code = "user.sql.type_unsupported" CodeSQLInvalid Code = "user.sql.invalid" // Source and sink configuration the user got wrong. CodeSourceSecurityInvalid Code = "user.source.security_invalid" CodeSourceInvalid Code = "user.source.invalid" CodeSinkInvalid Code = "user.sink.invalid" // Source and sink failures that are not the user's doing. CodeSourceUnreachable Code = "system.source.unreachable" CodeSourceInternal Code = "system.source.internal" CodeSinkUnreachable Code = "system.sink.unreachable" CodeSinkWriteFailed Code = "system.sink.write_failed" CodeSinkInternal Code = "system.sink.internal" // Durable state and the offsets committed with it. CodeStateCorrupt Code = "system.state.corrupt" CodeStateCommitFailed Code = "system.state.commit_failed" CodeStateInternal Code = "system.state.internal" // Batch orchestration. CodeBatchInternal Code = "system.batch.internal" // The last resort. CodeOf returns it for an error carrying no code, so an // unclassified failure still reports as ours rather than the user's. CodeInternalUnexpected Code = "system.internal.unexpected" )
The registry is append-only. A published code keeps its meaning forever, because providers show these to their customers and write automation against them.
Adding a code is safe: consumers match on the class prefix, so an unknown code still routes correctly. Removing one or changing what it means is not. TestRegistryIsAppendOnly compares this file against testdata/codes.golden and fails on a removal.
Each domain carries a catch-all, so a new failure always has a home before anyone gives it a specific code. User domains catch all with `invalid`, system domains with `internal`. Reach for a specific code first; use the catch-all rather than inventing a code in a hurry.
func CodeOf ¶
CodeOf returns the code of the outermost coded error in the chain.
An error carrying no code reports as CodeInternalUnexpected rather than as a user error. Guessing the other way would blame a customer for our bug.
func (Code) Domain ¶
Domain returns the subsystem the failure came from: config, sql, source, sink, state, batch, limit or internal.
type Definition ¶
Definition documents one code. Action is what the operator should do, and it is the reason this registry exists rather than a bare list of constants: a code with no recommended action is not usable by the people who see it.
func All ¶
func All() []Definition
All returns every registered definition, ordered by code. Documentation and the append-only test both read it.
func Lookup ¶
func Lookup(c Code) (Definition, bool)
Lookup returns the definition for a code. A caller that gets ok == false is holding a code this build does not know, which is normal when a newer component produced it: fall back to the class prefix.
type Error ¶
Error carries a code alongside the message.
Interior call sites keep using fmt.Errorf with %w. Only the places where an error becomes observable need a code: the CLI boundary, the log, and the `validate` response. The code survives any amount of wrapping in between, which is why converting the codebase does not mean touching every raise site.
func Wrap ¶
Wrap attaches a code to an existing error. Wrapping an error that already carries a code keeps the outer one: the boundary nearest the user decides how to describe the failure.
type Position ¶
type Position struct {
// Source names what Line and Column index into: "sql" or "config".
Source string
Line int
Column int
}
Position points at the place in the user's own text that caused a user error. `validate` and `preview` return it so a UI can highlight the line rather than printing a paragraph.
func PositionOf ¶
PositionOf returns the position of the outermost coded error, if it has one.