Skip to content

Commit

Permalink
Merge pull request #2025 from josephschorr/pg-readonly-error
Browse files Browse the repository at this point in the history
Add nicer error if the Postgres primary node has gone readonly
  • Loading branch information
josephschorr authored Aug 15, 2024
2 parents 881a32e + 3c57fdd commit 4b587c0
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
25 changes: 25 additions & 0 deletions internal/datastore/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ func NewSerializationError(err error) error {
return SerializationError{err}
}

// ReadOnlyTransactionError is returned when an otherwise read-write
// transaction fails on writes with an error indicating that the datastore
// is currently in a read-only mode.
type ReadOnlyTransactionError struct {
error
}

func (err ReadOnlyTransactionError) GRPCStatus() *status.Status {
return spiceerrors.WithCodeAndDetails(
err,
codes.Aborted,
spiceerrors.ForReason(
v1.ErrorReason_ERROR_REASON_SERVICE_READ_ONLY,
map[string]string{},
),
)
}

// NewReadOnlyTransactionError creates a new ReadOnlyTransactionError.
func NewReadOnlyTransactionError(err error) error {
return ReadOnlyTransactionError{
fmt.Errorf("could not perform write operation, as the datastore is currently in read-only mode: %w. This may indicate that the datastore has been put into maintenance mode", err),
}
}

// CreateRelationshipExistsError is an error returned when attempting to CREATE an already-existing
// relationship.
type CreateRelationshipExistsError struct {
Expand Down
8 changes: 8 additions & 0 deletions internal/datastore/postgres/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
pgUniqueConstraintViolation = "23505"
pgSerializationFailure = "40001"
pgTransactionAborted = "25P02"
pgReadOnlyTransaction = "25006"
)

var (
Expand All @@ -30,6 +31,13 @@ func IsConstraintFailureError(err error) bool {
return errors.As(err, &pgerr) && pgerr.Code == pgUniqueConstraintViolation
}

// IsReadOnlyTransactionError returns true if the error is a Postgres error indicating a read-only
// transaction.
func IsReadOnlyTransactionError(err error) bool {
var pgerr *pgconn.PgError
return errors.As(err, &pgerr) && pgerr.Code == pgReadOnlyTransaction
}

// ConvertToWriteConstraintError converts the given Postgres error into a CreateRelationshipExistsError
// if applicable. If not applicable, returns nils.
func ConvertToWriteConstraintError(livingTupleConstraints []string, err error) error {
Expand Down
4 changes: 4 additions & 0 deletions internal/datastore/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,10 @@ func wrapError(err error) error {
return common.NewSerializationError(err)
}

if pgxcommon.IsReadOnlyTransactionError(err) {
return common.NewReadOnlyTransactionError(err)
}

// hack: pgx asyncClose usually happens after cancellation,
// but the reason for it being closed is not propagated
// and all we get is attempting to perform an operation
Expand Down

0 comments on commit 4b587c0

Please sign in to comment.