Skip to content

Commit f089aa5

Browse files
authored
SA.GetOrder: conduct queries inside a transaction (#6541)
The SA's GetOrder method issues four separate database queries: - get the Order object itself - get the list of Names associated with it - get the list of Authorization IDs associated with it - get the contents of those Authorizations themselves These four queries can hit different database replicas with different amounts of replication lag, and therefore different views of the universe. This can result in inconsistent results, such as the Order existing, but not having any Authorization IDs associated with it. Conduct these four queries all within a single transaction, to ensure they all hit a single read-replica with a consistent view of the world. Fixes #6540
1 parent fd74d20 commit f089aa5

File tree

3 files changed

+79
-63
lines changed

3 files changed

+79
-63
lines changed

sa/model.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,3 +960,32 @@ func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, erro
960960
}
961961
return allAuthzValidity, nil
962962
}
963+
964+
// authzForOrder retrieves the authorization IDs for an order. It assumes that
965+
// the provided database selector already has a context associated with it.
966+
func authzForOrder(s db.Selector, orderID int64) ([]int64, error) {
967+
var v2IDs []int64
968+
_, err := s.Select(
969+
&v2IDs,
970+
"SELECT authzID FROM orderToAuthz2 WHERE orderID = ?",
971+
orderID,
972+
)
973+
return v2IDs, err
974+
}
975+
976+
// namesForOrder finds all of the requested names associated with an order. The
977+
// names are returned in their reversed form (see `sa.ReverseName`). It assumes
978+
// that the provided database selector already has a context associated with it.
979+
func namesForOrder(s db.Selector, orderID int64) ([]string, error) {
980+
var reversedNames []string
981+
_, err := s.Select(
982+
&reversedNames,
983+
`SELECT reversedName
984+
FROM requestedNames
985+
WHERE orderID = ?`,
986+
orderID)
987+
if err != nil {
988+
return nil, err
989+
}
990+
return reversedNames, nil
991+
}

sa/sa_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ func TestNewOrderAndAuthzs(t *testing.T) {
986986
test.AssertEquals(t, len(authzIDs), 4)
987987
test.AssertDeepEquals(t, authzIDs, []int64{1, 2, 3, 4})
988988

989-
names, err := sa.namesForOrder(context.Background(), order.Id)
989+
names, err := namesForOrder(sa.dbReadOnlyMap, order.Id)
990990
test.AssertNotError(t, err, "namesForOrder errored")
991991
test.AssertEquals(t, len(names), 4)
992992
test.AssertDeepEquals(t, names, []string{"com.a", "com.b", "com.c", "com.d"})

sa/saro.go

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -573,83 +573,70 @@ func (ssa *SQLStorageAuthority) PreviousCertificateExists(ctx context.Context, r
573573
return ssa.SQLStorageAuthorityRO.PreviousCertificateExists(ctx, req)
574574
}
575575

576-
// authzForOrder retrieves the authorization IDs for an order.
577-
func (ssa *SQLStorageAuthorityRO) authzForOrder(ctx context.Context, orderID int64) ([]int64, error) {
578-
var v2IDs []int64
579-
_, err := ssa.dbReadOnlyMap.WithContext(ctx).Select(
580-
&v2IDs,
581-
"SELECT authzID FROM orderToAuthz2 WHERE orderID = ?",
582-
orderID,
583-
)
584-
return v2IDs, err
585-
}
586-
587-
// namesForOrder finds all of the requested names associated with an order. The
588-
// names are returned in their reversed form (see `sa.ReverseName`).
589-
func (ssa *SQLStorageAuthorityRO) namesForOrder(ctx context.Context, orderID int64) ([]string, error) {
590-
var reversedNames []string
591-
_, err := ssa.dbReadOnlyMap.WithContext(ctx).Select(
592-
&reversedNames,
593-
`SELECT reversedName
594-
FROM requestedNames
595-
WHERE orderID = ?`,
596-
orderID)
597-
if err != nil {
598-
return nil, err
599-
}
600-
return reversedNames, nil
601-
}
602-
603576
// GetOrder is used to retrieve an already existing order object
604577
func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderRequest) (*corepb.Order, error) {
605578
if req == nil || req.Id == 0 {
606579
return nil, errIncompleteRequest
607580
}
608581

609-
omObj, err := ssa.dbReadOnlyMap.WithContext(ctx).Get(orderModel{}, req.Id)
610-
if err != nil {
611-
if db.IsNoRows(err) {
582+
output, err := db.WithTransaction(ctx, ssa.dbReadOnlyMap, func(txWithCtx db.Executor) (interface{}, error) {
583+
omObj, err := txWithCtx.Get(orderModel{}, req.Id)
584+
if err != nil {
585+
if db.IsNoRows(err) {
586+
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
587+
}
588+
return nil, err
589+
}
590+
if omObj == nil {
612591
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
613592
}
614-
return nil, err
615-
}
616-
if omObj == nil {
617-
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
618-
}
619-
order, err := modelToOrder(omObj.(*orderModel))
620-
if err != nil {
621-
return nil, err
622-
}
623-
orderExp := time.Unix(0, order.Expires)
624-
if orderExp.Before(ssa.clk.Now()) {
625-
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
626-
}
627593

628-
v2AuthzIDs, err := ssa.authzForOrder(ctx, order.Id)
629-
if err != nil {
630-
return nil, err
631-
}
632-
order.V2Authorizations = v2AuthzIDs
594+
order, err := modelToOrder(omObj.(*orderModel))
595+
if err != nil {
596+
return nil, err
597+
}
598+
599+
orderExp := time.Unix(0, order.Expires)
600+
if orderExp.Before(ssa.clk.Now()) {
601+
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
602+
}
603+
604+
v2AuthzIDs, err := authzForOrder(txWithCtx, order.Id)
605+
if err != nil {
606+
return nil, err
607+
}
608+
order.V2Authorizations = v2AuthzIDs
633609

634-
names, err := ssa.namesForOrder(ctx, order.Id)
610+
names, err := namesForOrder(txWithCtx, order.Id)
611+
if err != nil {
612+
return nil, err
613+
}
614+
// The requested names are stored reversed to improve indexing performance. We
615+
// need to reverse the reversed names here before giving them back to the
616+
// caller.
617+
reversedNames := make([]string, len(names))
618+
for i, n := range names {
619+
reversedNames[i] = ReverseName(n)
620+
}
621+
order.Names = reversedNames
622+
623+
// Calculate the status for the order
624+
status, err := statusForOrder(txWithCtx, order, ssa.clk.Now())
625+
if err != nil {
626+
return nil, err
627+
}
628+
order.Status = status
629+
630+
return order, nil
631+
})
635632
if err != nil {
636633
return nil, err
637634
}
638-
// The requested names are stored reversed to improve indexing performance. We
639-
// need to reverse the reversed names here before giving them back to the
640-
// caller.
641-
reversedNames := make([]string, len(names))
642-
for i, n := range names {
643-
reversedNames[i] = ReverseName(n)
644-
}
645-
order.Names = reversedNames
646635

647-
// Calculate the status for the order
648-
status, err := statusForOrder(ssa.dbReadOnlyMap.WithContext(ctx), order, ssa.clk.Now())
649-
if err != nil {
650-
return nil, err
636+
order, ok := output.(*corepb.Order)
637+
if !ok {
638+
return nil, fmt.Errorf("casting error in GetOrder")
651639
}
652-
order.Status = status
653640

654641
return order, nil
655642
}

0 commit comments

Comments
 (0)