package inventory import ( "errors" "io" "net" "github.com/jackc/pgx/v5/pgconn" ) // Unreachable says whether an error means the store could not be asked right now, rather than // that it answered no. // // The difference decides whether something worth keeping is kept or lost. The store is recreated // when the foundation is adopted (ADR 0078), and for those seconds every write fails; a caller // that treats that like a refusal throws away what it was writing — a node's report of the apply // that caused the restart was lost exactly so, and the mesh never heard from the node again // (novox/hq issue 082). // // Unreachable is the connection failing and the server saying "not now". The connection failing // is a network error, a connection that ended mid-conversation (a store killed rather than // stopped gives a bare unexpected EOF), a timeout, or anything pgx marks safe to retry. The server // saying "not now" is a connection exception (class 08) or it shutting down or starting up (57P01, // 57P02, 57P03). Everything else is an answer, and asking again gets the same one: a wrong // password, a database that does not exist, a statement cancelled, a constraint. Those are // checked first, even inside a failed connection, because a connection that failed on a wrong // password would otherwise read as a network fault and be asked again for ever. func Unreachable(err error) bool { if err == nil { return false } var pg *pgconn.PgError if errors.As(err, &pg) { switch pg.Code { case "57P01", "57P02", "57P03": return true } return len(pg.Code) == 5 && pg.Code[:2] == "08" } if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) { return true } if pgconn.Timeout(err) || pgconn.SafeToRetry(err) { return true } var network net.Error if errors.As(err, &network) { return true } var connect *pgconn.ConnectError return errors.As(err, &connect) }