A report the store cannot take yet is handed back to the broker, not acknowledged and lost (novox/hq issue 082)
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"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 with "the
|
||||
// database system is starting up" or a refused connection; 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 a connection that failed, and the server classes that mean "not now": 08
|
||||
// (connection exception) and 57 (operator intervention — starting up, shutting down, admin
|
||||
// shutdown, cannot connect now). Everything else is an answer, and asking again gets the same one.
|
||||
func Unreachable(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var connect *pgconn.ConnectError
|
||||
if errors.As(err, &connect) {
|
||||
return true
|
||||
}
|
||||
var pg *pgconn.PgError
|
||||
if errors.As(err, &pg) {
|
||||
return strings.HasPrefix(pg.Code, "08") || strings.HasPrefix(pg.Code, "57")
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// The store restarting is "not now"; a constraint the store enforced is an answer (issue 082).
|
||||
func TestAStoreThatIsStartingIsUnreachableAndARefusalIsNot(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
err error
|
||||
want bool
|
||||
}{
|
||||
{&pgconn.PgError{Code: "57P03", Message: "the database system is starting up"}, true},
|
||||
{fmt.Errorf("recording: %w", &pgconn.PgError{Code: "57P01"}), true},
|
||||
{&pgconn.PgError{Code: "08006"}, true},
|
||||
{&pgconn.PgError{Code: "23503", Message: "violates foreign key constraint"}, false},
|
||||
{errors.New("a report named no node"), false},
|
||||
{nil, false},
|
||||
} {
|
||||
if got := Unreachable(c.err); got != c.want {
|
||||
t.Errorf("Unreachable(%v) = %v, want %v", c.err, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user