Repository: letsencrypt/boulder

Issue:
Performance regression when upgrading go-sql-driver/mysql to v1.6.0
We see:
 - Higher CPU usage on our database
 - Higher latency for certain SA RPCs, particularly for those that make multiple DB queries.

Discussion:
@aarongable: We suspect this commit in go-sql-driver/mysql v1.6.0 as the "root" cause: https://github.com/go-sql-driver/mysql/pull/1095@jsha: Or possibly this one: https://github.com/go-sql-driver/mysql/pull/1174@aarongable: We have now upgraded to go-sql-driver/mysql v1.7.0 and have not observed a similar performance regression. We are not sure whether this is due to changes in v1.7.0 or due to our introduction of proxysql since the last time this upgrade was attempted. Regardless, this bug can be closed.@aarongable: We believe we have solved the underlying issues, and this is now a prerequisite for https://github.com/letsencrypt/boulder/issues/7872, so we're going to pick this up again.

Solution patch:
diff --git a/.github/workflows/boulder-ci.yml b/.github/workflows/boulder-ci.yml
index 13b2e5875..1b099c0e9 100644
--- a/.github/workflows/boulder-ci.yml
+++ b/.github/workflows/boulder-ci.yml
@@ -36,8 +36,8 @@ jobs:
       matrix:
         # Add additional docker image tags here and all tests will be run with the additional image.
         BOULDER_TOOLS_TAG:
-          - go1.19.2_2023-01-10
-          - go1.19.5_2023-01-10
+          - go1.19.5_2023-02-02
+          - go1.20_2023-02-02
         # Tests command definitions. Use the entire "docker compose" command you want to run.
         tests:
           # Run ./test.sh --help for a description of each of the flags.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 819db8288..9fb2f84d2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,7 +1,8 @@
 # Build the Boulder Debian package on every PR, push to main, and tag push. On
 # tag pushes, additionally create a GitHub release and with the resulting Debian
 # package.
-# Keep in sync with try-release.yml
+# Keep in sync with try-release.yml, with the exception that try-release.yml
+# can have multiple entries in its matrix but this should only have one.
 name: Build release
 on:
   push:
@@ -10,6 +11,12 @@ on:
 
 jobs:
   push-release:
+    strategy:
+      fail-fast: false
+      matrix:
+        GO_VERSION:
+          - "1.19.5"
+          - "1.20"
     runs-on: ubuntu-20.04
     permissions:
       contents: write
@@ -21,11 +28,18 @@ jobs:
       - name: Build .deb
         id: build
         env:
-          GO_VERSION: 1.19.5
+          GO_VERSION: ${{ matrix.GO_VERSION }}
         run: ./tools/make-deb.sh
 
       - name: Create release
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         # https://cli.github.com/manual/gh_release_create
-        run: gh release create "${GITHUB_REF_NAME}" boulder*.deb
+        run: gh release create "${GITHUB_REF_NAME}"
+        continue-on-error: true
+
+      - name: Upload release files
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        # https://cli.github.com/manual/gh_release_upload
+        run: gh release upload "${GITHUB_REF_NAME}" boulder*.deb
diff --git a/.github/workflows/try-release.yml b/.github/workflows/try-release.yml
index 350ead1a5..7689b5d3d 100644
--- a/.github/workflows/try-release.yml
+++ b/.github/workflows/try-release.yml
@@ -11,6 +11,12 @@ on:
 
 jobs:
   try-release:
+    strategy:
+      fail-fast: false
+      matrix:
+        GO_VERSION:
+          - "1.19.5"
+          - "1.20"
     runs-on: ubuntu-20.04
     steps:
       - uses: actions/checkout@v3
@@ -20,5 +26,5 @@ jobs:
       - name: Build .deb
         id: build
         env:
-          GO_VERSION: 1.19.5
+          GO_VERSION: ${{ matrix.GO_VERSION }}
         run: ./tools/make-deb.sh
diff --git a/bdns/dns.go b/bdns/dns.go
index dac45d359..d2ca2291c 100644
--- a/bdns/dns.go
+++ b/bdns/dns.go
@@ -584,14 +584,14 @@ func logDNSError(
 		}
 
 		logger.Infof(
-			"logDNSError ID mismatch chosenServer=[%s] hostname=[%s] respHostname=[%s] queryType=[%s] err=[%s] msg=[%s] resp=[%s]",
+			"logDNSError ID mismatch chosenServer=[%s] hostname=[%s] respHostname=[%s] queryType=[%s] msg=[%s] resp=[%s] err=[%s]",
 			chosenServer,
 			hostname,
 			respQname,
 			queryType,
-			underlying,
 			encodedMsg,
-			encodedResp)
+			encodedResp,
+			underlying)
 	} else {
 		// Otherwise log a general DNS error
 		logger.Infof("logDNSError chosenServer=[%s] hostname=[%s] queryType=[%s] err=[%s]",
diff --git a/ca/ca.go b/ca/ca.go
index c4f7a5ccf..2944d53f4 100644
--- a/ca/ca.go
+++ b/ca/ca.go
@@ -478,8 +478,8 @@ func (ca *certificateAuthorityImpl) storeCertificate(
 		err = berrors.InternalServerError(err.Error())
 		// Note: This log line is parsed by cmd/orphan-finder. If you make any
 		// changes here, you should make sure they are reflected in orphan-finder.
-		ca.log.AuditErrf("Failed RPC to store at SA, orphaning certificate: serial=[%s] cert=[%s] err=[%v], regID=[%d], orderID=[%d]",
-			core.SerialToString(serialBigInt), hex.EncodeToString(certDER), err, regID, orderID)
+		ca.log.AuditErrf("Failed RPC to store at SA, orphaning certificate: serial=[%s], cert=[%s], issuerID=[%d], regID=[%d], orderID=[%d], err=[%v]",
+			core.SerialToString(serialBigInt), hex.EncodeToString(certDER), issuerID, regID, orderID, err)
 		if ca.orphanQueue != nil {
 			ca.queueOrphan(&orphanedCert{
 				DER:      certDER,
diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go
index cb9302755..387dca8f8 100644
--- a/cmd/boulder-ca/main.go
+++ b/cmd/boulder-ca/main.go
@@ -15,6 +15,7 @@ import (
 	"github.com/letsencrypt/boulder/ctpolicy/loglist"
 	"github.com/letsencrypt/boulder/features"
 	"github.com/letsencrypt/boulder/goodkey"
+	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
 	bgrpc "github.com/letsencrypt/boulder/grpc"
 	"github.com/letsencrypt/boulder/issuance"
 	"github.com/letsencrypt/boulder/linter"
@@ -239,7 +240,7 @@ func main() {
 	cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")
 	sa := sapb.NewStorageAuthorityClient(conn)
 
-	kp, err := goodkey.NewKeyPolicy(&c.CA.GoodKey, sa.KeyBlocked)
+	kp, err := sagoodkey.NewKeyPolicy(&c.CA.GoodKey, sa.KeyBlocked)
 	cmd.FailOnError(err, "Unable to create key policy")
 
 	var orphanQueue *goque.Queue
diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go
index c0f5f44fc..2afe40306 100644
--- a/cmd/boulder-ra/main.go
+++ b/cmd/boulder-ra/main.go
@@ -14,6 +14,7 @@ import (
 	"github.com/letsencrypt/boulder/ctpolicy/loglist"
 	"github.com/letsencrypt/boulder/features"
 	"github.com/letsencrypt/boulder/goodkey"
+	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
 	bgrpc "github.com/letsencrypt/boulder/grpc"
 	"github.com/letsencrypt/boulder/issuance"
 	"github.com/letsencrypt/boulder/policy"
@@ -228,7 +229,7 @@ func main() {
 	}
 	pendingAuthorizationLifetime := time.Duration(c.RA.PendingAuthorizationLifetimeDays) * 24 * time.Hour
 
-	kp, err := goodkey.NewKeyPolicy(&c.RA.GoodKey, sac.KeyBlocked)
+	kp, err := sagoodkey.NewKeyPolicy(&c.RA.GoodKey, sac.KeyBlocked)
 	cmd.FailOnError(err, "Unable to create key policy")
 
 	if c.RA.MaxNames == 0 {
diff --git a/cmd/boulder-wfe2/main.go b/cmd/boulder-wfe2/main.go
index 78f682324..b314f3892 100644
--- a/cmd/boulder-wfe2/main.go
+++ b/cmd/boulder-wfe2/main.go
@@ -17,10 +17,12 @@ import (
 	"github.com/letsencrypt/boulder/cmd"
 	"github.com/letsencrypt/boulder/features"
 	"github.com/letsencrypt/boulder/goodkey"
+	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
 	bgrpc "github.com/letsencrypt/boulder/grpc"
+	"github.com/letsencrypt/boulder/grpc/noncebalancer"
 	"github.com/letsencrypt/boulder/issuance"
 	blog "github.com/letsencrypt/boulder/log"
-	noncepb "github.com/letsencrypt/boulder/nonce/proto"
+	"github.com/letsencrypt/boulder/nonce"
 	rapb "github.com/letsencrypt/boulder/ra/proto"
 	sapb "github.com/letsencrypt/boulder/sa/proto"
 	"github.com/letsencrypt/boulder/wfe2"
@@ -50,16 +52,36 @@ type Config struct {
 
 		RAService *cmd.GRPCClientConfig
 		SAService *cmd.GRPCClientConfig
-		// GetNonceService contains a gRPC config for any nonce-service instances
-		// which we want to retrieve nonces from. In a multi-DC deployment this
-		// should refer to any local nonce-service instances.
+
+		// GetNonceService is a gRPC config which contains a single SRV name
+		// used to lookup nonce-service instances used exclusively for nonce
+		// creation. In a multi-DC deployment this should refer to local
+		// nonce-service instances only.
 		GetNonceService *cmd.GRPCClientConfig
+
 		// RedeemNonceServices contains a map of nonce-service prefixes to
 		// gRPC configs we want to use to redeem nonces. In a multi-DC deployment
 		// this should contain all nonce-services from all DCs as we want to be
 		// able to redeem nonces generated at any DC.
+		//
+		// DEPRECATED: See RedeemNonceService, below.
+		// TODO (#6610) Remove this after all configs have migrated to
+		// `RedeemNonceService`.
 		RedeemNonceServices map[string]cmd.GRPCClientConfig
 
+		// RedeemNonceService is a gRPC config which contains a list of SRV
+		// names used to lookup nonce-service instances used exclusively for
+		// nonce redemption. In a multi-DC deployment this should contain both
+		// local and remote nonce-service instances.
+		RedeemNonceService *cmd.GRPCClientConfig
+
+		// NoncePrefixKey is a secret used for deriving the prefix of each nonce
+		// instance. It should contain 256 bits of random data to be suitable as
+		// an HMAC-SHA256 key (e.g. the output of `openssl rand -hex 32`). In a
+		// multi-DC deployment this value should be the same across all
+		// boulder-wfe and nonce-service instances.
+		NoncePrefixKey cmd.PasswordConfig
+
 		// CertificateChains maps AIA issuer URLs to certificate filenames.
 		// Certificates are read into the chain in the order they are defined in the
 		// slice of filenames.
@@ -280,7 +302,7 @@ func loadChain(certFiles []string) (*issuance.Certificate, []byte, error) {
 	return certs[0], buf.Bytes(), nil
 }
 
-func setupWFE(c Config, scope prometheus.Registerer, clk clock.Clock) (rapb.RegistrationAuthorityClient, sapb.StorageAuthorityReadOnlyClient, noncepb.NonceServiceClient, map[string]noncepb.NonceServiceClient) {
+func setupWFE(c Config, scope prometheus.Registerer, clk clock.Clock) (rapb.RegistrationAuthorityClient, sapb.StorageAuthorityReadOnlyClient, nonce.Getter, map[string]nonce.Redeemer, nonce.Redeemer, string) {
 	tlsConfig, err := c.WFE.TLS.Load()
 	cmd.FailOnError(err, "TLS config")
 
@@ -292,21 +314,56 @@ func setupWFE(c Config, scope prometheus.Registerer, clk clock.Clock) (rapb.Regi
 	cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")
 	sac := sapb.NewStorageAuthorityReadOnlyClient(saConn)
 
-	var rns noncepb.NonceServiceClient
-	npm := map[string]noncepb.NonceServiceClient{}
-	if c.WFE.GetNonceService != nil {
-		rnsConn, err := bgrpc.ClientSetup(c.WFE.GetNonceService, tlsConfig, scope, clk)
-		cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to get nonce service")
-		rns = noncepb.NewNonceServiceClient(rnsConn)
+	// TODO(#6610) Refactor these checks.
+	if c.WFE.RedeemNonceService != nil && c.WFE.RedeemNonceServices != nil {
+		cmd.Fail("Only one of 'redeemNonceService' or 'redeemNonceServices' should be configured.")
+	}
+	if c.WFE.RedeemNonceService == nil && c.WFE.RedeemNonceServices == nil {
+		cmd.Fail("One of 'redeemNonceService' or 'redeemNonceServices' must be configured.")
+	}
+	if c.WFE.RedeemNonceService != nil && c.WFE.NoncePrefixKey.PasswordFile == "" {
+		cmd.Fail("'noncePrefixKey' must be configured if 'redeemNonceService' is configured.")
+	}
+	if c.WFE.GetNonceService == nil {
+		cmd.Fail("'getNonceService' must be configured")
+	}
+
+	var rncKey string
+	if c.WFE.NoncePrefixKey.PasswordFile != "" {
+		rncKey, err = c.WFE.NoncePrefixKey.Pass()
+		cmd.FailOnError(err, "Failed to load noncePrefixKey")
+	}
+
+	getNonceConn, err := bgrpc.ClientSetup(c.WFE.GetNonceService, tlsConfig, scope, clk)
+	cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to get nonce service")
+	gnc := nonce.NewGetter(getNonceConn)
+
+	var rnc nonce.Redeemer
+	var npm map[string]nonce.Redeemer
+	if c.WFE.RedeemNonceService != nil {
+		// Dispatch nonce redemption RPCs dynamically.
+		if c.WFE.RedeemNonceService.SRVResolver != noncebalancer.SRVResolverScheme {
+			cmd.Fail(fmt.Sprintf(
+				"'redeemNonceService.SRVResolver' must be set to %q", noncebalancer.SRVResolverScheme),
+			)
+		}
+		redeemNonceConn, err := bgrpc.ClientSetup(c.WFE.RedeemNonceService, tlsConfig, scope, clk)
+		cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to redeem nonce service")
+		rnc = nonce.NewRedeemer(redeemNonceConn)
+	} else {
+		// Dispatch nonce redpemption RPCs using a static mapping.
+		//
+		// TODO(#6610) Remove code below and the `npm` mapping.
+		npm = make(map[string]nonce.Redeemer)
 		for prefix, serviceConfig := range c.WFE.RedeemNonceServices {
 			serviceConfig := serviceConfig
 			conn, err := bgrpc.ClientSetup(&serviceConfig, tlsConfig, scope, clk)
 			cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to redeem nonce service")
-			npm[prefix] = noncepb.NewNonceServiceClient(conn)
+			npm[prefix] = nonce.NewRedeemer(conn)
 		}
 	}
 
-	return rac, sac, rns, npm
+	return rac, sac, gnc, npm, rnc, rncKey
 }
 
 type errorWriter struct {
@@ -391,9 +448,9 @@ func main() {
 
 	clk := cmd.Clock()
 
-	rac, sac, rns, npm := setupWFE(c, stats, clk)
+	rac, sac, gnc, npm, rnc, npKey := setupWFE(c, stats, clk)
 
-	kp, err := goodkey.NewKeyPolicy(&c.WFE.GoodKey, sac.KeyBlocked)
+	kp, err := sagoodkey.NewKeyPolicy(&c.WFE.GoodKey, sac.KeyBlocked)
 	cmd.FailOnError(err, "Unable to create key policy")
 
 	if c.WFE.StaleTimeout.Duration == 0 {
@@ -434,8 +491,6 @@ func main() {
 		kp,
 		allCertChains,
 		issuerCerts,
-		rns,
-		npm,
 		logger,
 		c.WFE.Timeout.Duration,
 		c.WFE.StaleTimeout.Duration,
@@ -443,6 +498,10 @@ func main() {
 		pendingAuthorizationLifetime,
 		rac,
 		sac,
+		gnc,
+		npm,
+		rnc,
+		npKey,
 		accountGetter,
 	)
 	cmd.FailOnError(err, "Unable to create WFE")
@@ -458,9 +517,6 @@ func main() {
 	logger.Infof("Server running, listening on %s....", c.WFE.ListenAddress)
 	handler := wfe.Handler(stats)
 
-	// The gosec linter complains that ReadHeaderTimeout is not set. That's fine,
-	// because that field inherits its value from ReadTimeout.
-	////nolint:gosec
 	srv := http.Server{
 		ReadTimeout:  30 * time.Second,
 		WriteTimeout: 120 * time.Second,
diff --git a/cmd/ceremony/cert.go b/cmd/ceremony/cert.go
index d244c7d10..6de79966b 100644
--- a/cmd/ceremony/cert.go
+++ b/cmd/ceremony/cert.go
@@ -2,7 +2,7 @@ package notmain
 
 import (
 	"crypto"
-	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"encoding/asn1"
@@ -219,7 +219,7 @@ func generateSKID(pk []byte) ([]byte, error) {
 	if _, err := asn1.Unmarshal(pk, &pkixPublicKey); err != nil {
 		return nil, err
 	}
-	skid := sha1.Sum(pkixPublicKey.BitString.Bytes)
+	skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
 	return skid[:], nil
 }
 
diff --git a/cmd/cert-checker/main.go b/cmd/cert-checker/main.go
index 82e5a2889..340c56291 100644
--- a/cmd/cert-checker/main.go
+++ b/cmd/cert-checker/main.go
@@ -25,9 +25,11 @@ import (
 
 	"github.com/letsencrypt/boulder/cmd"
 	"github.com/letsencrypt/boulder/core"
+	corepb "github.com/letsencrypt/boulder/core/proto"
 	"github.com/letsencrypt/boulder/ctpolicy/loglist"
 	"github.com/letsencrypt/boulder/features"
 	"github.com/letsencrypt/boulder/goodkey"
+	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
 	"github.com/letsencrypt/boulder/identifier"
 	_ "github.com/letsencrypt/boulder/linter"
 	blog "github.com/letsencrypt/boulder/log"
@@ -238,10 +240,46 @@ var expectedExtensionContent = map[string][]byte{
 	"1.3.6.1.5.5.7.1.24": {0x30, 0x03, 0x02, 0x01, 0x05}, // Must staple feature
 }
 
+// checkValidations checks the database for matching authorizations that were
+// likely valid at the time the certificate was issued. Authorizations with
+// status = "deactivated" are counted for this, so long as their validatedAt
+// is before the issuance and expiration is after.
+func (c *certChecker) checkValidations(cert core.Certificate, dnsNames []string) error {
+	authzs, err := sa.SelectAuthzsMatchingIssuance(c.dbMap, cert.RegistrationID, cert.Issued, dnsNames)
+	if err != nil {
+		return fmt.Errorf("error checking authzs for certificate %s: %w", cert.Serial, err)
+	}
+
+	if len(authzs) == 0 {
+		return fmt.Errorf("no relevant authzs found valid at %s", cert.Issued)
+	}
+
+	// We may get multiple authorizations for the same name, but that's okay.
+	// Any authorization for a given name is sufficient.
+	nameToAuthz := make(map[string]*corepb.Authorization)
+	for _, m := range authzs {
+		nameToAuthz[m.Identifier] = m
+	}
+
+	var errors []error
+	for _, name := range dnsNames {
+		_, ok := nameToAuthz[name]
+		if !ok {
+			errors = append(errors, fmt.Errorf("missing authz for %q", name))
+			continue
+		}
+	}
+	if len(errors) > 0 {
+		return fmt.Errorf("%s", errors)
+	}
+	return nil
+}
+
 // checkCert returns a list of DNS names in the certificate and a list of problems with the certificate.
 func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]bool) ([]string, []string) {
 	var dnsNames []string
 	var problems []string
+
 	// Check that the digests match.
 	if cert.Digest != core.Fingerprint256(cert.DER) {
 		problems = append(problems, "Stored digest doesn't match certificate digest")
@@ -351,6 +389,17 @@ func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]b
 		if err != nil {
 			problems = append(problems, fmt.Sprintf("Key Policy isn't willing to issue for public key: %s", err))
 		}
+
+		if features.Enabled(features.CertCheckerChecksValidations) {
+			err = c.checkValidations(cert, parsedCert.DNSNames)
+			if err != nil {
+				if features.Enabled(features.CertCheckerRequiresValidations) {
+					problems = append(problems, err.Error())
+				} else {
+					c.logger.Errf("Certificate %s %s: %s", cert.Serial, parsedCert.DNSNames, err)
+				}
+			}
+		}
 	}
 	return dnsNames, problems
 }
@@ -439,7 +488,7 @@ func main() {
 	if config.CertChecker.GoodKey.BlockedKeyFile != "" {
 		cmd.Fail("cert-checker does not support checking against blocked key files")
 	}
-	kp, err := goodkey.NewKeyPolicy(&config.CertChecker.GoodKey, nil)
+	kp, err := sagoodkey.NewKeyPolicy(&config.CertChecker.GoodKey, nil)
 	cmd.FailOnError(err, "Unable to create key policy")
 
 	saDbMap, err := sa.InitWrappedDb(config.CertChecker.DB, prometheus.DefaultRegisterer, logger)
diff --git a/cmd/config.go b/cmd/config.go
index bfcf62c7e..55721606f 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -16,6 +16,7 @@ import (
 	"github.com/go-sql-driver/mysql"
 	"github.com/honeycombio/beeline-go"
 	"github.com/letsencrypt/boulder/core"
+	"google.golang.org/grpc/resolver"
 )
 
 // PasswordConfig contains a path to a file containing a password.
@@ -245,12 +246,20 @@ func (d *ConfigDuration) UnmarshalYAML(unmarshal func(interface{}) error) error
 	return nil
 }
 
+// ServiceDomain contains the service and domain name the gRPC client will use
+// to construct a SRV DNS query to lookup backends.
+type ServiceDomain struct {
+	Service string
+	Domain  string
+}
+
 // GRPCClientConfig contains the information necessary to setup a gRPC client
 // connection. The following field combinations are allowed:
 //
 // ServerIPAddresses, [Timeout]
 // ServerAddress, [Timeout], [DNSAuthority], [HostOverride]
-// SRVLookup, [Timeout], [DNSAuthority], [HostOverride]
+// SRVLookup, [Timeout], [DNSAuthority], [HostOverride], [SRVResolver]
+// SRVLookups, [Timeout], [DNSAuthority], [HostOverride], [SRVResolver]
 type GRPCClientConfig struct {
 	// DNSAuthority is a single <hostname|IPv4|[IPv6]>:<port> of the DNS server
 	// to be used for resolution of gRPC backends. If the address contains a
@@ -290,10 +299,21 @@ type GRPCClientConfig struct {
 	// $ dig @10.55.55.10 -t SRV _foo._tcp.service.consul +short
 	// 1 1 8080 0a585858.addr.dc1.consul.
 	// 1 1 8080 0a4d4d4d.addr.dc1.consul.
-	SRVLookup *struct {
-		Service string
-		Domain  string
-	}
+	SRVLookup *ServiceDomain
+
+	// SRVLookups allows you to pass multiple SRV records to the gRPC client.
+	// The gRPC client will resolves each SRV record and use the results to
+	// construct a list of backends to connect to. For more details, see the
+	// documentation for the SRVLookup field. Note: while you can pass multiple
+	// targets to the gRPC client using this field, all of the targets will use
+	// the same HostOverride and TLS configuration.
+	SRVLookups []*ServiceDomain
+
+	// SRVResolver is an optional override to indicate that a specific
+	// implementation of the SRV resolver should be used. The default is 'srv'
+	// For more details, see the documentation in:
+	// grpc/internal/resolver/dns/dns_resolver.go.
+	SRVResolver string
 
 	// ServerAddress is a single <hostname|IPv4|[IPv6]>:<port> or `:<port>` that
 	// the gRPC client will, if necessary, resolve via DNS and then connect to.
@@ -359,6 +379,10 @@ func (c *GRPCClientConfig) MakeTargetAndHostOverride() (string, string, error) {
 		return fmt.Sprintf("dns://%s/%s", c.DNSAuthority, c.ServerAddress), hostOverride, nil
 
 	} else if c.SRVLookup != nil {
+		scheme, err := c.makeSRVScheme()
+		if err != nil {
+			return "", "", err
+		}
 		if c.ServerIPAddresses != nil {
 			return "", "", errors.New(
 				"both 'SRVLookup' and 'serverIPAddresses' in gRPC client config. Only one should be provided",
@@ -371,12 +395,32 @@ func (c *GRPCClientConfig) MakeTargetAndHostOverride() (string, string, error) {
 		if c.HostOverride != "" {
 			hostOverride = c.HostOverride
 		}
-		return fmt.Sprintf("srv://%s/%s", c.DNSAuthority, targetHost), hostOverride, nil
+		return fmt.Sprintf("%s://%s/%s", scheme, c.DNSAuthority, targetHost), hostOverride, nil
+
+	} else if c.SRVLookups != nil {
+		scheme, err := c.makeSRVScheme()
+		if err != nil {
+			return "", "", err
+		}
+		if c.ServerIPAddresses != nil {
+			return "", "", errors.New(
+				"both 'SRVLookups' and 'serverIPAddresses' in gRPC client config. Only one should be provided",
+			)
+		}
+		// Lookup backends using multiple DNS SRV records.
+		var targetHosts []string
+		for _, s := range c.SRVLookups {
+			targetHosts = append(targetHosts, s.Service+"."+s.Domain)
+		}
+		if c.HostOverride != "" {
+			hostOverride = c.HostOverride
+		}
+		return fmt.Sprintf("%s://%s/%s", scheme, c.DNSAuthority, strings.Join(targetHosts, ",")), hostOverride, nil
 
 	} else {
 		if c.ServerIPAddresses == nil {
 			return "", "", errors.New(
-				"neither 'serverAddress', 'SRVLookup' nor 'serverIPAddresses' in gRPC client config. One should be provided",
+				"neither 'serverAddress', 'SRVLookup', 'SRVLookups' nor 'serverIPAddresses' in gRPC client config. One should be provided",
 			)
 		}
 		// Specify backends as a list of IP addresses.
@@ -384,6 +428,20 @@ func (c *GRPCClientConfig) MakeTargetAndHostOverride() (string, string, error) {
 	}
 }
 
+// makeSRVScheme returns the scheme to use for SRV lookups. If the SRVResolver
+// field is empty, it returns "srv". Otherwise it checks that the specified
+// SRVResolver is registered with the gRPC runtime and returns it.
+func (c *GRPCClientConfig) makeSRVScheme() (string, error) {
+	if c.SRVResolver == "" {
+		return "srv", nil
+	}
+	rb := resolver.Get(c.SRVResolver)
+	if rb == nil {
+		return "", fmt.Errorf("resolver %q is not registered", c.SRVResolver)
+	}
+	return c.SRVResolver, nil
+}
+
 // GRPCServerConfig contains the information needed to start a gRPC server.
 type GRPCServerConfig struct {
 	Address string `json:"address"`
diff --git a/cmd/nonce-service/main.go b/cmd/nonce-service/main.go
index 0cd7ac681..70fe53bff 100644
--- a/cmd/nonce-service/main.go
+++ b/cmd/nonce-service/main.go
@@ -2,6 +2,8 @@ package notmain
 
 import (
 	"flag"
+	"fmt"
+	"net"
 
 	"github.com/honeycombio/beeline-go"
 
@@ -15,18 +17,53 @@ type Config struct {
 	NonceService struct {
 		cmd.ServiceConfig
 
-		MaxUsed     int
+		MaxUsed int
+		// TODO(#6610): Remove once we've moved to derivable prefixes by
+		// default.
 		NoncePrefix string
 
+		// UseDerivablePrefix indicates whether to use a nonce prefix derived
+		// from the gRPC listening address. If this is false, the nonce prefix
+		// will be the value of the NoncePrefix field. If this is true, the
+		// NoncePrefixKey field is required.
+		//
+		// TODO(#6610): Remove once we've moved to derivable prefixes by
+		// default.
+		UseDerivablePrefix bool
+
+		// NoncePrefixKey is a secret used for deriving the prefix of each nonce
+		// instance. It should contain 256 bits (32 bytes) of random data to be
+		// suitable as an HMAC-SHA256 key (e.g. the output of `openssl rand -hex
+		// 32`). In a multi-DC deployment this value should be the same across
+		// all boulder-wfe and nonce-service instances. This is only used if
+		// UseDerivablePrefix is true.
+		//
+		// TODO(#6610): Edit this comment once we've moved to derivable prefixes
+		// by default.
+		NoncePrefixKey cmd.PasswordConfig
+
 		Syslog  cmd.SyslogConfig
 		Beeline cmd.BeelineConfig
 	}
 }
 
+func derivePrefix(key string, grpcAddr string) (string, error) {
+	host, port, err := net.SplitHostPort(grpcAddr)
+	if err != nil {
+		return "", fmt.Errorf("parsing gRPC listen address: %w", err)
+	}
+	if host != "" && port != "" {
+		hostIP := net.ParseIP(host)
+		if hostIP == nil {
+			return "", fmt.Errorf("parsing IP from gRPC listen address: %w", err)
+		}
+	}
+	return nonce.DerivePrefix(grpcAddr, key), nil
+}
+
 func main() {
 	grpcAddr := flag.String("addr", "", "gRPC listen address override")
 	debugAddr := flag.String("debug-addr", "", "Debug server address override")
-	prefixOverride := flag.String("prefix", "", "Override the configured nonce prefix")
 	configFile := flag.String("config", "", "File path to the configuration file for this service")
 	flag.Parse()
 
@@ -40,8 +77,22 @@ func main() {
 	if *debugAddr != "" {
 		c.NonceService.DebugAddr = *debugAddr
 	}
-	if *prefixOverride != "" {
-		c.NonceService.NoncePrefix = *prefixOverride
+
+	// TODO(#6610): Remove once we've moved to derivable prefixes by default.
+	if c.NonceService.NoncePrefix != "" && c.NonceService.UseDerivablePrefix {
+		cmd.Fail("Cannot set both 'noncePrefix' and 'useDerivablePrefix'")
+	}
+
+	// TODO(#6610): Remove once we've moved to derivable prefixes by default.
+	if c.NonceService.UseDerivablePrefix && c.NonceService.NoncePrefixKey.PasswordFile == "" {
+		cmd.Fail("Cannot set 'noncePrefixKey' without 'useDerivablePrefix'")
+	}
+
+	if c.NonceService.UseDerivablePrefix && c.NonceService.NoncePrefixKey.PasswordFile != "" {
+		key, err := c.NonceService.NoncePrefixKey.Pass()
+		cmd.FailOnError(err, "Failed to load 'noncePrefixKey' file.")
+		c.NonceService.NoncePrefix, err = derivePrefix(key, c.NonceService.GRPC.Address)
+		cmd.FailOnError(err, "Failed to derive nonce prefix")
 	}
 
 	bc, err := c.NonceService.Beeline.Load()
diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go
index 983fb33ab..ed88332cd 100644
--- a/cmd/ocsp-responder/main.go
+++ b/cmd/ocsp-responder/main.go
@@ -237,9 +237,6 @@ as generated by Boulder's ceremony command.
 
 	m := mux(c.OCSPResponder.Path, source, c.OCSPResponder.Timeout.Duration, scope, logger, c.OCSPResponder.LogSampleRate)
 
-	// The gosec linter complains that ReadHeaderTimeout is not set. That's fine,
-	// because that field inherits its value from ReadTimeout.
-	////nolint:gosec
 	srv := &http.Server{
 		ReadTimeout:  30 * time.Second,
 		WriteTimeout: 120 * time.Second,
diff --git a/cmd/rocsp-tool/client.go b/cmd/rocsp-tool/client.go
index d431e0347..37c70c0f4 100644
--- a/cmd/rocsp-tool/client.go
+++ b/cmd/rocsp-tool/client.go
@@ -20,7 +20,7 @@ import (
 )
 
 type client struct {
-	redis         rocsp.Writer
+	redis         *rocsp.RWClient
 	db            *db.WrappedMap // optional
 	ocspGenerator capb.OCSPGeneratorClient
 	clk           clock.Clock
diff --git a/cmd/shell.go b/cmd/shell.go
index 36bef3db8..2230c85fd 100644
--- a/cmd/shell.go
+++ b/cmd/shell.go
@@ -258,12 +258,10 @@ func newStatsRegistry(addr string, logger blog.Logger) prometheus.Registerer {
 		ErrorLog: promLogger{logger},
 	}))
 
-	// The gosec linter complains that ReadHeaderTimeout is not set. That's fine,
-	// because this endpoint is not exposed to the internet.
-	////nolint:gosec
 	server := http.Server{
-		Addr:    addr,
-		Handler: mux,
+		Addr:        addr,
+		Handler:     mux,
+		ReadTimeout: time.Minute,
 	}
 	go func() {
 		err := server.ListenAndServe()
diff --git a/core/objects.go b/core/objects.go
index f89c99f46..2afbfbe91 100644
--- a/core/objects.go
+++ b/core/objects.go
@@ -337,9 +337,6 @@ type Authorization struct {
 	// slice and the order of these challenges may not be predictable.
 	Challenges []Challenge `json:"challenges,omitempty" db:"-"`
 
-	// This field is deprecated. It's filled in by WFE for the ACMEv1 API.
-	Combinations [][]int `json:"combinations,omitempty" db:"combinations"`
-
 	// Wildcard is a Boulder-specific Authorization field that indicates the
 	// authorization was created as a result of an order containing a name with
 	// a `*.`wildcard prefix. This will help convey to users that an
diff --git a/core/proto/core.proto b/core/proto/core.proto
index 946bb16c8..157f68a91 100644
--- a/core/proto/core.proto
+++ b/core/proto/core.proto
@@ -76,7 +76,7 @@ message Authorization {
   string status = 4;
   int64 expires = 5; // Unix timestamp (nanoseconds)
   repeated core.Challenge challenges = 6;
-  reserved 7; // previously combinations
+  reserved 7; // previously ACMEv1 combinations
   reserved 8; // previously v2
 }
 
diff --git a/db/map.go b/db/map.go
index a2acf0b17..c79182500 100644
--- a/db/map.go
+++ b/db/map.go
@@ -6,9 +6,9 @@ import (
 	"errors"
 	"fmt"
 	"regexp"
-	"strings"
 
 	gorp "github.com/go-gorp/gorp/v3"
+	"github.com/go-sql-driver/mysql"
 )
 
 // ErrDatabaseOp wraps an underlying err with a description of the operation
@@ -20,21 +20,6 @@ type ErrDatabaseOp struct {
 	Err   error
 }
 
-// noRows returns true when the underlying error is sql.ErrNoRows and indicates
-// that the error was that no results were found.
-func (e ErrDatabaseOp) noRows() bool {
-	return e.Err == sql.ErrNoRows
-}
-
-// duplicate returns true when the underlying error has a message with a prefix
-// matching "Error 1062: Duplicate entry". This is the error prefixed returned
-// by MariaDB when a duplicate row is to be inserted.
-func (e ErrDatabaseOp) duplicate() bool {
-	return strings.HasPrefix(
-		e.Err.Error(),
-		"Error 1062: Duplicate entry")
-}
-
 // Error for an ErrDatabaseOp composes a message with context about the
 // operation and table as well as the underlying Err's error message.
 func (e ErrDatabaseOp) Error() string {
@@ -57,31 +42,19 @@ func (e ErrDatabaseOp) Unwrap() error {
 	return e.Err
 }
 
-// IsNoRows is a utility function for casting an error to ErrDatabaseOp and
-// returning true if its wrapped err is sql.ErrNoRows. If the error is not an
-// ErrDatabaseOp the return value of IsNoRows will always be false.
+// IsNoRows is a utility function for determining if an error wraps the go sql
+// package's ErrNoRows, which is returned when a Scan operation has no more
+// results to return, and as such is returned by many gorp methods.
 func IsNoRows(err error) bool {
-	// if the err is an ErrDatabaseOp instance, return its noRows() result to see
-	// if the inner err is sql.ErrNoRows
-	var dbErr ErrDatabaseOp
-	if errors.As(err, &dbErr) {
-		return dbErr.noRows()
-	}
-	return false
+	return errors.Is(err, sql.ErrNoRows)
 }
 
-// IsDuplicate is a utility function for casting an error to ErrDatabaseOp and
-// returning a boolean indicating if it is a duplicate error or not. If the
-// error is not an ErrDatabaseOp the return value of IsDuplicate will always be
-// false.
+// IsDuplicate is a utility function for determining if an error wrap MySQL's
+// Error 1062: Duplicate entry. This error is returned when inserting a row
+// would violate a unique key constraint.
 func IsDuplicate(err error) bool {
-	// if the err is an ErrDatabaseOp instance, return its duplicate() result to
-	// see if the inner err indicates a duplicate row error.
-	var dbErr ErrDatabaseOp
-	if errors.As(err, &dbErr) {
-		return dbErr.duplicate()
-	}
-	return false
+	var dbErr *mysql.MySQLError
+	return errors.As(err, &dbErr) && dbErr.Number == 1062
 }
 
 // WrappedMap wraps a *gorp.DbMap such that its major functions wrap error
diff --git a/docker-compose.next.yml b/docker-compose.next.yml
index 9be028705..c9b77f97e 100644
--- a/docker-compose.next.yml
+++ b/docker-compose.next.yml
@@ -5,26 +5,3 @@ services:
       FAKE_DNS: 10.77.77.77
       BOULDER_CONFIG_DIR: &boulder_config_dir test/config-next
       GOFLAGS: -mod=vendor
-  # TODO(#6517): remove bredis_clusterer
-  bredis_clusterer:
-    depends_on:
-      - bredis_7
-      - bredis_8
-  # TODO(#6517): move both nodes to docker-compose.yml
-  bredis_7:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.8
-
-  bredis_8:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.9
diff --git a/docker-compose.yml b/docker-compose.yml
index 7fd47d364..4ce7c1d64 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,7 +3,7 @@ services:
   boulder:
     # When updating the Go version here, please also update
     # .github/workflows/release.yml and .github/workflows/try-release.yml
-    image: &boulder_image letsencrypt/boulder-tools:${BOULDER_TOOLS_TAG:-go1.19.5_2023-01-10}
+    image: &boulder_image letsencrypt/boulder-tools:${BOULDER_TOOLS_TAG:-go1.19.5_2023-02-02}
     environment:
       FAKE_DNS: 10.77.77.77
       BOULDER_CONFIG_DIR: &boulder_config_dir test/config
@@ -41,7 +41,8 @@ services:
       - 4003:4003 # OCSP
     depends_on:
       - bmysql
-      - bredis_clusterer
+      - bredis_1
+      - bredis_2
       - bconsul
     entrypoint: test/entrypoint.sh
     working_dir: &boulder_working_dir /boulder
@@ -62,13 +63,12 @@ services:
     command: mysqld --bind-address=0.0.0.0 --slow-query-log --log-output=TABLE --log-queries-not-using-indexes=ON
     logging:
       driver: none
-  # TODO(#6517): replace all bredis_ services with those from
-  # docker-compose.next.yml.
+
   bredis_1:
     image: redis:6.2.7
     volumes:
       - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
+    command: redis-server /test/redis.config
     networks:
       redisnet:
         ipv4_address: 10.33.33.2
@@ -77,68 +77,11 @@ services:
     image: redis:6.2.7
     volumes:
       - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
+    command: redis-server /test/redis.config
     networks:
       redisnet:
         ipv4_address: 10.33.33.3
 
-  bredis_3:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.4
-
-  bredis_4:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.5
-
-  bredis_5:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.6
-
-  bredis_6:
-    image: redis:6.2.7
-    volumes:
-      - ./test/:/test/:cached
-    command: redis-server /test/redis-cluster.config
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.7
-  # TODO(#6517): remove bredis_clusterer.
-  bredis_clusterer:
-    image: redis:6.2.7
-    environment:
-      BOULDER_CONFIG_DIR: *boulder_config_dir
-    volumes:
-      - ./test/:/test/:cached
-      - ./cluster/:/cluster/:cached
-    command: /test/wait-for-it.sh 10.33.33.2 4218 /test/redis-create.sh
-    depends_on:
-      - bredis_1
-      - bredis_2
-      - bredis_3
-      - bredis_4
-      - bredis_5
-      - bredis_6
-    networks:
-      redisnet:
-        ipv4_address: 10.33.33.10
-        aliases:
-          - boulder-redis-clusterer
-
   bconsul:
     image: hashicorp/consul:1.13.1
     volumes:
diff --git a/docs/redis.md b/docs/redis.md
index 378e8b7e5..6d796b6f6 100644
--- a/docs/redis.md
+++ b/docs/redis.md
@@ -1,13 +1,8 @@
 # Redis
 
-TODO(#6517): Update this to reflect the use of Redis Ring.
-
-We use Redis Cluster for OCSP. The Boulder dev environment stands up a cluster
-of 6 nodes, with 3 primaries and 3 replicas. Check docker-compose.yml for
-details of those.
-
-The initial setup is done by test/redis-create.sh, which assigns all the
-individual Redis nodes to their roles as primaries or replicas.
+We use Redis for OCSP. The Boulder dev environment stands up a two nodes. We use
+the Ring client in the github.com/redis/go-redis package to consistently hash
+our reads and writes across these two nodes. 
 
 ## Debugging
 
@@ -19,18 +14,24 @@ The first tool you might turn to is `redis-cli`. You probably don't
 have redis-cli on your host, so we'll run it in a Docker container. We
 also need to pass some specific arguments for TLS and authentication. There's a
 script that handles all that for you: `test/redis-cli.sh`. First, make sure your
-redis cluster is running:
+redis is running:
 
-```
-docker compose up bredis_clusterer
+```shell
+docker compose up boulder
 ```
 
-Then, in a different window, run:
+Then, in a different window, run the following to connect to `bredis_1`:
 
-```
+```shell
 ./test/redis-cli.sh -h 10.33.33.2
 ```
 
+Similarly, to connect to `bredis_2`:
+
+```shell
+./test/redis-cli.sh -h 10.33.33.3
+```
+
 You can pass any IP address for the -h (host) parameter. The full list of IP
 addresses for Redis nodes is in `docker-compose.yml`. You can also pass other
 redis-cli commandline parameters. They'll get passed through.
@@ -38,7 +39,7 @@ redis-cli commandline parameters. They'll get passed through.
 You may want to go a level deeper and communicate with a Redis node using the
 Redis protocol. Here's the command to do that (run from the Boulder root):
 
-```
+```shell
 openssl s_client -connect 10.33.33.2:4218 \
   -CAfile test/redis-tls/minica.pem \
   -cert test/redis-tls/boulder/cert.pem \
diff --git a/features/featureflag_string.go b/features/featureflag_string.go
index 8790bbe43..e6eaf79cb 100644
--- a/features/featureflag_string.go
+++ b/features/featureflag_string.go
@@ -21,11 +21,13 @@ func _() {
 	_ = x[ROCSPStage6-10]
 	_ = x[ROCSPStage7-11]
 	_ = x[ExpirationMailerUsesJoin-12]
+	_ = x[CertCheckerChecksValidations-13]
+	_ = x[CertCheckerRequiresValidations-14]
 }
 
-const _FeatureFlag_name = "unusedStoreRevokerInfoCAAValidationMethodsCAAAccountURIEnforceMultiVAMultiVAFullResultsMandatoryPOSTAsGETECDSAForAllServeRenewalInfoAllowUnrecognizedFeaturesROCSPStage6ROCSPStage7ExpirationMailerUsesJoin"
+const _FeatureFlag_name = "unusedStoreRevokerInfoCAAValidationMethodsCAAAccountURIEnforceMultiVAMultiVAFullResultsMandatoryPOSTAsGETECDSAForAllServeRenewalInfoAllowUnrecognizedFeaturesROCSPStage6ROCSPStage7ExpirationMailerUsesJoinCertCheckerChecksValidationsCertCheckerRequiresValidations"
 
-var _FeatureFlag_index = [...]uint8{0, 6, 22, 42, 55, 69, 87, 105, 116, 132, 157, 168, 179, 203}
+var _FeatureFlag_index = [...]uint16{0, 6, 22, 42, 55, 69, 87, 105, 116, 132, 157, 168, 179, 203, 231, 261}
 
 func (i FeatureFlag) String() string {
 	if i < 0 || i >= FeatureFlag(len(_FeatureFlag_index)-1) {
diff --git a/features/features.go b/features/features.go
index bc9201a8a..058fa5ef4 100644
--- a/features/features.go
+++ b/features/features.go
@@ -53,23 +53,35 @@ const (
 	// rather than a SELECT from certificateStatus followed by thousands of
 	// one-row SELECTs from certificates.
 	ExpirationMailerUsesJoin
+
+	// CertCheckerChecksValidations enables an extra query for each certificate
+	// checked, to find the relevant authzs. Since this query might be
+	// expensive, we gate it behind a feature flag.
+	CertCheckerChecksValidations
+
+	// CertCheckerRequiresValidations causes cert-checker to fail if the
+	// query enabled by CertCheckerChecksValidations didn't find corresponding
+	// authorizations.
+	CertCheckerRequiresValidations
 )
 
 // List of features and their default value, protected by fMu
 var features = map[FeatureFlag]bool{
-	unused:                    false,
-	CAAValidationMethods:      false,
-	CAAAccountURI:             false,
-	EnforceMultiVA:            false,
-	MultiVAFullResults:        false,
-	MandatoryPOSTAsGET:        false,
-	StoreRevokerInfo:          false,
-	ECDSAForAll:               false,
-	ServeRenewalInfo:          false,
-	AllowUnrecognizedFeatures: false,
-	ROCSPStage6:               false,
-	ROCSPStage7:               false,
-	ExpirationMailerUsesJoin:  false,
+	unused:                         false,
+	CAAValidationMethods:           false,
+	CAAAccountURI:                  false,
+	EnforceMultiVA:                 false,
+	MultiVAFullResults:             false,
+	MandatoryPOSTAsGET:             false,
+	StoreRevokerInfo:               false,
+	ECDSAForAll:                    false,
+	ServeRenewalInfo:               false,
+	AllowUnrecognizedFeatures:      false,
+	ROCSPStage6:                    false,
+	ROCSPStage7:                    false,
+	ExpirationMailerUsesJoin:       false,
+	CertCheckerChecksValidations:   false,
+	CertCheckerRequiresValidations: false,
 }
 
 var fMu = new(sync.RWMutex)
diff --git a/go.mod b/go.mod
index 9864fd9c2..28b4606f0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,15 +3,15 @@ module github.com/letsencrypt/boulder
 go 1.18
 
 require (
-	github.com/aws/aws-sdk-go-v2 v1.17.3
+	github.com/aws/aws-sdk-go-v2 v1.17.4
 	github.com/aws/aws-sdk-go-v2/config v1.18.9
-	github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0
+	github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2
 	github.com/aws/smithy-go v1.13.5
 	github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af
 	github.com/eggsampler/acme/v3 v3.3.0
 	github.com/go-gorp/gorp/v3 v3.0.2
 	github.com/go-redis/redis/v8 v8.11.5
-	github.com/go-sql-driver/mysql v1.5.0
+	github.com/go-sql-driver/mysql v1.7.0
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
 	github.com/google/certificate-transparency-go v1.0.22-0.20181127102053-c25855a82c75
 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
@@ -22,7 +22,7 @@ require (
 	github.com/letsencrypt/pkcs11key/v4 v4.0.0
 	github.com/miekg/dns v1.1.50
 	github.com/miekg/pkcs11 v1.1.1
-	github.com/prometheus/client_golang v1.13.0
+	github.com/prometheus/client_golang v1.14.0
 	github.com/prometheus/client_model v0.3.0
 	github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399
 	github.com/weppos/publicsuffix-go v0.20.1-0.20221209102050-40d9c30084b3
@@ -44,14 +44,14 @@ require (
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
 	github.com/aws/aws-sdk-go-v2/credentials v1.13.9 // indirect
 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sts v1.18.1 // indirect
diff --git a/go.sum b/go.sum
index 03f829b24..a5c4ff83e 100644
--- a/go.sum
+++ b/go.sum
@@ -43,8 +43,9 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
 github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
+github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY=
+github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
 github.com/aws/aws-sdk-go-v2/config v1.18.9 h1:pd+QUO1dvro6vGOuhgglJV6adGunU95xSTSzsQGhKpY=
@@ -53,24 +54,27 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.9 h1:oxM/C8eXGsiHH+u0gZGo1++QTFPf
 github.com/aws/aws-sdk-go-v2/credentials v1.13.9/go.mod h1:45DrDZTok50mEx4Uw59ym7n11Oy7G4gt0Pez2Z4ktAA=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 h1:H/mF2LNWwX00lD6FlYfKpLLZgUW7oIzCBkig78x4Xok=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18/go.mod h1:T2Ku+STrYQ1zIkL1wMvj8P3wWQaaCMKNdz70MT2FLfE=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 h1:FGvpyTg2LKEmMrLlpjOgkoNp9XF5CGeyAyo33LdqZW8=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 h1:kv5vRAl00tozRxSnI0IszPWGXsJOyA7hmEUHFYqsyvw=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22/go.mod h1:Od+GU5+Yx41gryN/ZGZzAJMZ9R1yn6lgA0fD5Lo5SkQ=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 h1:c5+bNdV8E4fIPteWx4HZSkqI07oY9exbfQ7JH7Yx4PI=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23/go.mod h1:1jcUfF+FAOEwtIcNiHPaV4TSoZqkUIPzrohmD7fb95c=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 h1:vY5siRXvW5TrOKm2qKEf9tliBfdLxdfy0i02LOcmqUo=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21/go.mod h1:WZvNXT1XuH8dnJM0HvOlvk+RNn7NbAPvA/ACO0QarSc=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0 h1:wddsyuESfviaiXk3w9N6/4iRwTg/a3gktjODY6jYQBo=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0/go.mod h1:L2l2/q76teehcW7YEsgsDjqdsDTERJeX3nOMIFlgGUE=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 h1:ISLJ2BKXe4zzyZ7mp5ewKECiw0U7KpLgS3S6OxY9Cm0=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK54XArazLvn2wvWMRBi/jGrWii46qbr5DyPGjc=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 h1:5EQWIFO+Hc8E2hFcXQJ1vm6ufl/PMt/6RVRDZRju2vM=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM=
 github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c=
 github.com/aws/aws-sdk-go-v2/service/sso v1.12.0/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI=
@@ -161,8 +165,9 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS
 github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
 github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobuffalo/attrs v1.0.2/go.mod h1:tJ7wJj6XbMNhYwJ8fl2PFDpDcUfsG1spWdUJISvPAZQ=
 github.com/gobuffalo/envy v1.10.1/go.mod h1:AWx4++KnNOW3JOeEvhSaq+mvgAvnMYOY1XSIin4Mago=
@@ -402,8 +407,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
+github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
+github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
diff --git a/goodkey/good_key.go b/goodkey/good_key.go
index 336e637e7..741bbf263 100644
--- a/goodkey/good_key.go
+++ b/goodkey/good_key.go
@@ -13,8 +13,6 @@ import (
 
 	"github.com/letsencrypt/boulder/core"
 	berrors "github.com/letsencrypt/boulder/errors"
-	sapb "github.com/letsencrypt/boulder/sa/proto"
-	"google.golang.org/grpc"
 
 	"github.com/titanous/rocacheck"
 )
@@ -67,10 +65,12 @@ func badKey(msg string, args ...interface{}) error {
 	return fmt.Errorf("%w%s", ErrBadKey, fmt.Errorf(msg, args...))
 }
 
-// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy,
-// rather than storing a full sa.SQLStorageAuthority. This makes testing
+// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey functionality to KeyPolicy,
+// rather than storing a full sa.SQLStorageAuthority. This allows external
+// users who don’t want to import all of boulder/sa, and makes testing
 // significantly simpler.
-type BlockedKeyCheckFunc func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error)
+// On success, the function returns a boolean which is true if the key is blocked.
+type BlockedKeyCheckFunc func(ctx context.Context, keyHash []byte) (bool, error)
 
 // KeyPolicy determines which types of key may be used with various boulder
 // operations.
@@ -81,7 +81,7 @@ type KeyPolicy struct {
 	weakRSAList        *WeakRSAKeys
 	blockedList        *blockedKeys
 	fermatRounds       int
-	dbCheck            BlockedKeyCheckFunc
+	blockedCheck       BlockedKeyCheckFunc
 }
 
 // NewKeyPolicy returns a KeyPolicy that allows RSA, ECDSA256 and ECDSA384.
@@ -96,7 +96,7 @@ func NewKeyPolicy(config *Config, bkc BlockedKeyCheckFunc) (KeyPolicy, error) {
 		AllowRSA:           true,
 		AllowECDSANISTP256: true,
 		AllowECDSANISTP384: true,
-		dbCheck:            bkc,
+		blockedCheck:       bkc,
 	}
 	if config.WeakKeyFile != "" {
 		keyList, err := LoadWeakRSASuffixes(config.WeakKeyFile)
@@ -141,15 +141,15 @@ func (policy *KeyPolicy) GoodKey(ctx context.Context, key crypto.PublicKey) erro
 			return badKey("public key is forbidden")
 		}
 	}
-	if policy.dbCheck != nil {
+	if policy.blockedCheck != nil {
 		digest, err := core.KeyDigest(key)
 		if err != nil {
 			return badKey("%w", err)
 		}
-		exists, err := policy.dbCheck(ctx, &sapb.KeyBlockedRequest{KeyHash: digest[:]})
+		exists, err := policy.blockedCheck(ctx, digest[:])
 		if err != nil {
 			return err
-		} else if exists.Exists {
+		} else if exists {
 			return badKey("public key is forbidden")
 		}
 	}
diff --git a/goodkey/sagoodkey/good_key.go b/goodkey/sagoodkey/good_key.go
new file mode 100644
index 000000000..f714fb5bb
--- /dev/null
+++ b/goodkey/sagoodkey/good_key.go
@@ -0,0 +1,31 @@
+package sagoodkey
+
+import (
+	"context"
+
+	"github.com/letsencrypt/boulder/goodkey"
+	sapb "github.com/letsencrypt/boulder/sa/proto"
+	"google.golang.org/grpc"
+)
+
+// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy,
+// rather than storing a full sa.SQLStorageAuthority. This makes testing
+// significantly simpler.
+type BlockedKeyCheckFunc func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error)
+
+// NewKeyPolicy returns a KeyPolicy that uses a sa.BlockedKey method.
+// See goodkey.NewKeyPolicy for more details about the policy itself.
+func NewKeyPolicy(config *goodkey.Config, bkc BlockedKeyCheckFunc) (goodkey.KeyPolicy, error) {
+	var genericCheck goodkey.BlockedKeyCheckFunc
+	if bkc != nil {
+		genericCheck = func(ctx context.Context, keyHash []byte) (bool, error) {
+			exists, err := bkc(ctx, &sapb.KeyBlockedRequest{KeyHash: keyHash})
+			if err != nil {
+				return false, err
+			}
+			return exists.Exists, nil
+		}
+	}
+
+	return goodkey.NewKeyPolicy(config, genericCheck)
+}
diff --git a/grpc/internal/resolver/dns/dns_resolver.go b/grpc/internal/resolver/dns/dns_resolver.go
index ff0a56147..2586e947d 100644
--- a/grpc/internal/resolver/dns/dns_resolver.go
+++ b/grpc/internal/resolver/dns/dns_resolver.go
@@ -33,8 +33,10 @@ import (
 	"time"
 
 	"github.com/letsencrypt/boulder/grpc/internal/backoff"
+	"github.com/letsencrypt/boulder/grpc/noncebalancer"
 	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/resolver"
+	"google.golang.org/grpc/serviceconfig"
 )
 
 var logger = grpclog.Component("srv")
@@ -47,7 +49,8 @@ var (
 )
 
 func init() {
-	resolver.Register(NewBuilder())
+	resolver.Register(NewDefaultSRVBuilder())
+	resolver.Register(NewNonceSRVBuilder())
 }
 
 const defaultDNSSvrPort = "53"
@@ -88,47 +91,65 @@ var customAuthorityResolver = func(authority string) (*net.Resolver, error) {
 	}, nil
 }
 
-// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
-func NewBuilder() resolver.Builder {
-	return &dnsBuilder{}
+// NewDefaultSRVBuilder creates a srvBuilder which is used to factory SRV DNS
+// resolvers.
+func NewDefaultSRVBuilder() resolver.Builder {
+	return &srvBuilder{scheme: "srv"}
 }
 
-type dnsBuilder struct{}
+// NewSRVBuilder creates a srvBuilder which is used to factory SRV DNS resolvers
+// with a custom grpc.Balancer use by nonce-service clients.
+func NewNonceSRVBuilder() resolver.Builder {
+	return &srvBuilder{scheme: noncebalancer.SRVResolverScheme, balancer: noncebalancer.Name}
+}
+
+type srvBuilder struct {
+	scheme   string
+	balancer string
+}
 
 // Build creates and starts a DNS resolver that watches the name resolution of the target.
-func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
-	service, domain, err := parseServiceDomain(target.Endpoint)
-	if err != nil {
-		return nil, err
+func (b *srvBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
+	var names []name
+	for _, i := range strings.Split(target.Endpoint, ",") {
+		service, domain, err := parseServiceDomain(i)
+		if err != nil {
+			return nil, err
+		}
+		names = append(names, name{service: service, domain: domain})
 	}
 
 	ctx, cancel := context.WithCancel(context.Background())
 	d := &dnsResolver{
-		service: service,
-		domain:  domain,
-		ctx:     ctx,
-		cancel:  cancel,
-		cc:      cc,
-		rn:      make(chan struct{}, 1),
+		names:  names,
+		ctx:    ctx,
+		cancel: cancel,
+		cc:     cc,
+		rn:     make(chan struct{}, 1),
 	}
 
 	if target.Authority == "" {
 		d.resolver = defaultResolver
 	} else {
+		var err error
 		d.resolver, err = customAuthorityResolver(target.Authority)
 		if err != nil {
 			return nil, err
 		}
 	}
 
+	if b.balancer != "" {
+		d.serviceConfig = cc.ParseServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, b.balancer))
+	}
+
 	d.wg.Add(1)
 	go d.watcher()
 	return d, nil
 }
 
-// Scheme returns the naming scheme of this resolver builder, which is "srv".
-func (b *dnsBuilder) Scheme() string {
-	return "srv"
+// Scheme returns the naming scheme of this resolver builder.
+func (b *srvBuilder) Scheme() string {
+	return b.scheme
 }
 
 type netResolver interface {
@@ -136,10 +157,14 @@ type netResolver interface {
 	LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error)
 }
 
+type name struct {
+	service string
+	domain  string
+}
+
 // dnsResolver watches for the name resolution update for a non-IP target.
 type dnsResolver struct {
-	service  string
-	domain   string
+	names    []name
 	resolver netResolver
 	ctx      context.Context
 	cancel   context.CancelFunc
@@ -152,7 +177,8 @@ type dnsResolver struct {
 	// If Close() doesn't wait for watcher() goroutine finishes, race detector sometimes
 	// will warns lookup (READ the lookup function pointers) inside watcher() goroutine
 	// has data race with replaceNetFunc (WRITE the lookup function pointers).
-	wg sync.WaitGroup
+	wg            sync.WaitGroup
+	serviceConfig *serviceconfig.ParseResult
 }
 
 // ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
@@ -178,6 +204,9 @@ func (d *dnsResolver) watcher() {
 			// Report error to the underlying grpc.ClientConn.
 			d.cc.ReportError(err)
 		} else {
+			if d.serviceConfig != nil {
+				state.ServiceConfig = d.serviceConfig
+			}
 			err = d.cc.UpdateState(*state)
 		}
 
@@ -209,29 +238,31 @@ func (d *dnsResolver) watcher() {
 
 func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
 	var newAddrs []resolver.Address
-	_, srvs, err := d.resolver.LookupSRV(d.ctx, d.service, "tcp", d.domain)
-	if err != nil {
-		err = handleDNSError(err, "SRV") // may become nil
-		return nil, err
-	}
-	for _, s := range srvs {
-		backendAddrs, err := d.resolver.LookupHost(d.ctx, s.Target)
+	for _, n := range d.names {
+		_, srvs, err := d.resolver.LookupSRV(d.ctx, n.service, "tcp", n.domain)
 		if err != nil {
-			err = handleDNSError(err, "A") // may become nil
-			if err == nil {
-				// If there are other SRV records, look them up and ignore this
-				// one that does not exist.
-				continue
-			}
+			err = handleDNSError(err, "SRV") // may become nil
 			return nil, err
 		}
-		for _, a := range backendAddrs {
-			ip, ok := formatIP(a)
-			if !ok {
-				return nil, fmt.Errorf("srv: error parsing A record IP address %v", a)
+		for _, s := range srvs {
+			backendAddrs, err := d.resolver.LookupHost(d.ctx, s.Target)
+			if err != nil {
+				err = handleDNSError(err, "A") // may become nil
+				if err == nil {
+					// If there are other SRV records, look them up and ignore this
+					// one that does not exist.
+					continue
+				}
+				return nil, err
+			}
+			for _, a := range backendAddrs {
+				ip, ok := formatIP(a)
+				if !ok {
+					return nil, fmt.Errorf("srv: error parsing A record IP address %v", a)
+				}
+				addr := ip + ":" + strconv.Itoa(int(s.Port))
+				newAddrs = append(newAddrs, resolver.Address{Addr: addr, ServerName: s.Target})
 			}
-			addr := ip + ":" + strconv.Itoa(int(s.Port))
-			newAddrs = append(newAddrs, resolver.Address{Addr: addr, ServerName: s.Target})
 		}
 	}
 	return newAddrs, nil
diff --git a/grpc/noncebalancer/noncebalancer.go b/grpc/noncebalancer/noncebalancer.go
new file mode 100644
index 000000000..605927fa1
--- /dev/null
+++ b/grpc/noncebalancer/noncebalancer.go
@@ -0,0 +1,112 @@
+package noncebalancer
+
+import (
+	"errors"
+
+	"github.com/letsencrypt/boulder/nonce"
+	"google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/balancer/base"
+)
+
+const (
+	// Name is the name used to register the nonce balancer with the gRPC
+	// runtime.
+	Name = "nonce"
+
+	// SRVResolverScheme is the scheme used to invoke an instance of the SRV
+	// resolver which will use the noncebalancer to pick backends. It would be
+	// ideal to export this from the SRV resolver package but that package is
+	// internal.
+	SRVResolverScheme = "nonce-srv"
+)
+
+var errMissingPrefixCtxKey = errors.New("nonce.PrefixCtxKey value required in RPC context")
+var errMissingHMACKeyCtxKey = errors.New("nonce.HMACKeyCtxKey value required in RPC context")
+var errInvalidPrefixCtxKeyType = errors.New("nonce.PrefixCtxKey value in RPC context must be a string")
+var errInvalidHMACKeyCtxKeyType = errors.New("nonce.HMACKeyCtxKey value in RPC context must be a string")
+
+// Balancer implements the base.PickerBuilder interface. It's used to create new
+// balancer.Picker instances. It should only be used by nonce-service clients.
+type Balancer struct{}
+
+// Compile-time assertion that *Balancer implements the base.PickerBuilder
+// interface.
+var _ base.PickerBuilder = (*Balancer)(nil)
+
+// Build implements the base.PickerBuilder interface. It is called by the gRPC
+// runtime when the balancer is first initialized and when the set of backend
+// (SubConn) addresses changes. It is responsible for initializing the Picker's
+// backends map and returning a balancer.Picker.
+func (b *Balancer) Build(buildInfo base.PickerBuildInfo) balancer.Picker {
+	return &Picker{
+		backends: buildInfo.ReadySCs,
+	}
+}
+
+// Picker implements the balancer.Picker interface. It picks a backend (SubConn)
+// based on the nonce prefix contained in each request's Context.
+type Picker struct {
+	backends        map[balancer.SubConn]base.SubConnInfo
+	prefixToBackend map[string]balancer.SubConn
+}
+
+// Compile-time assertion that *Picker implements the balancer.Picker interface.
+var _ balancer.Picker = (*Picker)(nil)
+
+// Pick implements the balancer.Picker interface. It is called by the gRPC
+// runtime for each RPC message. It is responsible for picking a backend
+// (SubConn) based on the context of each RPC message.
+func (p *Picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
+	if len(p.backends) == 0 {
+		// The Picker must be rebuilt if there are no backends available.
+		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
+	}
+
+	// Get the HMAC key from the RPC context.
+	hmacKeyVal := info.Ctx.Value(nonce.HMACKeyCtxKey{})
+	if hmacKeyVal == nil {
+		// This should never happen.
+		return balancer.PickResult{}, errMissingHMACKeyCtxKey
+	}
+	hmacKey, ok := hmacKeyVal.(string)
+	if !ok {
+		// This should never happen.
+		return balancer.PickResult{}, errInvalidHMACKeyCtxKeyType
+	}
+
+	if p.prefixToBackend == nil {
+		// Iterate over the backends and build a map of the derived prefix for
+		// each backend SubConn.
+		prefixToBackend := make(map[string]balancer.SubConn)
+		for sc, scInfo := range p.backends {
+			scPrefix := nonce.DerivePrefix(scInfo.Address.Addr, hmacKey)
+			prefixToBackend[scPrefix] = sc
+		}
+		p.prefixToBackend = prefixToBackend
+	}
+
+	// Get the destination prefix from the RPC context.
+	destPrefixVal := info.Ctx.Value(nonce.PrefixCtxKey{})
+	if destPrefixVal == nil {
+		// This should never happen.
+		return balancer.PickResult{}, errMissingPrefixCtxKey
+	}
+	destPrefix, ok := destPrefixVal.(string)
+	if !ok {
+		// This should never happen.
+		return balancer.PickResult{}, errInvalidPrefixCtxKeyType
+	}
+
+	sc, ok := p.prefixToBackend[destPrefix]
+	if !ok {
+		// No backend SubConn was found for the destination prefix.
+		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
+	}
+	return balancer.PickResult{SubConn: sc}, nil
+}
+
+func init() {
+	balancer.Register(
+		base.NewBalancerBuilder(Name, &Balancer{}, base.Config{}),
+	)
+}
diff --git a/log/log.go b/log/log.go
index dae33eff4..023684376 100644
--- a/log/log.go
+++ b/log/log.go
@@ -30,6 +30,7 @@ type Logger interface {
 	Warningf(format string, a ...interface{})
 	Info(msg string)
 	Infof(format string, a ...interface{})
+	InfoObject(string, interface{})
 	Debug(msg string)
 	Debugf(format string, a ...interface{})
 	AuditPanic()
@@ -315,6 +316,17 @@ func (log *impl) Infof(format string, a ...interface{}) {
 	log.Info(fmt.Sprintf(format, a...))
 }
 
+// InfoObject logs an INFO level JSON-serialized object message.
+func (log *impl) InfoObject(msg string, obj interface{}) {
+	jsonObj, err := json.Marshal(obj)
+	if err != nil {
+		log.auditAtLevel(syslog.LOG_ERR, fmt.Sprintf("Object for msg %q could not be serialized to JSON. Raw: %+v", msg, obj))
+		return
+	}
+
+	log.Infof("%s JSON=%s", msg, jsonObj)
+}
+
 // Debug level messages pass through normally.
 func (log *impl) Debug(msg string) {
 	log.w.logAtLevel(syslog.LOG_DEBUG, msg)
@@ -342,7 +354,7 @@ func (log *impl) AuditInfof(format string, a ...interface{}) {
 func (log *impl) AuditObject(msg string, obj interface{}) {
 	jsonObj, err := json.Marshal(obj)
 	if err != nil {
-		log.auditAtLevel(syslog.LOG_ERR, fmt.Sprintf("Object could not be serialized to JSON. Raw: %+v", obj))
+		log.auditAtLevel(syslog.LOG_ERR, fmt.Sprintf("Object for msg %q could not be serialized to JSON. Raw: %+v", msg, obj))
 		return
 	}
 
diff --git a/nonce/nonce.go b/nonce/nonce.go
index f8f1e555f..2f3a210fd 100644
--- a/nonce/nonce.go
+++ b/nonce/nonce.go
@@ -18,7 +18,9 @@ import (
 	"context"
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/hmac"
 	"crypto/rand"
+	"crypto/sha256"
 	"encoding/base64"
 	"errors"
 	"fmt"
@@ -28,16 +30,39 @@ import (
 
 	noncepb "github.com/letsencrypt/boulder/nonce/proto"
 	"github.com/prometheus/client_golang/prometheus"
+	"google.golang.org/grpc"
 	"google.golang.org/protobuf/types/known/emptypb"
 )
 
 const (
-	defaultMaxUsed = 65536
-	nonceLen       = 32
+	// PrefixLen is the character length of a nonce prefix.
+	PrefixLen = 8
+	// DeprecatedPrefixLen is the character length of a nonce prefix.
+	//
+	// DEPRECATED: Use PrefixLen instead.
+	// TODO(#6610): Remove once we've moved to derivable prefixes by default.
+	DeprecatedPrefixLen = 4
+	defaultMaxUsed      = 65536
+	nonceLen            = 32
 )
 
 var errInvalidNonceLength = errors.New("invalid nonce length")
 
+// PrefixCtxKey is exported for use as a key in a context.Context.
+type PrefixCtxKey struct{}
+
+// HMACKeyCtxKey is exported for use as a key in a context.Context.
+type HMACKeyCtxKey struct{}
+
+// DerivePrefix derives a nonce prefix from the provided listening address and
+// key. The prefix is derived by take the first 8 characters of the base64url
+// encoded HMAC-SHA256 hash of the listening address using the provided key.
+func DerivePrefix(grpcAddr, key string) string {
+	h := hmac.New(sha256.New, []byte(key))
+	h.Write([]byte(grpcAddr))
+	return base64.RawURLEncoding.EncodeToString(h.Sum(nil))[:PrefixLen]
+}
+
 // NonceService generates, cancels, and tracks Nonces.
 type NonceService struct {
 	mu               sync.Mutex
@@ -51,6 +76,9 @@ type NonceService struct {
 	nonceCreates     prometheus.Counter
 	nonceRedeems     *prometheus.CounterVec
 	nonceHeapLatency prometheus.Histogram
+	// TODO(#6610): Remove this field once we've moved to derivable prefixes by
+	// default.
+	prefixLen int
 }
 
 type int64Heap []int64
@@ -73,15 +101,25 @@ func (h *int64Heap) Pop() interface{} {
 
 // NewNonceService constructs a NonceService with defaults
 func NewNonceService(stats prometheus.Registerer, maxUsed int, prefix string) (*NonceService, error) {
-	// If a prefix is provided it must be four characters and valid
-	// base64. The prefix is required to be base64url as RFC8555
-	// section 6.5.1 requires that nonces use that encoding.
-	// As base64 operates on three byte binary segments we require
-	// the prefix to be three bytes (four characters) so that the
-	// bytes preceding the prefix wouldn't impact the encoding.
+	// If a prefix is provided it must be four characters and valid base64. The
+	// prefix is required to be base64url as RFC8555 section 6.5.1 requires that
+	// nonces use that encoding. As base64 operates on three byte binary
+	// segments we require the prefix to be three or six bytes (four or eight
+	// characters) so that the bytes preceding the prefix wouldn't impact the
+	// encoding.
+	//
+	// TODO(#6610): Update this comment once we've moved to eight character
+	// prefixes by default.
 	if prefix != "" {
-		if len(prefix) != 4 {
-			return nil, errors.New("nonce prefix must be 4 characters")
+		// TODO(#6610): Refactor once we've moved to derivable prefixes by
+		// default.
+		if len(prefix) != PrefixLen && len(prefix) != DeprecatedPrefixLen {
+			return nil, fmt.Errorf(
+				"'noncePrefix' must be %d or %d characters, not %d",
+				PrefixLen,
+				DeprecatedPrefixLen,
+				len(prefix),
+			)
 		}
 		if _, err := base64.RawURLEncoding.DecodeString(prefix); err != nil {
 			return nil, errors.New("nonce prefix must be valid base64url")
@@ -133,6 +171,9 @@ func NewNonceService(stats prometheus.Registerer, maxUsed int, prefix string) (*
 		nonceCreates:     nonceCreates,
 		nonceRedeems:     nonceRedeems,
 		nonceHeapLatency: nonceHeapLatency,
+		// TODO(#6610): Remove this field once we've moved to derivable prefixes
+		// by default.
+		prefixLen: len(prefix),
 	}, nil
 }
 
@@ -142,7 +183,8 @@ func (ns *NonceService) encrypt(counter int64) (string, error) {
 	for i := 0; i < 4; i++ {
 		nonce[i] = 0
 	}
-	if _, err := rand.Read(nonce[4:]); err != nil {
+	_, err := rand.Read(nonce[4:])
+	if err != nil {
 		return "", err
 	}
 
@@ -166,7 +208,7 @@ func (ns *NonceService) decrypt(nonce string) (int64, error) {
 	if ns.prefix != "" {
 		var prefix string
 		var err error
-		prefix, body, err = splitNonce(nonce)
+		prefix, body, err = ns.splitNonce(nonce)
 		if err != nil {
 			return 0, err
 		}
@@ -247,17 +289,33 @@ func (ns *NonceService) Valid(nonce string) bool {
 	return true
 }
 
-func splitNonce(nonce string) (string, string, error) {
-	if len(nonce) < 4 {
+// splitNonce splits a nonce into a prefix and a body.
+func (ns *NonceService) splitNonce(nonce string) (string, string, error) {
+	if len(nonce) < ns.prefixLen {
+		return "", "", errInvalidNonceLength
+	}
+	return nonce[:ns.prefixLen], nonce[ns.prefixLen:], nil
+}
+
+// splitDeprecatedNonce splits a nonce into a prefix and a body.
+//
+// Deprecated: Use NonceService.splitDeprecatedNonce instead.
+// TODO(#6610): Remove this function once we've moved to derivable prefixes by
+// default.
+func splitDeprecatedNonce(nonce string) (string, string, error) {
+	if len(nonce) < DeprecatedPrefixLen {
 		return "", "", errInvalidNonceLength
 	}
-	return nonce[:4], nonce[4:], nil
+	return nonce[:DeprecatedPrefixLen], nonce[DeprecatedPrefixLen:], nil
 }
 
 // RemoteRedeem checks the nonce prefix and routes the Redeem RPC
-// to the associated remote nonce service
-func RemoteRedeem(ctx context.Context, noncePrefixMap map[string]noncepb.NonceServiceClient, nonce string) (bool, error) {
-	prefix, _, err := splitNonce(nonce)
+// to the associated remote nonce service.
+//
+// TODO(#6610): Remove this function once we've moved to derivable prefixes by
+// default.
+func RemoteRedeem(ctx context.Context, noncePrefixMap map[string]Redeemer, nonce string) (bool, error) {
+	prefix, _, err := splitDeprecatedNonce(nonce)
 	if err != nil {
 		return false, nil
 	}
@@ -296,3 +354,25 @@ func (ns *Server) Nonce(_ context.Context, _ *emptypb.Empty) (*noncepb.NonceMess
 	}
 	return &noncepb.NonceMessage{Nonce: nonce}, nil
 }
+
+// Getter is an interface for an RPC client that can get a nonce.
+type Getter interface {
+	Nonce(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*noncepb.NonceMessage, error)
+}
+
+// Getter is an interface for an RPC client that can redeem a nonce.
+type Redeemer interface {
+	Redeem(ctx context.Context, in *noncepb.NonceMessage, opts ...grpc.CallOption) (*noncepb.ValidMessage, error)
+}
+
+// NewGetter returns a new noncepb.NonceServiceClient which can only be used to
+// get nonces.
+func NewGetter(cc grpc.ClientConnInterface) Getter {
+	return noncepb.NewNonceServiceClient(cc)
+}
+
+// NewRedeemer returns a new noncepb.NonceServiceClient which can only be used
+// to redeem nonces.
+func NewRedeemer(cc grpc.ClientConnInterface) Redeemer {
+	return noncepb.NewNonceServiceClient(cc)
+}
diff --git a/ocsp/responder/redis/checked_redis_source.go b/ocsp/responder/redis/checked_redis_source.go
index 50dda22ee..e1d1b62aa 100644
--- a/ocsp/responder/redis/checked_redis_source.go
+++ b/ocsp/responder/redis/checked_redis_source.go
@@ -12,6 +12,7 @@ import (
 
 	"github.com/letsencrypt/boulder/core"
 	"github.com/letsencrypt/boulder/db"
+	berrors "github.com/letsencrypt/boulder/errors"
 	blog "github.com/letsencrypt/boulder/log"
 	"github.com/letsencrypt/boulder/ocsp/responder"
 	"github.com/letsencrypt/boulder/sa"
@@ -113,7 +114,7 @@ func (src *checkedRedisSource) Response(ctx context.Context, req *ocsp.Request)
 	if dbErr != nil {
 		// If the DB says "not found", the certificate either doesn't exist or has
 		// expired and been removed from the DB. We don't need to check the Redis error.
-		if db.IsNoRows(dbErr) {
+		if db.IsNoRows(dbErr) || errors.Is(dbErr, berrors.NotFound) {
 			src.counter.WithLabelValues("not_found").Inc()
 			return nil, responder.ErrNotFound
 		}
diff --git a/ocsp/responder/redis/redis_source.go b/ocsp/responder/redis/redis_source.go
index 9b3018e97..c6c47de94 100644
--- a/ocsp/responder/redis/redis_source.go
+++ b/ocsp/responder/redis/redis_source.go
@@ -49,7 +49,7 @@ type redisSource struct {
 // NewRedisSource returns a responder.Source which will look up OCSP responses in a
 // Redis table.
 func NewRedisSource(
-	client rocsp.Writer,
+	client *rocsp.RWClient,
 	signer responder.Source,
 	liveSigningPeriod time.Duration,
 	clk clock.Clock,
diff --git a/ra/ra.go b/ra/ra.go
index a749b6f05..b55bcd92b 100644
--- a/ra/ra.go
+++ b/ra/ra.go
@@ -54,6 +54,12 @@ import (
 var (
 	errIncompleteGRPCRequest  = errors.New("incomplete gRPC request message")
 	errIncompleteGRPCResponse = errors.New("incomplete gRPC response message")
+
+	// caaRecheckDuration is the amount of time after a CAA check that we will
+	// recheck the CAA records for a domain. Per Baseline Requirements, we must
+	// recheck CAA records within 8 hours of issuance. We set this to 7 hours to
+	// stay on the safe side.
+	caaRecheckDuration = -7 * time.Hour
 )
 
 type caaChecker interface {
@@ -231,25 +237,26 @@ func NewRegistrationAuthorityImpl(
 		maxContactsPerReg:            maxContactsPerReg,
 		keyPolicy:                    keyPolicy,
 		maxNames:                     maxNames,
-		reuseValidAuthz:              reuseValidAuthz,
-		publisher:                    pubc,
-		caa:                          caaClient,
-		orderLifetime:                orderLifetime,
-		ctpolicy:                     ctp,
-		ctpolicyResults:              ctpolicyResults,
-		purger:                       purger,
-		issuersByNameID:              issuersByNameID,
-		issuersByID:                  issuersByID,
-		namesPerCert:                 namesPerCert,
-		rateLimitCounter:             rateLimitCounter,
-		newRegCounter:                newRegCounter,
-		reusedValidAuthzCounter:      reusedValidAuthzCounter,
-		recheckCAACounter:            recheckCAACounter,
-		newCertCounter:               newCertCounter,
-		revocationReasonCounter:      revocationReasonCounter,
-		recheckCAAUsedAuthzLifetime:  recheckCAAUsedAuthzLifetime,
-		authzAges:                    authzAges,
-		orderAges:                    orderAges,
+		// TODO(#2734): Remove reuseValidAuthz hardcoding.
+		reuseValidAuthz:             true,
+		publisher:                   pubc,
+		caa:                         caaClient,
+		orderLifetime:               orderLifetime,
+		ctpolicy:                    ctp,
+		ctpolicyResults:             ctpolicyResults,
+		purger:                      purger,
+		issuersByNameID:             issuersByNameID,
+		issuersByID:                 issuersByID,
+		namesPerCert:                namesPerCert,
+		rateLimitCounter:            rateLimitCounter,
+		newRegCounter:               newRegCounter,
+		reusedValidAuthzCounter:     reusedValidAuthzCounter,
+		recheckCAACounter:           recheckCAACounter,
+		newCertCounter:              newCertCounter,
+		revocationReasonCounter:     revocationReasonCounter,
+		recheckCAAUsedAuthzLifetime: recheckCAAUsedAuthzLifetime,
+		authzAges:                   authzAges,
+		orderAges:                   orderAges,
 	}
 	return ra
 }
@@ -328,6 +335,20 @@ type certificateRevocationEvent struct {
 	Error string `json:",omitempty"`
 }
 
+// finalizationCAACheckEvent is a struct for holding information logged as JSON
+// to the info log as the result of an issuance event. It is logged when the RA
+// performs the final CAA check of a certificate finalization request.
+type finalizationCAACheckEvent struct {
+	// Requester is the associated account ID.
+	Requester int64 `json:",omitempty"`
+	// Reused is a count of Authz where the original CAA check was performed in
+	// the last 7 hours.
+	Reused int `json:",omitempty"`
+	// Rechecked is a count of Authz where a new CAA check was performed because
+	// the original check was older than 7 hours.
+	Rechecked int `json:",omitempty"`
+}
+
 // noRegistrationID is used for the regID parameter to GetThreshold when no
 // registration-based overrides are necessary.
 const noRegistrationID = -1
@@ -532,16 +553,19 @@ func (ra *RegistrationAuthorityImpl) validateContacts(contacts []string) error {
 func (ra *RegistrationAuthorityImpl) checkPendingAuthorizationLimit(ctx context.Context, regID int64) error {
 	limit := ra.rlPolicies.PendingAuthorizationsPerAccount()
 	if limit.Enabled() {
+		// This rate limit's threshold can only be overridden on a per-regID basis,
+		// not based on any other key.
+		threshold := limit.GetThreshold("", regID)
+		if threshold == -1 {
+			return nil
+		}
 		countPB, err := ra.SA.CountPendingAuthorizations2(ctx, &sapb.RegistrationID{
 			Id: regID,
 		})
 		if err != nil {
 			return err
 		}
-		// Most rate limits have a key for overrides, but there is no meaningful key
-		// here.
-		noKey := ""
-		if countPB.Count >= limit.GetThreshold(noKey, regID) {
+		if countPB.Count >= threshold {
 			ra.rateLimitCounter.WithLabelValues("pending_authorizations_by_registration_id", "exceeded").Inc()
 			ra.log.Infof("Rate limit exceeded, PendingAuthorizationsByRegID, regID: %d", regID)
 			return berrors.RateLimitError(0, "too many currently pending authorizations: %d", countPB.Count)
@@ -718,7 +742,8 @@ func (ra *RegistrationAuthorityImpl) checkOrderAuthorizations(
 	names = core.UniqueLowerNames(names)
 
 	// Check the authorizations to ensure validity for the names required.
-	if err = ra.checkAuthorizationsCAA(ctx, names, authzs, ra.clk.Now()); err != nil {
+	err = ra.checkAuthorizationsCAA(ctx, int64(acctID), names, authzs, ra.clk.Now())
+	if err != nil {
 		return nil, err
 	}
 
@@ -753,6 +778,7 @@ func validatedBefore(authz *core.Authorization, caaRecheckTime time.Time) (bool,
 // If it returns an error, it will be of type BoulderError.
 func (ra *RegistrationAuthorityImpl) checkAuthorizationsCAA(
 	ctx context.Context,
+	acctID int64,
 	names []string,
 	authzs map[string]*core.Authorization,
 	now time.Time) error {
@@ -768,14 +794,14 @@ func (ra *RegistrationAuthorityImpl) checkAuthorizationsCAA(
 	// check to see if the authorized challenge `AttemptedAt`
 	// (`Validated`) value from the database is before our caaRecheckTime.
 	// Set the recheck time to 7 hours ago.
-	caaRecheckAfter := now.Add(-7 * time.Hour)
+	caaRecheckAfter := now.Add(caaRecheckDuration)
 
 	// Set a CAA recheck time based on the assumption of a 30 day authz
 	// lifetime. This has been deprecated in favor of a new check based
 	// off the Validated time stored in the database, but we want to check
 	// both for a time and increment a stat if this code path is hit for
 	// compliance safety.
-	caaRecheckTime := now.Add(ra.authorizationLifetime).Add(-7 * time.Hour)
+	caaRecheckTime := now.Add(ra.authorizationLifetime).Add(caaRecheckDuration)
 
 	for _, name := range names {
 		authz := authzs[name]
@@ -814,6 +840,13 @@ func (ra *RegistrationAuthorityImpl) checkAuthorizationsCAA(
 		)
 	}
 
+	caaEvent := &finalizationCAACheckEvent{
+		Requester: acctID,
+		Reused:    len(authzs) - len(recheckAuthzs),
+		Rechecked: len(recheckAuthzs),
+	}
+	ra.log.InfoObject("FinalizationCaaCheck", caaEvent)
+
 	return nil
 }
 
@@ -1743,8 +1776,8 @@ func (ra *RegistrationAuthorityImpl) PerformValidation(
 
 		err = ra.recordValidation(vaCtx, authz.ID, authz.Expires, challenge)
 		if err != nil {
-			ra.log.AuditErrf("Could not record updated validation: err=[%s] regID=[%d] authzID=[%s]",
-				err, authz.RegistrationID, authz.ID)
+			ra.log.AuditErrf("Could not record updated validation: regID=[%d] authzID=[%s] err=[%s]",
+				authz.RegistrationID, authz.ID, err)
 		}
 	}(authz)
 	return bgrpc.AuthzToPB(authz)
diff --git a/ratelimit/rate-limits.go b/ratelimit/rate-limits.go
index 822f820cc..c1f084730 100644
--- a/ratelimit/rate-limits.go
+++ b/ratelimit/rate-limits.go
@@ -185,7 +185,8 @@ type RateLimitPolicy struct {
 	// For instance, a rate limit on the number of certificates per name uses name as
 	// a key, while a rate limit on the number of registrations per IP subnet would
 	// use subnet as a key. Note that a zero entry in the overrides map does not
-	// mean "no limit," it means a limit of zero.
+	// mean "no limit," it means a limit of zero. An entry of -1 means
+	// "no limit", only for the pending authorizations rate limit.
 	Overrides map[string]int64 `yaml:"overrides"`
 	// A per-registration override setting. This can be used, e.g. if there are
 	// hosting providers that we would like to grant a higher rate of issuance
diff --git a/rocsp/config/rocsp_config.go b/rocsp/config/rocsp_config.go
index ee8c81cf9..021885803 100644
--- a/rocsp/config/rocsp_config.go
+++ b/rocsp/config/rocsp_config.go
@@ -25,12 +25,6 @@ type RedisConfig struct {
 	TLS cmd.TLSConfig
 	// Username is a Redis username.
 	Username string
-	// Addrs is a list of IP address:port pairs. The go-redis `ClusterClient`
-	// will use this list to discover a cluster of Redis servers configured in
-	// Cluster mode.
-	//
-	// DEPRECATED: Use `ShardAddrs` instead. TODO(#6517) remove `Addrs`.
-	Addrs []string
 	// ShardAddrs is a map of shard names to IP address:port pairs. The go-redis
 	// `Ring` client will shard reads and writes across the provided Redis
 	// Servers based on a consistent hashing algorithm.
@@ -100,7 +94,7 @@ type RedisConfig struct {
 }
 
 // MakeClient produces a read-write ROCSP client from a config.
-func MakeClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (rocsp.Writer, error) {
+func MakeClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (*rocsp.RWClient, error) {
 	password, err := c.PasswordConfig.Pass()
 	if err != nil {
 		return nil, fmt.Errorf("loading password: %w", err)
@@ -111,38 +105,6 @@ func MakeClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (r
 		return nil, fmt.Errorf("loading TLS config: %w", err)
 	}
 
-	timeout := c.Timeout.Duration
-
-	// TODO(#6517) remove this block.
-	if len(c.Addrs) > 0 && len(c.ShardAddrs) > 0 {
-		return nil, errors.New("both 'addrs' and 'shardAddrs' were provided, only one is allowed")
-	}
-
-	// TODO(#6517) remove this block.
-	if len(c.Addrs) > 0 {
-		rdb := redis.NewClusterClient(&redis.ClusterOptions{
-			Addrs:     c.Addrs,
-			Username:  c.Username,
-			Password:  password,
-			TLSConfig: tlsConfig,
-
-			MaxRetries:      c.MaxRetries,
-			MinRetryBackoff: c.MinRetryBackoff.Duration,
-			MaxRetryBackoff: c.MaxRetryBackoff.Duration,
-			DialTimeout:     c.DialTimeout.Duration,
-			ReadTimeout:     c.ReadTimeout.Duration,
-			WriteTimeout:    c.WriteTimeout.Duration,
-
-			PoolSize:           c.PoolSize,
-			MinIdleConns:       c.MinIdleConns,
-			MaxConnAge:         c.MaxConnAge.Duration,
-			PoolTimeout:        c.PoolTimeout.Duration,
-			IdleTimeout:        c.IdleTimeout.Duration,
-			IdleCheckFrequency: c.IdleCheckFrequency.Duration,
-		})
-		return rocsp.NewClusterWritingClient(rdb, timeout, clk, stats), nil
-	}
-
 	rdb := redis.NewRing(&redis.RingOptions{
 		Addrs:     c.ShardAddrs,
 		Username:  c.Username,
@@ -163,13 +125,13 @@ func MakeClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (r
 		IdleTimeout:        c.IdleTimeout.Duration,
 		IdleCheckFrequency: c.IdleCheckFrequency.Duration,
 	})
-	return rocsp.NewWritingClient(rdb, timeout, clk, stats), nil
+	return rocsp.NewWritingClient(rdb, c.Timeout.Duration, clk, stats), nil
 }
 
 // MakeReadClient produces a read-only ROCSP client from a config.
-func MakeReadClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (rocsp.Reader, error) {
-	if len(c.Addrs) == 0 {
-		return nil, errors.New("redis config's 'addrs' field was empty")
+func MakeReadClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer) (*rocsp.ROClient, error) {
+	if len(c.ShardAddrs) == 0 {
+		return nil, errors.New("redis config's 'shardAddrs' field was empty")
 	}
 
 	password, err := c.PasswordConfig.Pass()
@@ -182,42 +144,6 @@ func MakeReadClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer
 		return nil, fmt.Errorf("loading TLS config: %w", err)
 	}
 
-	timeout := c.Timeout.Duration
-
-	// TODO(#6517) remove this block.
-	if len(c.Addrs) > 0 && len(c.ShardAddrs) > 0 {
-		return nil, errors.New("both 'addrs' and 'shardAddrs' were provided, only one is allowed")
-	}
-
-	// TODO(#6517) remove this block.
-	if len(c.Addrs) > 0 {
-		rdb := redis.NewClusterClient(&redis.ClusterOptions{
-			Addrs:     c.Addrs,
-			Username:  c.Username,
-			Password:  password,
-			TLSConfig: tlsConfig,
-
-			ReadOnly:       c.ReadOnly,
-			RouteByLatency: c.RouteByLatency,
-			RouteRandomly:  c.RouteRandomly,
-
-			PoolFIFO: c.PoolFIFO,
-
-			MaxRetries:      c.MaxRetries,
-			MinRetryBackoff: c.MinRetryBackoff.Duration,
-			MaxRetryBackoff: c.MaxRetryBackoff.Duration,
-			DialTimeout:     c.DialTimeout.Duration,
-			ReadTimeout:     c.ReadTimeout.Duration,
-
-			PoolSize:           c.PoolSize,
-			MinIdleConns:       c.MinIdleConns,
-			MaxConnAge:         c.MaxConnAge.Duration,
-			PoolTimeout:        c.PoolTimeout.Duration,
-			IdleTimeout:        c.IdleTimeout.Duration,
-			IdleCheckFrequency: c.IdleCheckFrequency.Duration,
-		})
-		return rocsp.NewClusterReadingClient(rdb, timeout, clk, stats), nil
-	}
 	rdb := redis.NewRing(&redis.RingOptions{
 		Addrs:     c.ShardAddrs,
 		Username:  c.Username,
@@ -239,7 +165,7 @@ func MakeReadClient(c *RedisConfig, clk clock.Clock, stats prometheus.Registerer
 		IdleTimeout:        c.IdleTimeout.Duration,
 		IdleCheckFrequency: c.IdleCheckFrequency.Duration,
 	})
-	return rocsp.NewReadingClient(rdb, timeout, clk, stats), nil
+	return rocsp.NewReadingClient(rdb, c.Timeout.Duration, clk, stats), nil
 }
 
 // A ShortIDIssuer combines an issuance.Certificate with some fields necessary
diff --git a/rocsp/rocspcluster.go b/rocsp/rocspcluster.go
deleted file mode 100644
index 030b7bbbd..000000000
--- a/rocsp/rocspcluster.go
+++ /dev/null
@@ -1,188 +0,0 @@
-package rocsp
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/go-redis/redis/v8"
-	"github.com/jmhodges/clock"
-	"github.com/letsencrypt/boulder/core"
-	"github.com/prometheus/client_golang/prometheus"
-	"golang.org/x/crypto/ocsp"
-)
-
-// TODO(#6517) remove this file and replace the Reader and Writer interfaces
-// with the structs in rocsp.go.
-
-// Reader is an interface for read-only Redis clients. It's implemented by
-// CROClient and ROClient.
-type Reader interface {
-	GetResponse(context.Context, string) ([]byte, error)
-	Ping(context.Context) error
-	ScanResponses(context.Context, string) <-chan ScanResponsesResult
-}
-
-// CROClient represents a read-only Redis client.
-type CROClient struct {
-	rdb        *redis.ClusterClient
-	timeout    time.Duration
-	clk        clock.Clock
-	getLatency *prometheus.HistogramVec
-}
-
-// NewClusterReadingClient creates a read-only client for use with a Redis Cluster. The
-// timeout applies to all requests, though a shorter timeout can be applied on a
-// per-request basis using context.Context. rdb.Options().Addrs must have at
-// least one entry.
-func NewClusterReadingClient(rdb *redis.ClusterClient, timeout time.Duration, clk clock.Clock, stats prometheus.Registerer) *CROClient {
-	if len(rdb.Options().Addrs) == 0 {
-		return nil
-	}
-	labels := prometheus.Labels{
-		"addresses": strings.Join(rdb.Options().Addrs, ", "),
-		"user":      rdb.Options().Username,
-	}
-	stats.MustRegister(newMetricsCollector(rdb, labels))
-	getLatency := prometheus.NewHistogramVec(
-		prometheus.HistogramOpts{
-			Name: "rocsp_get_latency",
-			Help: "Histogram of latencies of rocsp.GetResponse calls with result",
-			// 8 buckets, ranging from 0.5ms to 2s
-			Buckets: prometheus.ExponentialBucketsRange(0.0005, 2, 8),
-		},
-		[]string{"result"},
-	)
-	stats.MustRegister(getLatency)
-
-	return &CROClient{
-		rdb:        rdb,
-		timeout:    timeout,
-		clk:        clk,
-		getLatency: getLatency,
-	}
-}
-
-func (c *CROClient) Ping(ctx context.Context) error {
-	ctx, cancel := context.WithTimeout(ctx, c.timeout)
-	defer cancel()
-	return c.rdb.Ping(ctx).Err()
-}
-
-// Writer is an interface for read-only Redis clients. It's implemented by
-// CRWClient and RWClient.
-type Writer interface {
-	StoreResponse(context.Context, *ocsp.Response) error
-	GetResponse(context.Context, string) ([]byte, error)
-	Ping(context.Context) error
-	ScanResponses(context.Context, string) <-chan ScanResponsesResult
-}
-
-// WritingClient represents a Redis client that can both read and write.
-type CRWClient struct {
-	*CROClient
-	storeResponseLatency *prometheus.HistogramVec
-}
-
-// NewWritingClient creates a WritingClient.
-func NewClusterWritingClient(rdb *redis.ClusterClient, timeout time.Duration, clk clock.Clock, stats prometheus.Registerer) *CRWClient {
-	storeResponseLatency := prometheus.NewHistogramVec(
-		prometheus.HistogramOpts{
-			Name: "rocsp_store_response_latency",
-			Help: "Histogram of latencies of rocsp.StoreResponse calls with result labels",
-		},
-		[]string{"result"},
-	)
-	stats.MustRegister(storeResponseLatency)
-	return &CRWClient{NewClusterReadingClient(rdb, timeout, clk, stats), storeResponseLatency}
-}
-
-// StoreResponse parses the given bytes as an OCSP response, and stores it
-// into Redis. The expiration time (ttl) of the Redis key is set to OCSP
-// response `NextUpdate`.
-func (c *CRWClient) StoreResponse(ctx context.Context, resp *ocsp.Response) error {
-	start := c.clk.Now()
-	ctx, cancel := context.WithTimeout(ctx, c.timeout)
-	defer cancel()
-
-	serial := core.SerialToString(resp.SerialNumber)
-
-	// Set the ttl duration to the response `NextUpdate - now()`
-	ttl := time.Until(resp.NextUpdate)
-
-	err := c.rdb.Set(ctx, serial, resp.Raw, ttl).Err()
-	if err != nil {
-		state := "failed"
-		if errors.Is(err, context.DeadlineExceeded) {
-			state = "deadlineExceeded"
-		} else if errors.Is(err, context.Canceled) {
-			state = "canceled"
-		}
-		c.storeResponseLatency.With(prometheus.Labels{"result": state}).Observe(time.Since(start).Seconds())
-		return fmt.Errorf("setting response: %w", err)
-	}
-
-	c.storeResponseLatency.With(prometheus.Labels{"result": "success"}).Observe(time.Since(start).Seconds())
-	return nil
-}
-
-// GetResponse fetches a response for the given serial number.
-// Returns error if the OCSP response fails to parse.
-func (c *CROClient) GetResponse(ctx context.Context, serial string) ([]byte, error) {
-	start := c.clk.Now()
-	ctx, cancel := context.WithTimeout(ctx, c.timeout)
-	defer cancel()
-
-	resp, err := c.rdb.Get(ctx, serial).Result()
-	if err != nil {
-		// go-redis `Get` returns redis.Nil error when key does not exist. In
-		// that case return a `ErrRedisNotFound` error.
-		if errors.Is(err, redis.Nil) {
-			c.getLatency.With(prometheus.Labels{"result": "notFound"}).Observe(time.Since(start).Seconds())
-			return nil, ErrRedisNotFound
-		}
-
-		state := "failed"
-		if errors.Is(err, context.DeadlineExceeded) {
-			state = "deadlineExceeded"
-		} else if errors.Is(err, context.Canceled) {
-			state = "canceled"
-		}
-		c.getLatency.With(prometheus.Labels{"result": state}).Observe(time.Since(start).Seconds())
-		return nil, fmt.Errorf("getting response: %w", err)
-	}
-
-	c.getLatency.With(prometheus.Labels{"result": "success"}).Observe(time.Since(start).Seconds())
-	return []byte(resp), nil
-}
-
-// ScanResponses scans Redis for all OCSP responses where the serial number matches the provided pattern.
-// It returns immediately and emits results and errors on `<-chan ScanResponsesResult`. It closes the
-// channel when it is done or hits an error.
-func (c *CROClient) ScanResponses(ctx context.Context, serialPattern string) <-chan ScanResponsesResult {
-	pattern := fmt.Sprintf("r{%s}", serialPattern)
-	results := make(chan ScanResponsesResult)
-	go func() {
-		defer close(results)
-		err := c.rdb.ForEachMaster(ctx, func(ctx context.Context, rdb *redis.Client) error {
-			iter := rdb.Scan(ctx, 0, pattern, 0).Iterator()
-			for iter.Next(ctx) {
-				key := iter.Val()
-				val, err := c.rdb.Get(ctx, key).Result()
-				if err != nil {
-					results <- ScanResponsesResult{Err: fmt.Errorf("getting response: %w", err)}
-					continue
-				}
-				results <- ScanResponsesResult{Serial: key, Body: []byte(val)}
-			}
-			return iter.Err()
-		})
-		if err != nil {
-			results <- ScanResponsesResult{Err: err}
-			return
-		}
-	}()
-	return results
-}
diff --git a/sa/db-users/boulder_sa.sql b/sa/db-users/boulder_sa.sql
index f19551006..08f7eb1a8 100644
--- a/sa/db-users/boulder_sa.sql
+++ b/sa/db-users/boulder_sa.sql
@@ -79,6 +79,7 @@ GRANT SELECT ON fqdnSets TO 'mailer'@'localhost';
 
 -- Cert checker
 GRANT SELECT ON certificates TO 'cert_checker'@'localhost';
+GRANT SELECT ON authz2 TO 'cert_checker'@'localhost';
 
 -- Bad Key Revoker
 GRANT SELECT,UPDATE ON blockedKeys TO 'badkeyrevoker'@'localhost';
diff --git a/sa/model.go b/sa/model.go
index 5dd77618a..ee873a2c5 100644
--- a/sa/model.go
+++ b/sa/model.go
@@ -21,6 +21,7 @@ import (
 	"github.com/letsencrypt/boulder/db"
 	berrors "github.com/letsencrypt/boulder/errors"
 	"github.com/letsencrypt/boulder/grpc"
+	"github.com/letsencrypt/boulder/identifier"
 	"github.com/letsencrypt/boulder/probs"
 	"github.com/letsencrypt/boulder/revocation"
 	sapb "github.com/letsencrypt/boulder/sa/proto"
@@ -445,6 +446,66 @@ type authzModel struct {
 	ValidationRecord []byte     `db:"validationRecord"`
 }
 
+// SelectAuthzsMatchingIssuance looks for a set of authzs that would have
+// authorized a given issuance that is known to have occurred. The returned
+// authzs will all belong to the given regID, will have potentially been valid
+// at the time of issuance, and will have the appropriate identifier type and
+// value. This may return multiple authzs for the same identifier type and value.
+//
+// This returns "potentially" valid authzs because a client may have set an
+// authzs status to deactivated after issuance, so we return both valid and
+// deactivated authzs. It also uses a small amount of leeway (1s) to account
+// for possible clock skew.
+//
+// This function doesn't do anything special for authzs with an expiration in
+// the past. If the stored authz has a valid status, it is returned with a
+// valid status regardless of whether it is also expired.
+func SelectAuthzsMatchingIssuance(
+	s db.Selector,
+	regID int64,
+	issued time.Time,
+	dnsNames []string,
+) ([]*corepb.Authorization, error) {
+	query := fmt.Sprintf(`SELECT %s FROM authz2 WHERE
+			registrationID = ? AND
+			status IN (?, ?) AND
+			expires >= ? AND
+			attemptedAt <= ? AND
+			identifierType = ? AND
+			identifierValue IN (%s)`,
+		authzFields,
+		db.QuestionMarks(len(dnsNames)))
+	var args []any
+	args = append(args,
+		regID,
+		statusToUint[core.StatusValid],
+		statusToUint[core.StatusDeactivated],
+		issued.Add(-1*time.Second), // leeway for clock skew
+		issued.Add(1*time.Second),  // leeway for clock skew
+		identifierTypeToUint[string(identifier.DNS)],
+	)
+	for _, name := range dnsNames {
+		args = append(args, name)
+	}
+
+	var authzModels []authzModel
+	_, err := s.Select(&authzModels, query, args...)
+	if err != nil {
+		return nil, err
+	}
+
+	var authzs []*corepb.Authorization
+	for _, model := range authzModels {
+		authz, err := modelToAuthzPB(model)
+		if err != nil {
+			return nil, err
+		}
+		authzs = append(authzs, authz)
+
+	}
+	return authzs, err
+}
+
 // hasMultipleNonPendingChallenges checks if a slice of challenges contains
 // more than one non-pending challenge
 func hasMultipleNonPendingChallenges(challenges []*corepb.Challenge) bool {
diff --git a/sa/sa.go b/sa/sa.go
index 8930093b4..2754787a3 100644
--- a/sa/sa.go
+++ b/sa/sa.go
@@ -312,6 +312,10 @@ func (ssa *SQLStorageAuthority) DeactivateAuthorization2(ctx context.Context, re
 // authorizations are created, but then their corresponding order is never
 // created, leading to "invisible" pending authorizations.
 func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb.NewOrderAndAuthzsRequest) (*corepb.Order, error) {
+	if req.NewOrder == nil {
+		return nil, errIncompleteRequest
+	}
+
 	output, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
 		// First, insert all of the new authorizations and record their IDs.
 		newAuthzIDs := make([]int64, 0)
@@ -333,14 +337,14 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
 					am.IdentifierType,
 					am.IdentifierValue,
 					am.RegistrationID,
-					am.Status,
+					statusToUint[core.StatusPending],
 					am.Expires,
 					am.Challenges,
-					am.Attempted,
-					am.AttemptedAt,
+					nil,
+					nil,
 					am.Token,
-					am.ValidationError,
-					am.ValidationRecord,
+					nil,
+					nil,
 				})
 				if err != nil {
 					return nil, err
diff --git a/tools/fetch-and-verify-go.sh b/tools/fetch-and-verify-go.sh
index b58477fe7..838a55ca7 100755
--- a/tools/fetch-and-verify-go.sh
+++ b/tools/fetch-and-verify-go.sh
@@ -227,8 +227,8 @@ EOF
 OUTPUT_DIR="$(mktemp -d)"
 FILENAME="go${VERSION}.${PLATFORM}.tar.gz"
 OUTPUT_FILE="${OUTPUT_DIR}/${FILENAME}"
-curl -sSL "https://dl.google.com/go/${FILENAME}.asc" -o "${OUTPUT_FILE}.asc"
-curl -sSL "https://dl.google.com/go/${FILENAME}" -o "${OUTPUT_FILE}"
+curl -fsSL "https://dl.google.com/go/${FILENAME}.asc" -o "${OUTPUT_FILE}.asc"
+curl -fsSL "https://dl.google.com/go/${FILENAME}" -o "${OUTPUT_FILE}"
 
 gpg2 --verify --trust-model=always "${OUTPUT_FILE}.asc" "${OUTPUT_FILE}"
 
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/CHANGELOG.md
index 197c118e2..d0580cafd 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/CHANGELOG.md
@@ -1,3 +1,1137 @@
+# Release (2023-02-03)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/autoscaling`: [v1.26.2](service/autoscaling/CHANGELOG.md#v1262-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/cloudformation`: [v1.26.2](service/cloudformation/CHANGELOG.md#v1262-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/cloudsearch`: [v1.14.1](service/cloudsearch/CHANGELOG.md#v1141-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatch`: [v1.25.2](service/cloudwatch/CHANGELOG.md#v1252-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/docdb`: [v1.20.2](service/docdb/CHANGELOG.md#v1202-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.84.1](service/ec2/CHANGELOG.md#v1841-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/elasticache`: [v1.26.2](service/elasticache/CHANGELOG.md#v1262-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk`: [v1.15.1](service/elasticbeanstalk/CHANGELOG.md#v1151-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing`: [v1.15.2](service/elasticloadbalancing/CHANGELOG.md#v1152-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2`: [v1.19.3](service/elasticloadbalancingv2/CHANGELOG.md#v1193-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/iam`: [v1.19.2](service/iam/CHANGELOG.md#v1192-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/neptune`: [v1.19.2](service/neptune/CHANGELOG.md#v1192-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/proton`: [v1.20.0](service/proton/CHANGELOG.md#v1200-2023-02-03)
+  * **Feature**: Add new GetResourcesSummary API
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.40.2](service/rds/CHANGELOG.md#v1402-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/redshift`: [v1.27.2](service/redshift/CHANGELOG.md#v1272-2023-02-03)
+  * **Documentation**: Corrects descriptions of the parameters for the API operations RestoreFromClusterSnapshot, RestoreTableFromClusterSnapshot, and CreateCluster.
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/ses`: [v1.15.1](service/ses/CHANGELOG.md#v1151-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/sns`: [v1.20.1](service/sns/CHANGELOG.md#v1201-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/sqs`: [v1.20.2](service/sqs/CHANGELOG.md#v1202-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+* `github.com/aws/aws-sdk-go-v2/service/sts`: [v1.18.3](service/sts/CHANGELOG.md#v1183-2023-02-03)
+  * **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization.
+
+# Release (2023-02-02)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/appconfig`: [v1.16.0](service/appconfig/CHANGELOG.md#v1160-2023-02-02)
+  * **Feature**: AWS AppConfig introduces KMS customer-managed key (CMK) encryption of configuration data, along with AWS Secrets Manager as a new configuration data source. S3 objects using SSE-KMS encryption and SSM Parameter Store SecureStrings are also now supported.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.84.0](service/ec2/CHANGELOG.md#v1840-2023-02-02)
+  * **Feature**: Documentation updates for EC2.
+* `github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2`: [v1.19.2](service/elasticloadbalancingv2/CHANGELOG.md#v1192-2023-02-02)
+  * **Documentation**: The GWLB Flex Health Check project updates the default values of healthy-threshold-count from 3 to 5 and unhealthy-threshold-count from 3 to 2
+* `github.com/aws/aws-sdk-go-v2/service/quicksight`: [v1.31.0](service/quicksight/CHANGELOG.md#v1310-2023-02-02)
+  * **Feature**: QuickSight support for Radar Chart and Dashboard Publish Options
+
+# Release (2023-02-01)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/devopsguru`: [v1.22.0](service/devopsguru/CHANGELOG.md#v1220-2023-02-01)
+  * **Feature**: This release adds filter support ListAnomalyForInsight API.
+* `github.com/aws/aws-sdk-go-v2/service/forecast`: [v1.25.0](service/forecast/CHANGELOG.md#v1250-2023-02-01)
+  * **Feature**: This release will enable customer select INCREMENTAL as ImportModel in Forecast's CreateDatasetImportJob API. Verified latest SDK containing required attribute, following https://w.amazon.com/bin/view/AWS-Seer/Launch/Trebuchet/
+* `github.com/aws/aws-sdk-go-v2/service/iam`: [v1.19.1](service/iam/CHANGELOG.md#v1191-2023-02-01)
+  * **Documentation**: Documentation updates for AWS Identity and Access Management (IAM).
+* `github.com/aws/aws-sdk-go-v2/service/mediatailor`: [v1.22.0](service/mediatailor/CHANGELOG.md#v1220-2023-02-01)
+  * **Feature**: The AWS Elemental MediaTailor SDK for Channel Assembly has added support for program updates, and the ability to clip the end of VOD sources in programs.
+* `github.com/aws/aws-sdk-go-v2/service/sns`: [v1.20.0](service/sns/CHANGELOG.md#v1200-2023-02-01)
+  * **Feature**: Additional attributes added for set-topic-attributes.
+
+# Release (2023-01-31)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/appsync`: [v1.19.0](service/appsync/CHANGELOG.md#v1190-2023-01-31)
+  * **Feature**: This release introduces the feature to support EventBridge as AppSync data source.
+* `github.com/aws/aws-sdk-go-v2/service/cloudtrail`: [v1.23.0](service/cloudtrail/CHANGELOG.md#v1230-2023-01-31)
+  * **Feature**: Add new "Channel" APIs to enable users to manage channels used for CloudTrail Lake integrations, and "Resource Policy" APIs to enable users to manage the resource-based permissions policy attached to a channel.
+* `github.com/aws/aws-sdk-go-v2/service/cloudtraildata`: [v1.0.0](service/cloudtraildata/CHANGELOG.md#v100-2023-01-31)
+  * **Release**: New AWS service client module
+  * **Feature**: Add CloudTrail Data Service to enable users to ingest activity events from non-AWS sources into CloudTrail Lake.
+* `github.com/aws/aws-sdk-go-v2/service/codeartifact`: [v1.16.0](service/codeartifact/CHANGELOG.md#v1160-2023-01-31)
+  * **Feature**: This release introduces a new DeletePackage API, which enables deletion of a package and all of its versions from a repository.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.83.0](service/ec2/CHANGELOG.md#v1830-2023-01-31)
+  * **Feature**: This launch allows customers to associate up to 8 IP addresses to their NAT Gateways to increase the limit on concurrent connections to a single destination by eight times from 55K to 440K.
+* `github.com/aws/aws-sdk-go-v2/service/groundstation`: [v1.17.0](service/groundstation/CHANGELOG.md#v1170-2023-01-31)
+  * **Feature**: DigIF Expansion changes to the Customer APIs.
+* `github.com/aws/aws-sdk-go-v2/service/iot`: [v1.34.0](service/iot/CHANGELOG.md#v1340-2023-01-31)
+  * **Feature**: Added support for IoT Rules Engine Cloudwatch Logs action batch mode.
+* `github.com/aws/aws-sdk-go-v2/service/opensearch`: [v1.14.0](service/opensearch/CHANGELOG.md#v1140-2023-01-31)
+  * **Feature**: Amazon OpenSearch Service adds the option for a VPC endpoint connection between two domains when the local domain uses OpenSearch version 1.3 or 2.3. You can now use remote reindex to copy indices from one VPC domain to another without a reverse proxy.
+* `github.com/aws/aws-sdk-go-v2/service/polly`: [v1.24.0](service/polly/CHANGELOG.md#v1240-2023-01-31)
+  * **Feature**: Amazon Polly adds two new neural American English voices - Ruth, Stephen
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.67.0](service/sagemaker/CHANGELOG.md#v1670-2023-01-31)
+  * **Feature**: Amazon SageMaker Automatic Model Tuning now supports more completion criteria for Hyperparameter Optimization.
+* `github.com/aws/aws-sdk-go-v2/service/securityhub`: [v1.28.0](service/securityhub/CHANGELOG.md#v1280-2023-01-31)
+  * **Feature**: New fields have been added to the AWS Security Finding Format. Compliance.SecurityControlId is a unique identifier for a security control across standards. Compliance.AssociatedStandards contains all enabled standards in which a security control is enabled.
+
+# Release (2023-01-30)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/cloudformation`: [v1.26.0](service/cloudformation/CHANGELOG.md#v1260-2023-01-30)
+  * **Feature**: This feature provides a method of obtaining which regions a stackset has stack instances deployed in.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.82.0](service/ec2/CHANGELOG.md#v1820-2023-01-30)
+  * **Feature**: We add Prefix Lists as a new route destination option for LocalGatewayRoutes. This will allow customers to create routes to Prefix Lists. Prefix List routes will allow customers to group individual CIDR routes with the same target into a single route.
+
+# Release (2023-01-27)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/appstream`: [v1.20.0](service/appstream/CHANGELOG.md#v1200-2023-01-27)
+  * **Feature**: Fixing the issue where Appstream waiters hang for fleet_started and fleet_stopped.
+* `github.com/aws/aws-sdk-go-v2/service/mediatailor`: [v1.21.0](service/mediatailor/CHANGELOG.md#v1210-2023-01-27)
+  * **Feature**: This release introduces the As Run logging type, along with API and documentation updates.
+* `github.com/aws/aws-sdk-go-v2/service/outposts`: [v1.26.0](service/outposts/CHANGELOG.md#v1260-2023-01-27)
+  * **Feature**: Adding support for payment term in GetOrder, CreateOrder responses.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.66.0](service/sagemaker/CHANGELOG.md#v1660-2023-01-27)
+  * **Feature**: This release supports running SageMaker Training jobs with container images that are in a private Docker registry.
+* `github.com/aws/aws-sdk-go-v2/service/sagemakerruntime`: [v1.18.0](service/sagemakerruntime/CHANGELOG.md#v1180-2023-01-27)
+  * **Feature**: Amazon SageMaker Runtime which supports InvokeEndpointAsync asynchronously can now invoke endpoints with custom timeout values. Asynchronous invocations support longer processing times.
+
+# Release (2023-01-26)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/eventbridge`: [v1.18.0](service/eventbridge/CHANGELOG.md#v1180-2023-01-26)
+  * **Feature**: Minor comments for Redshift Serverless workgroup target support.
+
+# Release (2023-01-25)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.81.0](service/ec2/CHANGELOG.md#v1810-2023-01-25)
+  * **Feature**: This release adds new functionality that allows customers to provision IPv6 CIDR blocks through Amazon VPC IP Address Manager (IPAM) as well as allowing customers to utilize IPAM Resource Discovery APIs.
+* `github.com/aws/aws-sdk-go-v2/service/m2`: [v1.4.0](service/m2/CHANGELOG.md#v140-2023-01-25)
+  * **Feature**: Add returnCode, batchJobIdentifier in GetBatchJobExecution response, for user to view the batch job execution result & unique identifier from engine. Also removed unused headers from REST APIs
+* `github.com/aws/aws-sdk-go-v2/service/polly`: [v1.23.0](service/polly/CHANGELOG.md#v1230-2023-01-25)
+  * **Feature**: Add 5 new neural voices - Sergio (es-ES), Andres (es-MX), Remi (fr-FR), Adriano (it-IT) and Thiago (pt-BR).
+* `github.com/aws/aws-sdk-go-v2/service/redshiftserverless`: [v1.4.1](service/redshiftserverless/CHANGELOG.md#v141-2023-01-25)
+  * **Documentation**: Added query monitoring rules as possible parameters for create and update workgroup operations.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.65.0](service/sagemaker/CHANGELOG.md#v1650-2023-01-25)
+  * **Feature**: SageMaker Inference Recommender now decouples from Model Registry and could accept Model Name to invoke inference recommendations job; Inference Recommender now provides CPU/Memory Utilization metrics data in recommendation output.
+* `github.com/aws/aws-sdk-go-v2/service/sts`: [v1.18.2](service/sts/CHANGELOG.md#v1182-2023-01-25)
+  * **Documentation**: Doc only change to update wording in a key topic
+
+# Release (2023-01-24)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/route53`: [v1.27.0](service/route53/CHANGELOG.md#v1270-2023-01-24)
+  * **Feature**: Amazon Route 53 now supports the Asia Pacific (Melbourne) Region (ap-southeast-4) for latency records, geoproximity records, and private DNS for Amazon VPCs in that region.
+* `github.com/aws/aws-sdk-go-v2/service/ssmsap`: [v1.2.0](service/ssmsap/CHANGELOG.md#v120-2023-01-24)
+  * **Feature**: This release provides updates to documentation and support for listing operations performed by AWS Systems Manager for SAP.
+
+# Release (2023-01-23)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/lambda`: [v1.29.0](service/lambda/CHANGELOG.md#v1290-2023-01-23)
+  * **Feature**: Release Lambda RuntimeManagementConfig, enabling customers to better manage runtime updates to their Lambda functions. This release adds two new APIs, GetRuntimeManagementConfig and PutRuntimeManagementConfig, as well as support on existing Create/Get/Update function APIs.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.64.0](service/sagemaker/CHANGELOG.md#v1640-2023-01-23)
+  * **Feature**: Amazon SageMaker Inference now supports P4de instance types.
+
+# Release (2023-01-20)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.80.0](service/ec2/CHANGELOG.md#v1800-2023-01-20)
+  * **Feature**: C6in, M6in, M6idn, R6in and R6idn instances are powered by 3rd Generation Intel Xeon Scalable processors (code named Ice Lake) with an all-core turbo frequency of 3.5 GHz.
+* `github.com/aws/aws-sdk-go-v2/service/ivs`: [v1.20.0](service/ivs/CHANGELOG.md#v1200-2023-01-20)
+  * **Feature**: API and Doc update. Update to arns field in BatchGetStreamKey. Also updates to operations and structures.
+* `github.com/aws/aws-sdk-go-v2/service/quicksight`: [v1.30.0](service/quicksight/CHANGELOG.md#v1300-2023-01-20)
+  * **Feature**: This release adds support for data bars in QuickSight table and increases pivot table field well limit.
+
+# Release (2023-01-19)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/appflow`: [v1.24.0](service/appflow/CHANGELOG.md#v1240-2023-01-19)
+  * **Feature**: Adding support for Salesforce Pardot connector in Amazon AppFlow.
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs`: [v1.20.0](service/cloudwatchlogs/CHANGELOG.md#v1200-2023-01-19)
+  * **Feature**: Bug fix - Removed the regex pattern validation from CoralModel to avoid potential security issue.
+* `github.com/aws/aws-sdk-go-v2/service/codeartifact`: [v1.15.0](service/codeartifact/CHANGELOG.md#v1150-2023-01-19)
+  * **Feature**: Documentation updates for CodeArtifact
+* `github.com/aws/aws-sdk-go-v2/service/connect`: [v1.45.0](service/connect/CHANGELOG.md#v1450-2023-01-19)
+  * **Feature**: Amazon Connect Chat introduces Persistent Chat, allowing customers to resume previous conversations with context and transcripts carried over from previous chats, eliminating the need to repeat themselves and allowing agents to provide personalized service with access to entire conversation history.
+* `github.com/aws/aws-sdk-go-v2/service/connectparticipant`: [v1.15.0](service/connectparticipant/CHANGELOG.md#v1150-2023-01-19)
+  * **Feature**: This release updates Amazon Connect Participant's GetTranscript api to provide transcripts of past chats on a persistent chat session.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.79.0](service/ec2/CHANGELOG.md#v1790-2023-01-19)
+  * **Feature**: Adds SSM Parameter Resource Aliasing support to EC2 Launch Templates. Launch Templates can now store parameter aliases in place of AMI Resource IDs. CreateLaunchTemplateVersion and DescribeLaunchTemplateVersions now support a convenience flag, ResolveAlias, to return the resolved parameter value.
+* `github.com/aws/aws-sdk-go-v2/service/glue`: [v1.40.0](service/glue/CHANGELOG.md#v1400-2023-01-19)
+  * **Feature**: Release Glue Studio Hudi Data Lake Format for SDK/CLI
+* `github.com/aws/aws-sdk-go-v2/service/groundstation`: [v1.16.0](service/groundstation/CHANGELOG.md#v1160-2023-01-19)
+  * **Feature**: Add configurable prepass and postpass times for DataflowEndpointGroup. Add Waiter to allow customers to wait for a contact that was reserved through ReserveContact
+* `github.com/aws/aws-sdk-go-v2/service/medialive`: [v1.29.0](service/medialive/CHANGELOG.md#v1290-2023-01-19)
+  * **Feature**: AWS Elemental MediaLive adds support for SCTE 35 preRollMilliSeconds.
+* `github.com/aws/aws-sdk-go-v2/service/opensearch`: [v1.13.0](service/opensearch/CHANGELOG.md#v1130-2023-01-19)
+  * **Feature**: This release adds the enhanced dry run option, that checks for validation errors that might occur when deploying configuration changes and provides a summary of these errors, if any. The feature will also indicate whether a blue/green deployment will be required to apply a change.
+* `github.com/aws/aws-sdk-go-v2/service/panorama`: [v1.11.0](service/panorama/CHANGELOG.md#v1110-2023-01-19)
+  * **Feature**: Added AllowMajorVersionUpdate option to OTAJobConfig to make appliance software major version updates opt-in.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.63.0](service/sagemaker/CHANGELOG.md#v1630-2023-01-19)
+  * **Feature**: HyperParameterTuningJobs now allow passing environment variables into the corresponding TrainingJobs
+
+# Release (2023-01-18)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatch`: [v1.25.0](service/cloudwatch/CHANGELOG.md#v1250-2023-01-18)
+  * **Feature**: Enable cross-account streams in CloudWatch Metric Streams via Observability Access Manager.
+* `github.com/aws/aws-sdk-go-v2/service/efs`: [v1.19.1](service/efs/CHANGELOG.md#v1191-2023-01-18)
+  * **Documentation**: Documentation updates for EFS access points limit increase
+* `github.com/aws/aws-sdk-go-v2/service/wafv2`: [v1.24.2](service/wafv2/CHANGELOG.md#v1242-2023-01-18)
+  * **Documentation**: Improved the visibility of the guidance for updating AWS WAF resources, such as web ACLs and rule groups.
+
+# Release (2023-01-17)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/billingconductor`: [v1.5.0](service/billingconductor/CHANGELOG.md#v150-2023-01-17)
+  * **Feature**: This release adds support for SKU Scope for pricing plans.
+* `github.com/aws/aws-sdk-go-v2/service/imagebuilder`: [v1.22.0](service/imagebuilder/CHANGELOG.md#v1220-2023-01-17)
+  * **Feature**: Add support for AWS Marketplace product IDs as input during CreateImageRecipe for the parent-image parameter. Add support for listing third-party components.
+* `github.com/aws/aws-sdk-go-v2/service/networkfirewall`: [v1.24.0](service/networkfirewall/CHANGELOG.md#v1240-2023-01-17)
+  * **Feature**: Network Firewall now allows creation of dual stack endpoints, enabling inspection of IPv6 traffic.
+
+# Release (2023-01-13)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/connect`: [v1.44.0](service/connect/CHANGELOG.md#v1440-2023-01-13)
+  * **Feature**: This release updates the responses of UpdateContactFlowContent, UpdateContactFlowMetadata, UpdateContactFlowName and DeleteContactFlow API with empty responses.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.78.0](service/ec2/CHANGELOG.md#v1780-2023-01-13)
+  * **Feature**: Documentation updates for EC2.
+* `github.com/aws/aws-sdk-go-v2/service/outposts`: [v1.25.0](service/outposts/CHANGELOG.md#v1250-2023-01-13)
+  * **Feature**: This release adds POWER_30_KVA as an option for PowerDrawKva. PowerDrawKva is part of the RackPhysicalProperties structure in the CreateSite request.
+* `github.com/aws/aws-sdk-go-v2/service/resourcegroups`: [v1.14.0](service/resourcegroups/CHANGELOG.md#v1140-2023-01-13)
+  * **Feature**: AWS Resource Groups customers can now turn on Group Lifecycle Events in their AWS account. When you turn this on, Resource Groups monitors your groups for changes to group state or membership. Those changes are sent to Amazon EventBridge as events that you can respond to using rules you create.
+
+# Release (2023-01-12)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/cleanrooms`: [v1.0.0](service/cleanrooms/CHANGELOG.md#v100-2023-01-12)
+  * **Release**: New AWS service client module
+  * **Feature**: Initial release of AWS Clean Rooms
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs`: [v1.19.0](service/cloudwatchlogs/CHANGELOG.md#v1190-2023-01-12)
+  * **Feature**: Bug fix: logGroupName is now not a required field in GetLogEvents, FilterLogEvents, GetLogGroupFields, and DescribeLogStreams APIs as logGroupIdentifier can be provided instead
+* `github.com/aws/aws-sdk-go-v2/service/lambda`: [v1.28.0](service/lambda/CHANGELOG.md#v1280-2023-01-12)
+  * **Feature**: Add support for MaximumConcurrency parameter for SQS event source. Customers can now limit the maximum concurrent invocations for their SQS Event Source Mapping.
+* `github.com/aws/aws-sdk-go-v2/service/mediaconvert`: [v1.30.0](service/mediaconvert/CHANGELOG.md#v1300-2023-01-12)
+  * **Feature**: The AWS Elemental MediaConvert SDK has added support for compact DASH manifest generation, audio normalization using TruePeak measurements, and the ability to clip the sample range in the color corrector.
+* `github.com/aws/aws-sdk-go-v2/service/secretsmanager`: [v1.18.1](service/secretsmanager/CHANGELOG.md#v1181-2023-01-12)
+  * **Documentation**: Update documentation for new ListSecrets and DescribeSecret parameters
+
+# Release (2023-01-11)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/kendra`: [v1.38.0](service/kendra/CHANGELOG.md#v1380-2023-01-11)
+  * **Feature**: This release adds support to new document types - RTF, XML, XSLT, MS_EXCEL, CSV, JSON, MD
+
+# Release (2023-01-10)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/location`: [v1.21.0](service/location/CHANGELOG.md#v1210-2023-01-10)
+  * **Feature**: This release adds support for two new route travel models, Bicycle and Motorcycle which can be used with Grab data source.
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.40.0](service/rds/CHANGELOG.md#v1400-2023-01-10)
+  * **Feature**: This release adds support for configuring allocated storage on the CreateDBInstanceReadReplica, RestoreDBInstanceFromDBSnapshot, and RestoreDBInstanceToPointInTime APIs.
+
+# Release (2023-01-09)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/ecrpublic`: [v1.15.0](service/ecrpublic/CHANGELOG.md#v1150-2023-01-09)
+  * **Feature**: This release for Amazon ECR Public makes several change to bring the SDK into sync with the API.
+* `github.com/aws/aws-sdk-go-v2/service/kendraranking`: [v1.0.0](service/kendraranking/CHANGELOG.md#v100-2023-01-09)
+  * **Release**: New AWS service client module
+  * **Feature**: Introducing Amazon Kendra Intelligent Ranking, a new set of Kendra APIs that leverages Kendra semantic ranking capabilities to improve the quality of search results from other search services (i.e. OpenSearch, ElasticSearch, Solr).
+* `github.com/aws/aws-sdk-go-v2/service/networkfirewall`: [v1.23.0](service/networkfirewall/CHANGELOG.md#v1230-2023-01-09)
+  * **Feature**: Network Firewall now supports the Suricata rule action reject, in addition to the actions pass, drop, and alert.
+* `github.com/aws/aws-sdk-go-v2/service/workspacesweb`: [v1.9.0](service/workspacesweb/CHANGELOG.md#v190-2023-01-09)
+  * **Feature**: This release adds support for a new portal authentication type: AWS IAM Identity Center (successor to AWS Single Sign-On).
+
+# Release (2023-01-06)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/acmpca`: [v1.21.0](service/acmpca/CHANGELOG.md#v1210-2023-01-06)
+  * **Feature**: Added revocation parameter validation: bucket names must match S3 bucket naming rules and CNAMEs conform to RFC2396 restrictions on the use of special characters in URIs.
+* `github.com/aws/aws-sdk-go-v2/service/auditmanager`: [v1.23.0](service/auditmanager/CHANGELOG.md#v1230-2023-01-06)
+  * **Feature**: This release introduces a new data retention option in your Audit Manager settings. You can now use the DeregistrationPolicy parameter to specify if you want to delete your data when you deregister Audit Manager.
+
+# Release (2023-01-05)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/accessanalyzer`: [v1.19.0](service/accessanalyzer/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/account`: [v1.8.0](service/account/CHANGELOG.md#v180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/acm`: [v1.17.0](service/acm/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/acmpca`: [v1.20.0](service/acmpca/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/alexaforbusiness`: [v1.15.0](service/alexaforbusiness/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/amp`: [v1.16.0](service/amp/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/amplify`: [v1.13.0](service/amplify/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/amplifybackend`: [v1.14.0](service/amplifybackend/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Feature**: Updated GetBackendAPIModels response to include ModelIntrospectionSchema json string
+* `github.com/aws/aws-sdk-go-v2/service/amplifyuibuilder`: [v1.9.0](service/amplifyuibuilder/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/apigateway`: [v1.16.0](service/apigateway/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/apigatewaymanagementapi`: [v1.11.0](service/apigatewaymanagementapi/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/apigatewayv2`: [v1.13.0](service/apigatewayv2/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appconfig`: [v1.15.0](service/appconfig/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appconfigdata`: [v1.5.0](service/appconfigdata/CHANGELOG.md#v150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appflow`: [v1.23.0](service/appflow/CHANGELOG.md#v1230-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appintegrations`: [v1.14.0](service/appintegrations/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/applicationautoscaling`: [v1.17.0](service/applicationautoscaling/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/applicationcostprofiler`: [v1.10.0](service/applicationcostprofiler/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/applicationdiscoveryservice`: [v1.15.0](service/applicationdiscoveryservice/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/applicationinsights`: [v1.17.0](service/applicationinsights/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appmesh`: [v1.17.0](service/appmesh/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/apprunner`: [v1.16.0](service/apprunner/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Feature**: This release adds support of securely referencing secrets and configuration data that are stored in Secrets Manager and SSM Parameter Store by adding them as environment secrets in your App Runner service.
+* `github.com/aws/aws-sdk-go-v2/service/appstream`: [v1.19.0](service/appstream/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/appsync`: [v1.18.0](service/appsync/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/arczonalshift`: [v1.1.0](service/arczonalshift/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/athena`: [v1.22.0](service/athena/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/auditmanager`: [v1.22.0](service/auditmanager/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/autoscaling`: [v1.26.0](service/autoscaling/CHANGELOG.md#v1260-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/autoscalingplans`: [v1.13.0](service/autoscalingplans/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/backup`: [v1.19.0](service/backup/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/backupgateway`: [v1.9.0](service/backupgateway/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/backupstorage`: [v1.1.0](service/backupstorage/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/batch`: [v1.21.0](service/batch/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/billingconductor`: [v1.4.0](service/billingconductor/CHANGELOG.md#v140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/braket`: [v1.17.0](service/braket/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/budgets`: [v1.14.0](service/budgets/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chime`: [v1.22.0](service/chime/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chimesdkidentity`: [v1.10.0](service/chimesdkidentity/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chimesdkmediapipelines`: [v1.2.0](service/chimesdkmediapipelines/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chimesdkmeetings`: [v1.14.0](service/chimesdkmeetings/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chimesdkmessaging`: [v1.12.0](service/chimesdkmessaging/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/chimesdkvoice`: [v1.1.0](service/chimesdkvoice/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloud9`: [v1.17.0](service/cloud9/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudcontrol`: [v1.11.0](service/cloudcontrol/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/clouddirectory`: [v1.13.0](service/clouddirectory/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudformation`: [v1.25.0](service/cloudformation/CHANGELOG.md#v1250-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudfront`: [v1.24.0](service/cloudfront/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudhsm`: [v1.13.0](service/cloudhsm/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudhsmv2`: [v1.14.0](service/cloudhsmv2/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudsearch`: [v1.14.0](service/cloudsearch/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudsearchdomain`: [v1.12.0](service/cloudsearchdomain/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudtrail`: [v1.22.0](service/cloudtrail/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatch`: [v1.24.0](service/cloudwatch/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatchevents`: [v1.15.0](service/cloudwatchevents/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs`: [v1.18.0](service/cloudwatchlogs/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codeartifact`: [v1.14.0](service/codeartifact/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codebuild`: [v1.20.0](service/codebuild/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codecatalyst`: [v1.1.0](service/codecatalyst/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codecommit`: [v1.14.0](service/codecommit/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codedeploy`: [v1.16.0](service/codedeploy/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codeguruprofiler`: [v1.13.0](service/codeguruprofiler/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codegurureviewer`: [v1.17.0](service/codegurureviewer/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codepipeline`: [v1.14.0](service/codepipeline/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codestar`: [v1.13.0](service/codestar/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codestarconnections`: [v1.14.0](service/codestarconnections/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/codestarnotifications`: [v1.14.0](service/codestarnotifications/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cognitoidentity`: [v1.15.0](service/cognitoidentity/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider`: [v1.22.0](service/cognitoidentityprovider/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/cognitosync`: [v1.12.0](service/cognitosync/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/comprehend`: [v1.21.0](service/comprehend/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/comprehendmedical`: [v1.15.0](service/comprehendmedical/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/computeoptimizer`: [v1.20.0](service/computeoptimizer/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/configservice`: [v1.29.0](service/configservice/CHANGELOG.md#v1290-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/connect`: [v1.43.0](service/connect/CHANGELOG.md#v1430-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Feature**: Documentation update for a new Initiation Method value in DescribeContact API
+* `github.com/aws/aws-sdk-go-v2/service/connectcampaigns`: [v1.2.0](service/connectcampaigns/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/connectcases`: [v1.2.0](service/connectcases/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/connectcontactlens`: [v1.13.0](service/connectcontactlens/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/connectparticipant`: [v1.14.0](service/connectparticipant/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/controltower`: [v1.1.0](service/controltower/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/costandusagereportservice`: [v1.15.0](service/costandusagereportservice/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/costexplorer`: [v1.25.0](service/costexplorer/CHANGELOG.md#v1250-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/customerprofiles`: [v1.22.0](service/customerprofiles/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/databasemigrationservice`: [v1.23.0](service/databasemigrationservice/CHANGELOG.md#v1230-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/databrew`: [v1.21.0](service/databrew/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/dataexchange`: [v1.18.0](service/dataexchange/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/datapipeline`: [v1.14.0](service/datapipeline/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/datasync`: [v1.21.0](service/datasync/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/dax`: [v1.12.0](service/dax/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/detective`: [v1.18.0](service/detective/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/devicefarm`: [v1.15.0](service/devicefarm/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/devopsguru`: [v1.21.0](service/devopsguru/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/directconnect`: [v1.18.0](service/directconnect/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/directoryservice`: [v1.16.0](service/directoryservice/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/dlm`: [v1.14.0](service/dlm/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/docdb`: [v1.20.0](service/docdb/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/docdbelastic`: [v1.1.0](service/docdbelastic/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/drs`: [v1.10.0](service/drs/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/dynamodb`: [v1.18.0](service/dynamodb/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/dynamodbstreams`: [v1.14.0](service/dynamodbstreams/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ebs`: [v1.16.0](service/ebs/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect`: [v1.15.0](service/ec2instanceconnect/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ecr`: [v1.18.0](service/ecr/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ecrpublic`: [v1.14.0](service/ecrpublic/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ecs`: [v1.23.0](service/ecs/CHANGELOG.md#v1230-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/efs`: [v1.19.0](service/efs/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/eks`: [v1.27.0](service/eks/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticache`: [v1.26.0](service/elasticache/CHANGELOG.md#v1260-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk`: [v1.15.0](service/elasticbeanstalk/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticinference`: [v1.12.0](service/elasticinference/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing`: [v1.15.0](service/elasticloadbalancing/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2`: [v1.19.0](service/elasticloadbalancingv2/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elasticsearchservice`: [v1.18.0](service/elasticsearchservice/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/elastictranscoder`: [v1.14.0](service/elastictranscoder/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/emr`: [v1.22.0](service/emr/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/emrcontainers`: [v1.16.0](service/emrcontainers/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/emrserverless`: [v1.5.0](service/emrserverless/CHANGELOG.md#v150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Feature**: Adds support for customized images. You can now provide runtime images when creating or updating EMR Serverless Applications.
+* `github.com/aws/aws-sdk-go-v2/service/eventbridge`: [v1.17.0](service/eventbridge/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/evidently`: [v1.10.0](service/evidently/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/finspace`: [v1.9.0](service/finspace/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/finspacedata`: [v1.14.0](service/finspacedata/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/firehose`: [v1.16.0](service/firehose/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/fis`: [v1.14.0](service/fis/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/fms`: [v1.22.0](service/fms/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/forecast`: [v1.24.0](service/forecast/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/forecastquery`: [v1.13.0](service/forecastquery/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/frauddetector`: [v1.21.0](service/frauddetector/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/fsx`: [v1.28.0](service/fsx/CHANGELOG.md#v1280-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/gamelift`: [v1.17.0](service/gamelift/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/gamesparks`: [v1.2.0](service/gamesparks/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/glacier`: [v1.14.0](service/glacier/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/globalaccelerator`: [v1.16.0](service/globalaccelerator/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/glue`: [v1.39.0](service/glue/CHANGELOG.md#v1390-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/grafana`: [v1.11.0](service/grafana/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/greengrass`: [v1.15.0](service/greengrass/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/greengrassv2`: [v1.21.0](service/greengrassv2/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/groundstation`: [v1.15.0](service/groundstation/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/guardduty`: [v1.17.0](service/guardduty/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/health`: [v1.16.0](service/health/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/healthlake`: [v1.15.0](service/healthlake/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/honeycode`: [v1.13.0](service/honeycode/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iam`: [v1.19.0](service/iam/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/identitystore`: [v1.16.0](service/identitystore/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/imagebuilder`: [v1.21.0](service/imagebuilder/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/inspector`: [v1.13.0](service/inspector/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/inspector2`: [v1.11.0](service/inspector2/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iot`: [v1.33.0](service/iot/CHANGELOG.md#v1330-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iot1clickdevicesservice`: [v1.11.0](service/iot1clickdevicesservice/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iot1clickprojects`: [v1.12.0](service/iot1clickprojects/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotanalytics`: [v1.14.0](service/iotanalytics/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotdataplane`: [v1.14.0](service/iotdataplane/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotdeviceadvisor`: [v1.17.0](service/iotdeviceadvisor/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotevents`: [v1.15.0](service/iotevents/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ioteventsdata`: [v1.13.0](service/ioteventsdata/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotfleethub`: [v1.13.0](service/iotfleethub/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotfleetwise`: [v1.3.0](service/iotfleetwise/CHANGELOG.md#v130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotjobsdataplane`: [v1.12.0](service/iotjobsdataplane/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotroborunner`: [v1.1.0](service/iotroborunner/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotsecuretunneling`: [v1.15.0](service/iotsecuretunneling/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotsitewise`: [v1.27.0](service/iotsitewise/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotthingsgraph`: [v1.14.0](service/iotthingsgraph/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iottwinmaker`: [v1.10.0](service/iottwinmaker/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/iotwireless`: [v1.24.0](service/iotwireless/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ivs`: [v1.19.0](service/ivs/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ivschat`: [v1.3.0](service/ivschat/CHANGELOG.md#v130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kafka`: [v1.19.0](service/kafka/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kafkaconnect`: [v1.9.0](service/kafkaconnect/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kendra`: [v1.37.0](service/kendra/CHANGELOG.md#v1370-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/keyspaces`: [v1.1.0](service/keyspaces/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesis`: [v1.17.0](service/kinesis/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisanalytics`: [v1.14.0](service/kinesisanalytics/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisanalyticsv2`: [v1.16.0](service/kinesisanalyticsv2/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideo`: [v1.15.0](service/kinesisvideo/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideoarchivedmedia`: [v1.14.0](service/kinesisvideoarchivedmedia/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideomedia`: [v1.11.0](service/kinesisvideomedia/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideosignaling`: [v1.11.0](service/kinesisvideosignaling/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideowebrtcstorage`: [v1.2.0](service/kinesisvideowebrtcstorage/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/kms`: [v1.20.0](service/kms/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lakeformation`: [v1.19.0](service/lakeformation/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lambda`: [v1.27.0](service/lambda/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lexmodelbuildingservice`: [v1.17.0](service/lexmodelbuildingservice/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lexmodelsv2`: [v1.27.0](service/lexmodelsv2/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lexruntimeservice`: [v1.13.0](service/lexruntimeservice/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lexruntimev2`: [v1.16.0](service/lexruntimev2/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/licensemanager`: [v1.17.0](service/licensemanager/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/licensemanagerlinuxsubscriptions`: [v1.1.0](service/licensemanagerlinuxsubscriptions/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/licensemanagerusersubscriptions`: [v1.2.0](service/licensemanagerusersubscriptions/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lightsail`: [v1.25.0](service/lightsail/CHANGELOG.md#v1250-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Documentation**: Documentation updates for Amazon Lightsail.
+* `github.com/aws/aws-sdk-go-v2/service/location`: [v1.20.0](service/location/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lookoutequipment`: [v1.17.0](service/lookoutequipment/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lookoutmetrics`: [v1.19.0](service/lookoutmetrics/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/lookoutvision`: [v1.15.0](service/lookoutvision/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/m2`: [v1.3.0](service/m2/CHANGELOG.md#v130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/machinelearning`: [v1.15.0](service/machinelearning/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/macie`: [v1.15.0](service/macie/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/macie2`: [v1.26.0](service/macie2/CHANGELOG.md#v1260-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/managedblockchain`: [v1.14.0](service/managedblockchain/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/marketplacecatalog`: [v1.15.0](service/marketplacecatalog/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/marketplacecommerceanalytics`: [v1.12.0](service/marketplacecommerceanalytics/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/marketplaceentitlementservice`: [v1.12.0](service/marketplaceentitlementservice/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/marketplacemetering`: [v1.14.0](service/marketplacemetering/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediaconnect`: [v1.18.0](service/mediaconnect/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediaconvert`: [v1.29.0](service/mediaconvert/CHANGELOG.md#v1290-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/medialive`: [v1.28.0](service/medialive/CHANGELOG.md#v1280-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediapackage`: [v1.20.0](service/mediapackage/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediapackagevod`: [v1.21.0](service/mediapackagevod/CHANGELOG.md#v1210-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediastore`: [v1.13.0](service/mediastore/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediastoredata`: [v1.13.0](service/mediastoredata/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mediatailor`: [v1.20.0](service/mediatailor/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/memorydb`: [v1.12.0](service/memorydb/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mgn`: [v1.17.0](service/mgn/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/migrationhub`: [v1.13.0](service/migrationhub/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/migrationhubconfig`: [v1.13.0](service/migrationhubconfig/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/migrationhuborchestrator`: [v1.1.0](service/migrationhuborchestrator/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/migrationhubrefactorspaces`: [v1.8.0](service/migrationhubrefactorspaces/CHANGELOG.md#v180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/migrationhubstrategy`: [v1.7.0](service/migrationhubstrategy/CHANGELOG.md#v170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mobile`: [v1.12.0](service/mobile/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mq`: [v1.14.0](service/mq/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mturk`: [v1.14.0](service/mturk/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/mwaa`: [v1.14.0](service/mwaa/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Documentation**: MWAA supports Apache Airflow version 2.4.3.
+* `github.com/aws/aws-sdk-go-v2/service/neptune`: [v1.19.0](service/neptune/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/networkfirewall`: [v1.22.0](service/networkfirewall/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/networkmanager`: [v1.17.0](service/networkmanager/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/nimble`: [v1.16.0](service/nimble/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/oam`: [v1.1.0](service/oam/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/omics`: [v1.1.0](service/omics/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/opensearch`: [v1.12.0](service/opensearch/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/opensearchserverless`: [v1.1.0](service/opensearchserverless/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/opsworks`: [v1.14.0](service/opsworks/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/opsworkscm`: [v1.15.0](service/opsworkscm/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/organizations`: [v1.18.0](service/organizations/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/outposts`: [v1.24.0](service/outposts/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/panorama`: [v1.10.0](service/panorama/CHANGELOG.md#v1100-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/personalize`: [v1.23.0](service/personalize/CHANGELOG.md#v1230-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/personalizeevents`: [v1.13.0](service/personalizeevents/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/personalizeruntime`: [v1.13.0](service/personalizeruntime/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pi`: [v1.16.0](service/pi/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pinpoint`: [v1.18.0](service/pinpoint/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pinpointemail`: [v1.12.0](service/pinpointemail/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoice`: [v1.11.0](service/pinpointsmsvoice/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoicev2`: [v1.1.0](service/pinpointsmsvoicev2/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pipes`: [v1.1.0](service/pipes/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/polly`: [v1.22.0](service/polly/CHANGELOG.md#v1220-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/pricing`: [v1.18.0](service/pricing/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/privatenetworks`: [v1.1.0](service/privatenetworks/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/proton`: [v1.19.0](service/proton/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/qldb`: [v1.15.0](service/qldb/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/qldbsession`: [v1.14.0](service/qldbsession/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/quicksight`: [v1.29.0](service/quicksight/CHANGELOG.md#v1290-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ram`: [v1.17.0](service/ram/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/rbin`: [v1.8.0](service/rbin/CHANGELOG.md#v180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.39.0](service/rds/CHANGELOG.md#v1390-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+  * **Feature**: This release adds support for specifying which certificate authority (CA) to use for a DB instance's server certificate during DB instance creation, as well as other CA enhancements.
+* `github.com/aws/aws-sdk-go-v2/service/rdsdata`: [v1.13.0](service/rdsdata/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/redshift`: [v1.27.0](service/redshift/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/redshiftdata`: [v1.18.0](service/redshiftdata/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/redshiftserverless`: [v1.4.0](service/redshiftserverless/CHANGELOG.md#v140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/rekognition`: [v1.23.0](service/rekognition/CHANGELOG.md#v1230-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/resiliencehub`: [v1.8.0](service/resiliencehub/CHANGELOG.md#v180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/resourceexplorer2`: [v1.2.0](service/resourceexplorer2/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/resourcegroups`: [v1.13.0](service/resourcegroups/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi`: [v1.14.0](service/resourcegroupstaggingapi/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/robomaker`: [v1.18.0](service/robomaker/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/rolesanywhere`: [v1.1.0](service/rolesanywhere/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53`: [v1.26.0](service/route53/CHANGELOG.md#v1260-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53domains`: [v1.14.0](service/route53domains/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53recoverycluster`: [v1.11.0](service/route53recoverycluster/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53recoverycontrolconfig`: [v1.11.0](service/route53recoverycontrolconfig/CHANGELOG.md#v1110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53recoveryreadiness`: [v1.9.0](service/route53recoveryreadiness/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/route53resolver`: [v1.16.0](service/route53resolver/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/rum`: [v1.9.0](service/rum/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/s3`: [v1.30.0](service/s3/CHANGELOG.md#v1300-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/s3control`: [v1.29.0](service/s3control/CHANGELOG.md#v1290-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/s3outposts`: [v1.14.0](service/s3outposts/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.62.0](service/sagemaker/CHANGELOG.md#v1620-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemakera2iruntime`: [v1.15.0](service/sagemakera2iruntime/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemakeredge`: [v1.13.0](service/sagemakeredge/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemakerfeaturestoreruntime`: [v1.13.0](service/sagemakerfeaturestoreruntime/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemakergeospatial`: [v1.1.0](service/sagemakergeospatial/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sagemakerruntime`: [v1.17.0](service/sagemakerruntime/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/savingsplans`: [v1.12.0](service/savingsplans/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/scheduler`: [v1.1.0](service/scheduler/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/schemas`: [v1.15.0](service/schemas/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/secretsmanager`: [v1.18.0](service/secretsmanager/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/securityhub`: [v1.27.0](service/securityhub/CHANGELOG.md#v1270-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/securitylake`: [v1.2.0](service/securitylake/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/serverlessapplicationrepository`: [v1.12.0](service/serverlessapplicationrepository/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/servicecatalog`: [v1.16.0](service/servicecatalog/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/servicecatalogappregistry`: [v1.16.0](service/servicecatalogappregistry/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/servicediscovery`: [v1.19.0](service/servicediscovery/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/servicequotas`: [v1.14.0](service/servicequotas/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ses`: [v1.15.0](service/ses/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sesv2`: [v1.16.0](service/sesv2/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sfn`: [v1.17.0](service/sfn/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/shield`: [v1.18.0](service/shield/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/signer`: [v1.14.0](service/signer/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/simspaceweaver`: [v1.1.0](service/simspaceweaver/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sms`: [v1.13.0](service/sms/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/snowball`: [v1.17.0](service/snowball/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/snowdevicemanagement`: [v1.9.0](service/snowdevicemanagement/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sns`: [v1.19.0](service/sns/CHANGELOG.md#v1190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sqs`: [v1.20.0](service/sqs/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssm`: [v1.35.0](service/ssm/CHANGELOG.md#v1350-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssmcontacts`: [v1.14.0](service/ssmcontacts/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssmincidents`: [v1.20.0](service/ssmincidents/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssmsap`: [v1.1.0](service/ssmsap/CHANGELOG.md#v110-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sso`: [v1.12.0](service/sso/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssoadmin`: [v1.16.0](service/ssoadmin/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/ssooidc`: [v1.14.0](service/ssooidc/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/storagegateway`: [v1.18.0](service/storagegateway/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/sts`: [v1.18.0](service/sts/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/support`: [v1.14.0](service/support/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/supportapp`: [v1.2.0](service/supportapp/CHANGELOG.md#v120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/swf`: [v1.14.0](service/swf/CHANGELOG.md#v1140-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/synthetics`: [v1.17.0](service/synthetics/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/textract`: [v1.20.0](service/textract/CHANGELOG.md#v1200-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/timestreamquery`: [v1.15.0](service/timestreamquery/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/timestreamwrite`: [v1.15.0](service/timestreamwrite/CHANGELOG.md#v1150-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/transcribe`: [v1.25.0](service/transcribe/CHANGELOG.md#v1250-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/transcribestreaming`: [v1.9.0](service/transcribestreaming/CHANGELOG.md#v190-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/transfer`: [v1.28.0](service/transfer/CHANGELOG.md#v1280-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/translate`: [v1.17.0](service/translate/CHANGELOG.md#v1170-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/voiceid`: [v1.12.0](service/voiceid/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/waf`: [v1.12.0](service/waf/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/wafregional`: [v1.13.0](service/wafregional/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/wafv2`: [v1.24.0](service/wafv2/CHANGELOG.md#v1240-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/wellarchitected`: [v1.18.0](service/wellarchitected/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/wisdom`: [v1.12.0](service/wisdom/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/workdocs`: [v1.13.0](service/workdocs/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/worklink`: [v1.13.0](service/worklink/CHANGELOG.md#v1130-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/workmail`: [v1.18.0](service/workmail/CHANGELOG.md#v1180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/workmailmessageflow`: [v1.12.0](service/workmailmessageflow/CHANGELOG.md#v1120-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/workspaces`: [v1.28.0](service/workspaces/CHANGELOG.md#v1280-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/workspacesweb`: [v1.8.0](service/workspacesweb/CHANGELOG.md#v180-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+* `github.com/aws/aws-sdk-go-v2/service/xray`: [v1.16.0](service/xray/CHANGELOG.md#v1160-2023-01-05)
+  * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
+
+# Release (2023-01-04)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/applicationautoscaling`: [v1.16.0](service/applicationautoscaling/CHANGELOG.md#v1160-2023-01-04)
+  * **Feature**: Customers can now use the existing DescribeScalingActivities API to also see the detailed and machine-readable reasons for Application Auto Scaling not scaling their resources and, if needed, take the necessary corrective actions.
+* `github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs`: [v1.17.4](service/cloudwatchlogs/CHANGELOG.md#v1174-2023-01-04)
+  * **Documentation**: Update to remove sequenceToken as a required field in PutLogEvents calls.
+* `github.com/aws/aws-sdk-go-v2/service/ssm`: [v1.34.0](service/ssm/CHANGELOG.md#v1340-2023-01-04)
+  * **Feature**: Adding support for QuickSetup Document Type in Systems Manager
+
+# Release (2023-01-03)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/securitylake`: [v1.1.0](service/securitylake/CHANGELOG.md#v110-2023-01-03)
+  * **Feature**: Allow CreateSubscriber API to take string input that allows setting more descriptive SubscriberDescription field. Make souceTypes field required in model level for UpdateSubscriberRequest as it is required for every API call on the backend. Allow ListSubscribers take any String as nextToken param.
+
+# Release (2022-12-30)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/cloudfront`: [v1.23.0](service/cloudfront/CHANGELOG.md#v1230-2022-12-30)
+  * **Feature**: Extend response headers policy to support removing headers from viewer responses
+* `github.com/aws/aws-sdk-go-v2/service/iotfleetwise`: [v1.2.1](service/iotfleetwise/CHANGELOG.md#v121-2022-12-30)
+  * **Documentation**: Update documentation - correct the epoch constant value of default value for expiryTime field in CreateCampaign request.
+
+# Release (2022-12-29)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/apigateway`: [v1.15.28](service/apigateway/CHANGELOG.md#v11528-2022-12-29)
+  * **Documentation**: Documentation updates for Amazon API Gateway
+* `github.com/aws/aws-sdk-go-v2/service/emr`: [v1.21.0](service/emr/CHANGELOG.md#v1210-2022-12-29)
+  * **Feature**: Added GetClusterSessionCredentials API to allow Amazon SageMaker Studio to connect to EMR on EC2 clusters with runtime roles and AWS Lake Formation-based access control for Apache Spark, Apache Hive, and Presto queries.
+* `github.com/aws/aws-sdk-go-v2/service/secretsmanager`: [v1.17.0](service/secretsmanager/CHANGELOG.md#v1170-2022-12-29)
+  * **Feature**: Added owning service filter, include planned deletion flag, and next rotation date response parameter in ListSecrets.
+* `github.com/aws/aws-sdk-go-v2/service/wisdom`: [v1.11.0](service/wisdom/CHANGELOG.md#v1110-2022-12-29)
+  * **Feature**: This release extends Wisdom CreateContent and StartContentUpload APIs to support PDF and MicrosoftWord docx document uploading.
+
+# Release (2022-12-28)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/elasticache`: [v1.25.0](service/elasticache/CHANGELOG.md#v1250-2022-12-28)
+  * **Feature**: This release allows you to modify the encryption in transit setting, for existing Redis clusters. You can now change the TLS configuration of your Redis clusters without the need to re-build or re-provision the clusters or impact application availability.
+* `github.com/aws/aws-sdk-go-v2/service/networkfirewall`: [v1.21.0](service/networkfirewall/CHANGELOG.md#v1210-2022-12-28)
+  * **Feature**: AWS Network Firewall now provides status messages for firewalls to help you troubleshoot when your endpoint fails.
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.38.0](service/rds/CHANGELOG.md#v1380-2022-12-28)
+  * **Feature**: This release adds support for Custom Engine Version (CEV) on RDS Custom SQL Server.
+* `github.com/aws/aws-sdk-go-v2/service/route53recoverycontrolconfig`: [v1.10.0](service/route53recoverycontrolconfig/CHANGELOG.md#v1100-2022-12-28)
+  * **Feature**: Added support for Python paginators in the route53-recovery-control-config List* APIs.
+
+# Release (2022-12-27)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/memorydb`: [v1.11.0](service/memorydb/CHANGELOG.md#v1110-2022-12-27)
+  * **Feature**: This release adds support for MemoryDB Reserved nodes which provides a significant discount compared to on-demand node pricing. Reserved nodes are not physical nodes, but rather a billing discount applied to the use of on-demand nodes in your account.
+* `github.com/aws/aws-sdk-go-v2/service/transfer`: [v1.27.0](service/transfer/CHANGELOG.md#v1270-2022-12-27)
+  * **Feature**: Add additional operations to throw ThrottlingExceptions
+
+# Release (2022-12-23)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/connect`: [v1.42.0](service/connect/CHANGELOG.md#v1420-2022-12-23)
+  * **Feature**: Support for Routing Profile filter, SortCriteria, and grouping by Routing Profiles for GetCurrentMetricData API. Support for RoutingProfiles, UserHierarchyGroups, and Agents as filters, NextStatus and AgentStatusName for GetCurrentUserData. Adds ApproximateTotalCount to both APIs.
+* `github.com/aws/aws-sdk-go-v2/service/connectparticipant`: [v1.13.0](service/connectparticipant/CHANGELOG.md#v1130-2022-12-23)
+  * **Feature**: Amazon Connect Chat introduces the Message Receipts feature. This feature allows agents and customers to receive message delivered and read receipts after they send a chat message.
+* `github.com/aws/aws-sdk-go-v2/service/detective`: [v1.17.0](service/detective/CHANGELOG.md#v1170-2022-12-23)
+  * **Feature**: This release adds a missed AccessDeniedException type to several endpoints.
+* `github.com/aws/aws-sdk-go-v2/service/fsx`: [v1.27.0](service/fsx/CHANGELOG.md#v1270-2022-12-23)
+  * **Feature**: Fix a bug where a recent release might break certain existing SDKs.
+* `github.com/aws/aws-sdk-go-v2/service/inspector2`: [v1.10.0](service/inspector2/CHANGELOG.md#v1100-2022-12-23)
+  * **Feature**: Amazon Inspector adds support for scanning NodeJS 18.x and Go 1.x AWS Lambda function runtimes.
+
+# Release (2022-12-22)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/computeoptimizer`: [v1.19.0](service/computeoptimizer/CHANGELOG.md#v1190-2022-12-22)
+  * **Feature**: This release enables AWS Compute Optimizer to analyze and generate optimization recommendations for ecs services running on Fargate.
+* `github.com/aws/aws-sdk-go-v2/service/connect`: [v1.41.0](service/connect/CHANGELOG.md#v1410-2022-12-22)
+  * **Feature**: Amazon Connect Chat introduces the Idle Participant/Autodisconnect feature, which allows users to set timeouts relating to the activity of chat participants, using the new UpdateParticipantRoleConfig API.
+* `github.com/aws/aws-sdk-go-v2/service/iotdeviceadvisor`: [v1.16.0](service/iotdeviceadvisor/CHANGELOG.md#v1160-2022-12-22)
+  * **Feature**: This release adds the following new features: 1) Documentation updates for IoT Device Advisor APIs. 2) Updated required request parameters for IoT Device Advisor APIs. 3) Added new service feature: ability to provide the test endpoint when customer executing the StartSuiteRun API.
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideowebrtcstorage`: [v1.1.0](service/kinesisvideowebrtcstorage/CHANGELOG.md#v110-2022-12-22)
+  * **Feature**: Amazon Kinesis Video Streams offers capabilities to stream video and audio in real-time via WebRTC to the cloud for storage, playback, and analytical processing. Customers can use our enhanced WebRTC SDK and cloud APIs to enable real-time streaming, as well as media ingestion to the cloud.
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.37.0](service/rds/CHANGELOG.md#v1370-2022-12-22)
+  * **Feature**: Add support for managing master user password in AWS Secrets Manager for the DBInstance and DBCluster.
+* `github.com/aws/aws-sdk-go-v2/service/secretsmanager`: [v1.16.11](service/secretsmanager/CHANGELOG.md#v11611-2022-12-22)
+  * **Documentation**: Documentation updates for Secrets Manager
+
+# Release (2022-12-21)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/licensemanagerlinuxsubscriptions`: [v1.0.0](service/licensemanagerlinuxsubscriptions/CHANGELOG.md#v100-2022-12-21)
+  * **Release**: New AWS service client module
+  * **Feature**: AWS License Manager now offers cross-region, cross-account tracking of commercial Linux subscriptions on AWS. This includes subscriptions purchased as part of EC2 subscription-included AMIs, on the AWS Marketplace, or brought to AWS via Red Hat Cloud Access Program.
+* `github.com/aws/aws-sdk-go-v2/service/macie2`: [v1.25.0](service/macie2/CHANGELOG.md#v1250-2022-12-21)
+  * **Feature**: This release adds support for analyzing Amazon S3 objects that use the S3 Glacier Instant Retrieval (Glacier_IR) storage class.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.61.0](service/sagemaker/CHANGELOG.md#v1610-2022-12-21)
+  * **Feature**: This release enables adding RStudio Workbench support to an existing Amazon SageMaker Studio domain. It allows setting your RStudio on SageMaker environment configuration parameters and also updating the RStudioConnectUrl and RStudioPackageManagerUrl parameters for existing domains
+* `github.com/aws/aws-sdk-go-v2/service/ssm`: [v1.33.4](service/ssm/CHANGELOG.md#v1334-2022-12-21)
+  * **Documentation**: Doc-only updates for December 2022.
+* `github.com/aws/aws-sdk-go-v2/service/support`: [v1.13.22](service/support/CHANGELOG.md#v11322-2022-12-21)
+  * **Documentation**: Documentation updates for the AWS Support API
+* `github.com/aws/aws-sdk-go-v2/service/transfer`: [v1.26.0](service/transfer/CHANGELOG.md#v1260-2022-12-21)
+  * **Feature**: This release adds support for Decrypt as a workflow step type.
+
+# Release (2022-12-20)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/batch`: [v1.20.0](service/batch/CHANGELOG.md#v1200-2022-12-20)
+  * **Feature**: Adds isCancelled and isTerminated to DescribeJobs response.
+* `github.com/aws/aws-sdk-go-v2/service/ec2`: [v1.77.0](service/ec2/CHANGELOG.md#v1770-2022-12-20)
+  * **Feature**: Adds support for pagination in the EC2 DescribeImages API.
+* `github.com/aws/aws-sdk-go-v2/service/lookoutequipment`: [v1.16.0](service/lookoutequipment/CHANGELOG.md#v1160-2022-12-20)
+  * **Feature**: This release adds support for listing inference schedulers by status.
+* `github.com/aws/aws-sdk-go-v2/service/medialive`: [v1.27.0](service/medialive/CHANGELOG.md#v1270-2022-12-20)
+  * **Feature**: This release adds support for two new features to AWS Elemental MediaLive. First, you can now burn-in timecodes to your MediaLive outputs. Second, we now now support the ability to decode Dolby E audio when it comes in on an input.
+* `github.com/aws/aws-sdk-go-v2/service/nimble`: [v1.15.0](service/nimble/CHANGELOG.md#v1150-2022-12-20)
+  * **Feature**: Amazon Nimble Studio now supports configuring session storage volumes and persistence, as well as backup and restore sessions through launch profiles.
+* `github.com/aws/aws-sdk-go-v2/service/resourceexplorer2`: [v1.1.0](service/resourceexplorer2/CHANGELOG.md#v110-2022-12-20)
+  * **Feature**: Documentation updates for AWS Resource Explorer.
+* `github.com/aws/aws-sdk-go-v2/service/route53domains`: [v1.13.0](service/route53domains/CHANGELOG.md#v1130-2022-12-20)
+  * **Feature**: Use Route 53 domain APIs to change owner, create/delete DS record, modify IPS tag, resend authorization. New: AssociateDelegationSignerToDomain, DisassociateDelegationSignerFromDomain, PushDomain, ResendOperationAuthorization. Updated: UpdateDomainContact, ListOperations, CheckDomainTransferability.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.60.0](service/sagemaker/CHANGELOG.md#v1600-2022-12-20)
+  * **Feature**: Amazon SageMaker Autopilot adds support for new objective metrics in CreateAutoMLJob API.
+* `github.com/aws/aws-sdk-go-v2/service/transcribe`: [v1.24.0](service/transcribe/CHANGELOG.md#v1240-2022-12-20)
+  * **Feature**: Enable our batch transcription jobs for Swedish and Vietnamese.
+
+# Release (2022-12-19)
+
+## General Highlights
+* **Dependency Update**: Updated to the latest SDK module versions
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/athena`: [v1.21.0](service/athena/CHANGELOG.md#v1210-2022-12-19)
+  * **Feature**: Add missed InvalidRequestException in GetCalculationExecutionCode,StopCalculationExecution APIs. Correct required parameters (Payload and Type) in UpdateNotebook API. Change Notebook size from 15 Mb to 10 Mb.
+* `github.com/aws/aws-sdk-go-v2/service/ecs`: [v1.22.0](service/ecs/CHANGELOG.md#v1220-2022-12-19)
+  * **Feature**: This release adds support for alarm-based rollbacks in ECS, a new feature that allows customers to add automated safeguards for Amazon ECS service rolling updates.
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideo`: [v1.14.0](service/kinesisvideo/CHANGELOG.md#v1140-2022-12-19)
+  * **Feature**: Amazon Kinesis Video Streams offers capabilities to stream video and audio in real-time via WebRTC to the cloud for storage, playback, and analytical processing. Customers can use our enhanced WebRTC SDK and cloud APIs to enable real-time streaming, as well as media ingestion to the cloud.
+* `github.com/aws/aws-sdk-go-v2/service/kinesisvideowebrtcstorage`: [v1.0.0](service/kinesisvideowebrtcstorage/CHANGELOG.md#v100-2022-12-19)
+  * **Release**: New AWS service client module
+  * **Feature**: Amazon Kinesis Video Streams offers capabilities to stream video and audio in real-time via WebRTC to the cloud for storage, playback, and analytical processing. Customers can use our enhanced WebRTC SDK and cloud APIs to enable real-time streaming, as well as media ingestion to the cloud.
+* `github.com/aws/aws-sdk-go-v2/service/rds`: [v1.36.0](service/rds/CHANGELOG.md#v1360-2022-12-19)
+  * **Feature**: Add support for --enable-customer-owned-ip to RDS create-db-instance-read-replica API for RDS on Outposts.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.59.0](service/sagemaker/CHANGELOG.md#v1590-2022-12-19)
+  * **Feature**: AWS Sagemaker - Sagemaker Images now supports Aliases as secondary identifiers for ImageVersions. SageMaker Images now supports additional metadata for ImageVersions for better images management.
+
+# Release (2022-12-16)
+
+## Module Highlights
+* `github.com/aws/aws-sdk-go-v2/service/appflow`: [v1.22.0](service/appflow/CHANGELOG.md#v1220-2022-12-16)
+  * **Feature**: This release updates the ListConnectorEntities API action so that it returns paginated responses that customers can retrieve with next tokens.
+* `github.com/aws/aws-sdk-go-v2/service/cloudfront`: [v1.22.2](service/cloudfront/CHANGELOG.md#v1222-2022-12-16)
+  * **Documentation**: Updated documentation for CloudFront
+* `github.com/aws/aws-sdk-go-v2/service/datasync`: [v1.20.0](service/datasync/CHANGELOG.md#v1200-2022-12-16)
+  * **Feature**: AWS DataSync now supports the use of tags with task executions. With this new feature, you can apply tags each time you execute a task, giving you greater control and management over your task executions.
+* `github.com/aws/aws-sdk-go-v2/service/efs`: [v1.18.3](service/efs/CHANGELOG.md#v1183-2022-12-16)
+  * **Documentation**: General documentation updates for EFS.
+* `github.com/aws/aws-sdk-go-v2/service/guardduty`: [v1.16.6](service/guardduty/CHANGELOG.md#v1166-2022-12-16)
+  * **Documentation**: This release provides the valid characters for the Description and Name field.
+* `github.com/aws/aws-sdk-go-v2/service/iotfleetwise`: [v1.2.0](service/iotfleetwise/CHANGELOG.md#v120-2022-12-16)
+  * **Feature**: Updated error handling for empty resource names in "UpdateSignalCatalog" and "GetModelManifest" operations.
+* `github.com/aws/aws-sdk-go-v2/service/sagemaker`: [v1.58.0](service/sagemaker/CHANGELOG.md#v1580-2022-12-16)
+  * **Feature**: AWS sagemaker - Features: This release adds support for random seed, it's an integer value used to initialize a pseudo-random number generator. Setting a random seed will allow the hyperparameter tuning search strategies to produce more consistent configurations for the same tuning job.
+
 # Release (2022-12-15)
 
 ## General Highlights
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/README.md b/vendor/github.com/aws/aws-sdk-go-v2/README.md
index da74d0e33..dac5c8bcb 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/README.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/README.md
@@ -87,7 +87,7 @@ func main() {
 ###### Compile and Execute
 ```sh
 $ go run .
-Table:
+Tables:
 tableOne
 tableTwo
 ```
@@ -97,9 +97,9 @@ tableTwo
 Please use these community resources for getting help. We use the GitHub issues
 for tracking bugs and feature requests.
 
-* Ask a question on [StackOverflow](http://stackoverflow.com/) and tag it with the [`aws-sdk-go`](http://stackoverflow.com/questions/tagged/aws-sdk-go) tag.
-* Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html).
+* Ask us a [question](https://github.com/aws/aws-sdk-go-v2/discussions/new?category=q-a) or open a [discussion](https://github.com/aws/aws-sdk-go-v2/discussions/new?category=general).
 * If you think you may have found a bug, please open an [issue](https://github.com/aws/aws-sdk-go-v2/issues/new/choose).
+* Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html).
 
 This SDK implements AWS service APIs. For general issues regarding the AWS services and their limitations, you may also take a look at the [Amazon Web Services Discussion Forums](https://forums.aws.amazon.com/).
 
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go
index 37c643dd3..39abc2e24 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go
@@ -3,4 +3,4 @@
 package aws
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.17.3"
+const goModuleVersion = "1.17.4"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/array.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/array.go
index 9d7d3a0cb..47ebc0f54 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/array.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/array.go
@@ -36,20 +36,31 @@ type Array struct {
 	memberName string
 	// Elements are stored in values, so we keep track of the list size here.
 	size int32
+	// Empty lists are encoded as "<prefix>=", if we add a value later we will
+	// remove this encoding
+	emptyValue Value
 }
 
 func newArray(values url.Values, prefix string, flat bool, memberName string) *Array {
+	emptyValue := newValue(values, prefix, flat)
+	emptyValue.String("")
+
 	return &Array{
 		values:     values,
 		prefix:     prefix,
 		flat:       flat,
 		memberName: memberName,
+		emptyValue: emptyValue,
 	}
 }
 
 // Value adds a new element to the Query Array. Returns a Value type used to
 // encode the array element.
 func (a *Array) Value() Value {
+	if a.size == 0 {
+		delete(a.values, a.emptyValue.key)
+	}
+
 	// Query lists start a 1, so adjust the size first
 	a.size++
 	prefix := a.prefix
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md
index cffa7288a..8412c43d5 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v1.1.28 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v1.1.27 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go
index b47c6baa0..dfd4e2eea 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go
@@ -3,4 +3,4 @@
 package configsources
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.1.27"
+const goModuleVersion = "1.1.28"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md
index fb3d33ff5..7d8e325fd 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v2.4.22 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v2.4.21 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go
index e6a8286db..eb6a19509 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go
@@ -3,4 +3,4 @@
 package endpoints
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "2.4.21"
+const goModuleVersion = "2.4.22"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md
index 4abb1dcd1..e5f7cfe87 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v1.0.19 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v1.0.18 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go
index 7705c51d3..52427496d 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go
@@ -3,4 +3,4 @@
 package v4a
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.0.18"
+const goModuleVersion = "1.0.19"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/CHANGELOG.md
index e9633091d..aa1ff0245 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v1.1.23 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v1.1.22 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/go_module_metadata.go
index 63566f4d8..84d162ee5 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/checksum/go_module_metadata.go
@@ -3,4 +3,4 @@
 package checksum
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.1.22"
+const goModuleVersion = "1.1.23"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md
index ae9ae243f..37d72a51e 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v1.9.22 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v1.9.21 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go
index c49853b92..494ae82fc 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go
@@ -3,4 +3,4 @@
 package presignedurl
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.9.21"
+const goModuleVersion = "1.9.22"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/CHANGELOG.md
index 0371ccf08..484bbcb44 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v1.13.22 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
 # v1.13.21 (2022-12-15)
 
 * **Dependency Update**: Updated to the latest SDK module versions
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/go_module_metadata.go
index 3737b1bd2..a595d6a60 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/s3shared/go_module_metadata.go
@@ -3,4 +3,4 @@
 package s3shared
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.13.21"
+const goModuleVersion = "1.13.22"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/CHANGELOG.md
index cab26896f..ba905de18 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/CHANGELOG.md
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/CHANGELOG.md
@@ -1,3 +1,11 @@
+# v1.30.2 (2023-02-03)
+
+* **Dependency Update**: Updated to the latest SDK module versions
+
+# v1.30.1 (2023-01-23)
+
+* No change notes available for this release.
+
 # v1.30.0 (2023-01-05)
 
 * **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401).
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/go_module_metadata.go
index 21dcc59c2..44587c175 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/go_module_metadata.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/go_module_metadata.go
@@ -3,4 +3,4 @@
 package s3
 
 // goModuleVersion is the tagged release for this module
-const goModuleVersion = "1.30.0"
+const goModuleVersion = "1.30.2"
diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/internal/endpoints/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/internal/endpoints/endpoints.go
index 5fca8c1d4..dc0215c99 100644
--- a/vendor/github.com/aws/aws-sdk-go-v2/service/s3/internal/endpoints/endpoints.go
+++ b/vendor/github.com/aws/aws-sdk-go-v2/service/s3/internal/endpoints/endpoints.go
@@ -239,6 +239,15 @@ var defaultPartitions = endpoints.Partitions{
 			}: {
 				Hostname: "s3.dualstack.ap-southeast-3.amazonaws.com",
 			},
+			endpoints.EndpointKey{
+				Region: "ap-southeast-4",
+			}: endpoints.Endpoint{},
+			endpoints.EndpointKey{
+				Region:  "ap-southeast-4",
+				Variant: endpoints.DualStackVariant,
+			}: {
+				Hostname: "s3.dualstack.ap-southeast-4.amazonaws.com",
+			},
 			endpoints.EndpointKey{
 				Region: "aws-global",
 			}: endpoints.Endpoint{
diff --git a/vendor/github.com/go-sql-driver/mysql/.travis.yml b/vendor/github.com/go-sql-driver/mysql/.travis.yml
deleted file mode 100644
index 56fcf25f2..000000000
--- a/vendor/github.com/go-sql-driver/mysql/.travis.yml
+++ /dev/null
@@ -1,129 +0,0 @@
-sudo: false
-language: go
-go:
-  - 1.10.x
-  - 1.11.x
-  - 1.12.x
-  - 1.13.x
-  - master
-
-before_install:
-  - go get golang.org/x/tools/cmd/cover
-  - go get github.com/mattn/goveralls
-
-before_script:
-  - echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
-  - sudo service mysql restart
-  - .travis/wait_mysql.sh
-  - mysql -e 'create database gotest;'
-
-matrix:
-  include:
-    - env: DB=MYSQL8
-      sudo: required
-      dist: trusty
-      go: 1.10.x
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mysql:8.0
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mysql:8.0 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
-        - cp .travis/docker.cnf ~/.my.cnf
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - env: DB=MYSQL57
-      sudo: required
-      dist: trusty
-      go: 1.10.x
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mysql:5.7
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
-        - cp .travis/docker.cnf ~/.my.cnf
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - env: DB=MARIA55
-      sudo: required
-      dist: trusty
-      go: 1.10.x
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mariadb:5.5
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
-        - cp .travis/docker.cnf ~/.my.cnf
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - env: DB=MARIA10_1
-      sudo: required
-      dist: trusty
-      go: 1.10.x
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mariadb:10.1
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
-        - cp .travis/docker.cnf ~/.my.cnf
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - os: osx
-      osx_image: xcode10.1
-      addons:
-        homebrew:
-          packages:
-            - mysql
-          update: true
-      go: 1.12.x
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-      before_script:
-        - echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB\nlocal_infile=1" >> /usr/local/etc/my.cnf
-        - mysql.server start
-        - mysql -uroot -e 'CREATE USER gotest IDENTIFIED BY "secret"'
-        - mysql -uroot -e 'GRANT ALL ON *.* TO gotest'
-        - mysql -uroot -e 'create database gotest;'
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3306
-        - export MYSQL_TEST_CONCURRENT=1
-
-script:
-  - go test -v -covermode=count -coverprofile=coverage.out
-  - go vet ./...
-  - .travis/gofmt.sh
-after_script:
-  - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci
diff --git a/vendor/github.com/go-sql-driver/mysql/AUTHORS b/vendor/github.com/go-sql-driver/mysql/AUTHORS
index ad5989800..051327519 100644
--- a/vendor/github.com/go-sql-driver/mysql/AUTHORS
+++ b/vendor/github.com/go-sql-driver/mysql/AUTHORS
@@ -13,12 +13,17 @@
 
 Aaron Hopkins <go-sql-driver at die.net>
 Achille Roussel <achille.roussel at gmail.com>
+Alex Snast <alexsn at fb.com>
 Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
 Andrew Reid <andrew.reid at tixtrack.com>
+Animesh Ray <mail.rayanimesh at gmail.com>
 Arne Hormann <arnehormann at gmail.com>
+Ariel Mashraki <ariel at mashraki.co.il>
 Asta Xie <xiemengjun at gmail.com>
 Bulat Gaifullin <gaifullinbf at gmail.com>
+Caine Jette <jette at alum.mit.edu>
 Carlos Nieto <jose.carlos at menteslibres.net>
+Chris Kirkland <chriskirkland at github.com>
 Chris Moos <chris at tech9computers.com>
 Craig Wilson <craiggwilson at gmail.com>
 Daniel Montoya <dsmontoyam at gmail.com>
@@ -41,6 +46,7 @@ Ilia Cimpoes <ichimpoesh at gmail.com>
 INADA Naoki <songofacandy at gmail.com>
 Jacek Szwec <szwec.jacek at gmail.com>
 James Harr <james.harr at gmail.com>
+Janek Vedock <janekvedock at comcast.net>
 Jeff Hodges <jeff at somethingsimilar.com>
 Jeffrey Charles <jeffreycharles at gmail.com>
 Jerome Meyer <jxmeyer at gmail.com>
@@ -52,14 +58,17 @@ Julien Schmidt <go-sql-driver at julienschmidt.com>
 Justin Li <jli at j-li.net>
 Justin Nuß <nuss.justin at gmail.com>
 Kamil Dziedzic <kamil at klecza.pl>
+Kei Kamikawa <x00.x7f.x86 at gmail.com>
 Kevin Malachowski <kevin at chowski.com>
 Kieron Woodhouse <kieron.woodhouse at infosum.com>
+Lance Tian <lance6716 at gmail.com>
 Lennart Rudolph <lrudolph at hmc.edu>
 Leonardo YongUk Kim <dalinaum at gmail.com>
 Linh Tran Tuan <linhduonggnu at gmail.com>
 Lion Yang <lion at aosc.xyz>
 Luca Looz <luca.looz92 at gmail.com>
 Lucas Liu <extrafliu at gmail.com>
+Lunny Xiao <xiaolunwen at gmail.com>
 Luke Scott <luke at webconnex.com>
 Maciej Zimnoch <maciej.zimnoch at codilime.com>
 Michael Woolnough <michael.woolnough at gmail.com>
@@ -74,26 +83,35 @@ Reed Allman <rdallman10 at gmail.com>
 Richard Wilkes <wilkes at me.com>
 Robert Russell <robert at rrbrussell.com>
 Runrioter Wung <runrioter at gmail.com>
+Santhosh Kumar Tekuri <santhosh.tekuri at gmail.com>
+Sho Iizuka <sho.i518 at gmail.com>
+Sho Ikeda <suicaicoca at gmail.com>
 Shuode Li <elemount at qq.com>
 Simon J Mudd <sjmudd at pobox.com>
 Soroush Pour <me at soroushjp.com>
 Stan Putrya <root.vagner at gmail.com>
 Stanley Gunawan <gunawan.stanley at gmail.com>
 Steven Hartland <steven.hartland at multiplay.co.uk>
+Tan Jinhua <312841925 at qq.com>
 Thomas Wodarek <wodarekwebpage at gmail.com>
 Tim Ruffles <timruffles at gmail.com>
 Tom Jenkinson <tom at tjenkinson.me>
 Vladimir Kovpak <cn007b at gmail.com>
+Vladyslav Zhelezniak <zhvladi at gmail.com>
 Xiangyu Hu <xiangyu.hu at outlook.com>
 Xiaobing Jiang <s7v7nislands at gmail.com>
 Xiuming Chen <cc at cxm.cc>
+Xuehong Chan <chanxuehong at gmail.com>
 Zhenye Xie <xiezhenye at gmail.com>
+Zhixin Wen <john.wenzhixin at gmail.com>
+Ziheng Lyu <zihenglv at gmail.com>
 
 # Organizations
 
 Barracuda Networks, Inc.
 Counting Ltd.
 DigitalOcean Inc.
+dyves labs AG
 Facebook Inc.
 GitHub Inc.
 Google Inc.
@@ -103,3 +121,4 @@ Multiplay Ltd.
 Percona LLC
 Pivotal Inc.
 Stripe Inc.
+Zendesk Inc.
diff --git a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
index 9cb97b38d..77024a820 100644
--- a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
+++ b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
@@ -1,3 +1,50 @@
+## Version 1.7 (2022-11-29)
+
+Changes:
+
+  - Drop support of Go 1.12 (#1211)
+  - Refactoring `(*textRows).readRow` in a more clear way (#1230)
+  - util: Reduce boundary check in escape functions. (#1316)
+  - enhancement for mysqlConn handleAuthResult (#1250)
+
+New Features:
+
+  - support Is comparison on MySQLError (#1210)
+  - return unsigned in database type name when necessary (#1238)
+  - Add API to express like a --ssl-mode=PREFERRED MySQL client (#1370)
+  - Add SQLState to MySQLError (#1321)
+
+Bugfixes:
+
+  -  Fix parsing 0 year. (#1257)
+
+
+## Version 1.6 (2021-04-01)
+
+Changes:
+
+  - Migrate the CI service from travis-ci to GitHub Actions (#1176, #1183, #1190)
+  - `NullTime` is deprecated (#960, #1144)
+  - Reduce allocations when building SET command (#1111)
+  - Performance improvement for time formatting (#1118)
+  - Performance improvement for time parsing (#1098, #1113)
+
+New Features:
+
+  - Implement `driver.Validator` interface (#1106, #1174)
+  - Support returning `uint64` from `Valuer` in `ConvertValue` (#1143)
+  - Add `json.RawMessage` for converter and prepared statement (#1059)
+  - Interpolate `json.RawMessage` as `string` (#1058)
+  - Implements `CheckNamedValue` (#1090)
+
+Bugfixes:
+
+  - Stop rounding times (#1121, #1172)
+  - Put zero filler into the SSL handshake packet (#1066)
+  - Fix checking cancelled connections back into the connection pool (#1095)
+  - Fix remove last 0 byte for mysql_old_password when password is empty (#1133)
+
+
 ## Version 1.5 (2020-01-07)
 
 Changes:
diff --git a/vendor/github.com/go-sql-driver/mysql/README.md b/vendor/github.com/go-sql-driver/mysql/README.md
index d2627a41a..25de2e5aa 100644
--- a/vendor/github.com/go-sql-driver/mysql/README.md
+++ b/vendor/github.com/go-sql-driver/mysql/README.md
@@ -35,12 +35,12 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
   * Supports queries larger than 16MB
   * Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
   * Intelligent `LONG DATA` handling in prepared statements
-  * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
+  * Secure `LOAD DATA LOCAL INFILE` support with file allowlisting and `io.Reader` support
   * Optional `time.Time` parsing
   * Optional placeholder interpolation
 
 ## Requirements
-  * Go 1.10 or higher. We aim to support the 3 latest versions of Go.
+  * Go 1.13 or higher. We aim to support the 3 latest versions of Go.
   * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
 
 ---------------------------------------
@@ -56,15 +56,37 @@ Make sure [Git is installed](https://git-scm.com/downloads) on your machine and
 _Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
 
 Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name)  as `dataSourceName`:
+
 ```go
-import "database/sql"
-import _ "github.com/go-sql-driver/mysql"
+import (
+	"database/sql"
+	"time"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+// ...
 
 db, err := sql.Open("mysql", "user:password@/dbname")
+if err != nil {
+	panic(err)
+}
+// See "Important settings" section.
+db.SetConnMaxLifetime(time.Minute * 3)
+db.SetMaxOpenConns(10)
+db.SetMaxIdleConns(10)
 ```
 
 [Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
 
+### Important settings
+
+`db.SetConnMaxLifetime()` is required to ensure connections are closed by the driver safely before connection is closed by MySQL server, OS, or other middlewares. Since some middlewares close idle connections by 5 minutes, we recommend timeout shorter than 5 minutes. This setting helps load balancing and changing system variables too.
+
+`db.SetMaxOpenConns()` is highly recommended to limit the number of connection used by the application. There is no recommended limit number because it depends on application and MySQL server.
+
+`db.SetMaxIdleConns()` is recommended to be set same to `db.SetMaxOpenConns()`. When it is smaller than `SetMaxOpenConns()`, connections can be opened and closed much more frequently than you expect. Idle connections can be closed by the `db.SetConnMaxLifetime()`. If you want to close idle connections more rapidly, you can use `db.SetConnMaxIdleTime()` since Go 1.15.
+
 
 ### DSN (Data Source Name)
 
@@ -122,7 +144,7 @@ Valid Values:   true, false
 Default:        false
 ```
 
-`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files.
+`allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files.
 [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
 
 ##### `allowCleartextPasswords`
@@ -133,7 +155,18 @@ Valid Values:   true, false
 Default:        false
 ```
 
-`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
+`allowCleartextPasswords=true` allows using the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
+
+
+##### `allowFallbackToPlaintext`
+
+```
+Type:           bool
+Valid Values:   true, false
+Default:        false
+```
+
+`allowFallbackToPlaintext=true` acts like a `--ssl-mode=PREFERRED` MySQL client as described in [Command Options for Connecting to the Server](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode)
 
 ##### `allowNativePasswords`
 
@@ -230,7 +263,7 @@ Default:        false
 
 If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
 
-*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
+*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are rejected as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
 
 ##### `loc`
 
@@ -376,7 +409,7 @@ Rules:
 Examples:
   * `autocommit=1`: `SET autocommit=1`
   * [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
-  * [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'`
+  * [`transaction_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation): `SET transaction_isolation='REPEATABLE-READ'`
 
 
 #### Examples
@@ -432,7 +465,7 @@ user:password@/
 The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
 
 ## `ColumnType` Support
-This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported.
+This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `BIGINT`.
 
 ## `context.Context` Support
 Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
@@ -445,7 +478,7 @@ For this feature you need direct access to the package. Therefore you must chang
 import "github.com/go-sql-driver/mysql"
 ```
 
-Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
+Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
 
 To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
 
@@ -459,8 +492,6 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v
 
 **Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
 
-Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
-
 
 ### Unicode support
 Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
@@ -477,7 +508,7 @@ To run the driver tests you may need to adjust the configuration. See the [Testi
 Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
 If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
 
-See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details.
+See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/.github/CONTRIBUTING.md) for details.
 
 ---------------------------------------
 
@@ -498,4 +529,3 @@ Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you
 You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
 
 ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
-
diff --git a/vendor/github.com/go-sql-driver/mysql/atomic_bool.go b/vendor/github.com/go-sql-driver/mysql/atomic_bool.go
new file mode 100644
index 000000000..1b7e19f3e
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/atomic_bool.go
@@ -0,0 +1,19 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
+//
+// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+//go:build go1.19
+// +build go1.19
+
+package mysql
+
+import "sync/atomic"
+
+/******************************************************************************
+*                               Sync utils                                    *
+******************************************************************************/
+
+type atomicBool = atomic.Bool
diff --git a/vendor/github.com/go-sql-driver/mysql/atomic_bool_go118.go b/vendor/github.com/go-sql-driver/mysql/atomic_bool_go118.go
new file mode 100644
index 000000000..2e9a7f0b6
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/atomic_bool_go118.go
@@ -0,0 +1,47 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
+//
+// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+//go:build !go1.19
+// +build !go1.19
+
+package mysql
+
+import "sync/atomic"
+
+/******************************************************************************
+*                               Sync utils                                    *
+******************************************************************************/
+
+// atomicBool is an implementation of atomic.Bool for older version of Go.
+// it is a wrapper around uint32 for usage as a boolean value with
+// atomic access.
+type atomicBool struct {
+	_     noCopy
+	value uint32
+}
+
+// Load returns whether the current boolean value is true
+func (ab *atomicBool) Load() bool {
+	return atomic.LoadUint32(&ab.value) > 0
+}
+
+// Store sets the value of the bool regardless of the previous value
+func (ab *atomicBool) Store(value bool) {
+	if value {
+		atomic.StoreUint32(&ab.value, 1)
+	} else {
+		atomic.StoreUint32(&ab.value, 0)
+	}
+}
+
+// Swap sets the value of the bool and returns the old value.
+func (ab *atomicBool) Swap(value bool) bool {
+	if value {
+		return atomic.SwapUint32(&ab.value, 1) > 0
+	}
+	return atomic.SwapUint32(&ab.value, 0) > 0
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/auth.go b/vendor/github.com/go-sql-driver/mysql/auth.go
index fec7040d4..1ff203e57 100644
--- a/vendor/github.com/go-sql-driver/mysql/auth.go
+++ b/vendor/github.com/go-sql-driver/mysql/auth.go
@@ -15,6 +15,7 @@ import (
 	"crypto/sha256"
 	"crypto/x509"
 	"encoding/pem"
+	"fmt"
 	"sync"
 )
 
@@ -32,27 +33,26 @@ var (
 // Note: The provided rsa.PublicKey instance is exclusively owned by the driver
 // after registering it and may not be modified.
 //
-//  data, err := ioutil.ReadFile("mykey.pem")
-//  if err != nil {
-//  	log.Fatal(err)
-//  }
+//	data, err := ioutil.ReadFile("mykey.pem")
+//	if err != nil {
+//		log.Fatal(err)
+//	}
 //
-//  block, _ := pem.Decode(data)
-//  if block == nil || block.Type != "PUBLIC KEY" {
-//  	log.Fatal("failed to decode PEM block containing public key")
-//  }
+//	block, _ := pem.Decode(data)
+//	if block == nil || block.Type != "PUBLIC KEY" {
+//		log.Fatal("failed to decode PEM block containing public key")
+//	}
 //
-//  pub, err := x509.ParsePKIXPublicKey(block.Bytes)
-//  if err != nil {
-//  	log.Fatal(err)
-//  }
-//
-//  if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
-//  	mysql.RegisterServerPubKey("mykey", rsaPubKey)
-//  } else {
-//  	log.Fatal("not a RSA public key")
-//  }
+//	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
+//	if err != nil {
+//		log.Fatal(err)
+//	}
 //
+//	if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
+//		mysql.RegisterServerPubKey("mykey", rsaPubKey)
+//	} else {
+//		log.Fatal("not a RSA public key")
+//	}
 func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) {
 	serverPubKeyLock.Lock()
 	if serverPubKeyRegistry == nil {
@@ -136,10 +136,6 @@ func pwHash(password []byte) (result [2]uint32) {
 
 // Hash password using insecure pre 4.1 method
 func scrambleOldPassword(scramble []byte, password string) []byte {
-	if len(password) == 0 {
-		return nil
-	}
-
 	scramble = scramble[:8]
 
 	hashPw := pwHash([]byte(password))
@@ -247,6 +243,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
 		if !mc.cfg.AllowOldPasswords {
 			return nil, ErrOldPassword
 		}
+		if len(mc.cfg.Passwd) == 0 {
+			return nil, nil
+		}
 		// Note: there are edge cases where this should work but doesn't;
 		// this is currently "wontfix":
 		// https://github.com/go-sql-driver/mysql/issues/184
@@ -274,7 +273,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
 		if len(mc.cfg.Passwd) == 0 {
 			return []byte{0}, nil
 		}
-		if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
+		// unlike caching_sha2_password, sha256_password does not accept
+		// cleartext password on unix transport.
+		if mc.cfg.TLS != nil {
 			// write cleartext auth packet
 			return append([]byte(mc.cfg.Passwd), 0), nil
 		}
@@ -350,7 +351,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
 				}
 
 			case cachingSha2PasswordPerformFullAuthentication:
-				if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
+				if mc.cfg.TLS != nil || mc.cfg.Net == "unix" {
 					// write cleartext auth packet
 					err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
 					if err != nil {
@@ -365,14 +366,24 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
 							return err
 						}
 						data[4] = cachingSha2PasswordRequestPublicKey
-						mc.writePacket(data)
+						err = mc.writePacket(data)
+						if err != nil {
+							return err
+						}
 
-						// parse public key
 						if data, err = mc.readPacket(); err != nil {
 							return err
 						}
 
-						block, _ := pem.Decode(data[1:])
+						if data[0] != iAuthMoreData {
+							return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication")
+						}
+
+						// parse public key
+						block, rest := pem.Decode(data[1:])
+						if block == nil {
+							return fmt.Errorf("No Pem data found, data: %s", rest)
+						}
 						pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
 						if err != nil {
 							return err
@@ -401,6 +412,10 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
 			return nil // auth successful
 		default:
 			block, _ := pem.Decode(authData)
+			if block == nil {
+				return fmt.Errorf("no Pem data found, data: %s", authData)
+			}
+
 			pub, err := x509.ParsePKIXPublicKey(block.Bytes)
 			if err != nil {
 				return err
diff --git a/vendor/github.com/go-sql-driver/mysql/collations.go b/vendor/github.com/go-sql-driver/mysql/collations.go
index 8d2b55676..295bfbe52 100644
--- a/vendor/github.com/go-sql-driver/mysql/collations.go
+++ b/vendor/github.com/go-sql-driver/mysql/collations.go
@@ -13,7 +13,8 @@ const binaryCollation = "binary"
 
 // A list of available collations mapped to the internal ID.
 // To update this map use the following MySQL query:
-//     SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID
+//
+//	SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID
 //
 // Handshake packet have only 1 byte for collation_id.  So we can't use collations with ID > 255.
 //
@@ -247,7 +248,7 @@ var collations = map[string]byte{
 	"utf8mb4_0900_ai_ci":       255,
 }
 
-// A blacklist of collations which is unsafe to interpolate parameters.
+// A denylist of collations which is unsafe to interpolate parameters.
 // These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
 var unsafeCollations = map[string]bool{
 	"big5_chinese_ci":        true,
diff --git a/vendor/github.com/go-sql-driver/mysql/conncheck.go b/vendor/github.com/go-sql-driver/mysql/conncheck.go
index 024eb2858..0ea721720 100644
--- a/vendor/github.com/go-sql-driver/mysql/conncheck.go
+++ b/vendor/github.com/go-sql-driver/mysql/conncheck.go
@@ -6,6 +6,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
+//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
 // +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
 
 package mysql
diff --git a/vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go b/vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
index ea7fb607a..a56c138f2 100644
--- a/vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
+++ b/vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
@@ -6,6 +6,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
+//go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos
 // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
 
 package mysql
diff --git a/vendor/github.com/go-sql-driver/mysql/connection.go b/vendor/github.com/go-sql-driver/mysql/connection.go
index e4bb59e67..9539077cb 100644
--- a/vendor/github.com/go-sql-driver/mysql/connection.go
+++ b/vendor/github.com/go-sql-driver/mysql/connection.go
@@ -12,6 +12,7 @@ import (
 	"context"
 	"database/sql"
 	"database/sql/driver"
+	"encoding/json"
 	"io"
 	"net"
 	"strconv"
@@ -46,9 +47,10 @@ type mysqlConn struct {
 
 // Handles parameters set in DSN after the connection is established
 func (mc *mysqlConn) handleParams() (err error) {
+	var cmdSet strings.Builder
 	for param, val := range mc.cfg.Params {
 		switch param {
-		// Charset
+		// Charset: character_set_connection, character_set_client, character_set_results
 		case "charset":
 			charsets := strings.Split(val, ",")
 			for i := range charsets {
@@ -62,12 +64,25 @@ func (mc *mysqlConn) handleParams() (err error) {
 				return
 			}
 
-		// System Vars
+		// Other system vars accumulated in a single SET command
 		default:
-			err = mc.exec("SET " + param + "=" + val + "")
-			if err != nil {
-				return
+			if cmdSet.Len() == 0 {
+				// Heuristic: 29 chars for each other key=value to reduce reallocations
+				cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1))
+				cmdSet.WriteString("SET ")
+			} else {
+				cmdSet.WriteByte(',')
 			}
+			cmdSet.WriteString(param)
+			cmdSet.WriteByte('=')
+			cmdSet.WriteString(val)
+		}
+	}
+
+	if cmdSet.Len() > 0 {
+		err = mc.exec(cmdSet.String())
+		if err != nil {
+			return
 		}
 	}
 
@@ -89,7 +104,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
 }
 
 func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -108,7 +123,7 @@ func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
 
 func (mc *mysqlConn) Close() (err error) {
 	// Makes Close idempotent
-	if !mc.closed.IsSet() {
+	if !mc.closed.Load() {
 		err = mc.writeCommandPacket(comQuit)
 	}
 
@@ -122,7 +137,7 @@ func (mc *mysqlConn) Close() (err error) {
 // is called before auth or on auth failure because MySQL will have already
 // closed the network connection.
 func (mc *mysqlConn) cleanup() {
-	if !mc.closed.TrySet(true) {
+	if mc.closed.Swap(true) {
 		return
 	}
 
@@ -137,7 +152,7 @@ func (mc *mysqlConn) cleanup() {
 }
 
 func (mc *mysqlConn) error() error {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		if err := mc.canceled.Value(); err != nil {
 			return err
 		}
@@ -147,7 +162,7 @@ func (mc *mysqlConn) error() error {
 }
 
 func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -230,47 +245,21 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 			if v.IsZero() {
 				buf = append(buf, "'0000-00-00'"...)
 			} else {
-				v := v.In(mc.cfg.Loc)
-				v = v.Add(time.Nanosecond * 500) // To round under microsecond
-				year := v.Year()
-				year100 := year / 100
-				year1 := year % 100
-				month := v.Month()
-				day := v.Day()
-				hour := v.Hour()
-				minute := v.Minute()
-				second := v.Second()
-				micro := v.Nanosecond() / 1000
-
-				buf = append(buf, []byte{
-					'\'',
-					digits10[year100], digits01[year100],
-					digits10[year1], digits01[year1],
-					'-',
-					digits10[month], digits01[month],
-					'-',
-					digits10[day], digits01[day],
-					' ',
-					digits10[hour], digits01[hour],
-					':',
-					digits10[minute], digits01[minute],
-					':',
-					digits10[second], digits01[second],
-				}...)
-
-				if micro != 0 {
-					micro10000 := micro / 10000
-					micro100 := micro / 100 % 100
-					micro1 := micro % 100
-					buf = append(buf, []byte{
-						'.',
-						digits10[micro10000], digits01[micro10000],
-						digits10[micro100], digits01[micro100],
-						digits10[micro1], digits01[micro1],
-					}...)
+				buf = append(buf, '\'')
+				buf, err = appendDateTime(buf, v.In(mc.cfg.Loc))
+				if err != nil {
+					return "", err
 				}
 				buf = append(buf, '\'')
 			}
+		case json.RawMessage:
+			buf = append(buf, '\'')
+			if mc.status&statusNoBackslashEscapes == 0 {
+				buf = escapeBytesBackslash(buf, v)
+			} else {
+				buf = escapeBytesQuotes(buf, v)
+			}
+			buf = append(buf, '\'')
 		case []byte:
 			if v == nil {
 				buf = append(buf, "NULL"...)
@@ -306,7 +295,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 }
 
 func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -367,7 +356,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
 }
 
 func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -461,7 +450,7 @@ func (mc *mysqlConn) finish() {
 
 // Ping implements driver.Pinger interface
 func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return driver.ErrBadConn
 	}
@@ -480,6 +469,10 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
 
 // BeginTx implements driver.ConnBeginTx interface
 func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
+	if mc.closed.Load() {
+		return nil, driver.ErrBadConn
+	}
+
 	if err := mc.watchCancel(ctx); err != nil {
 		return nil, err
 	}
@@ -643,9 +636,15 @@ func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
 // ResetSession implements driver.SessionResetter.
 // (From Go 1.10)
 func (mc *mysqlConn) ResetSession(ctx context.Context) error {
-	if mc.closed.IsSet() {
+	if mc.closed.Load() {
 		return driver.ErrBadConn
 	}
 	mc.reset = true
 	return nil
 }
+
+// IsValid implements driver.Validator interface
+// (From Go 1.15)
+func (mc *mysqlConn) IsValid() bool {
+	return !mc.closed.Load()
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/driver.go b/vendor/github.com/go-sql-driver/mysql/driver.go
index c1bdf1199..ad7aec215 100644
--- a/vendor/github.com/go-sql-driver/mysql/driver.go
+++ b/vendor/github.com/go-sql-driver/mysql/driver.go
@@ -8,10 +8,10 @@
 //
 // The driver should be used via the database/sql package:
 //
-//  import "database/sql"
-//  import _ "github.com/go-sql-driver/mysql"
+//	import "database/sql"
+//	import _ "github.com/go-sql-driver/mysql"
 //
-//  db, err := sql.Open("mysql", "user:password@/dbname")
+//	db, err := sql.Open("mysql", "user:password@/dbname")
 //
 // See https://github.com/go-sql-driver/mysql#usage for details
 package mysql
diff --git a/vendor/github.com/go-sql-driver/mysql/dsn.go b/vendor/github.com/go-sql-driver/mysql/dsn.go
index 75c8c2489..4b71aaab0 100644
--- a/vendor/github.com/go-sql-driver/mysql/dsn.go
+++ b/vendor/github.com/go-sql-driver/mysql/dsn.go
@@ -46,22 +46,23 @@ type Config struct {
 	ServerPubKey     string            // Server public key name
 	pubKey           *rsa.PublicKey    // Server public key
 	TLSConfig        string            // TLS configuration name
-	tls              *tls.Config       // TLS configuration
+	TLS              *tls.Config       // TLS configuration, its priority is higher than TLSConfig
 	Timeout          time.Duration     // Dial timeout
 	ReadTimeout      time.Duration     // I/O read timeout
 	WriteTimeout     time.Duration     // I/O write timeout
 
-	AllowAllFiles           bool // Allow all files to be used with LOAD DATA LOCAL INFILE
-	AllowCleartextPasswords bool // Allows the cleartext client side plugin
-	AllowNativePasswords    bool // Allows the native password authentication method
-	AllowOldPasswords       bool // Allows the old insecure password method
-	CheckConnLiveness       bool // Check connections for liveness before using them
-	ClientFoundRows         bool // Return number of matching rows instead of rows changed
-	ColumnsWithAlias        bool // Prepend table alias to column names
-	InterpolateParams       bool // Interpolate placeholders into query string
-	MultiStatements         bool // Allow multiple statements in one query
-	ParseTime               bool // Parse time values to time.Time
-	RejectReadOnly          bool // Reject read-only connections
+	AllowAllFiles            bool // Allow all files to be used with LOAD DATA LOCAL INFILE
+	AllowCleartextPasswords  bool // Allows the cleartext client side plugin
+	AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
+	AllowNativePasswords     bool // Allows the native password authentication method
+	AllowOldPasswords        bool // Allows the old insecure password method
+	CheckConnLiveness        bool // Check connections for liveness before using them
+	ClientFoundRows          bool // Return number of matching rows instead of rows changed
+	ColumnsWithAlias         bool // Prepend table alias to column names
+	InterpolateParams        bool // Interpolate placeholders into query string
+	MultiStatements          bool // Allow multiple statements in one query
+	ParseTime                bool // Parse time values to time.Time
+	RejectReadOnly           bool // Reject read-only connections
 }
 
 // NewConfig creates a new Config and sets default values.
@@ -77,8 +78,8 @@ func NewConfig() *Config {
 
 func (cfg *Config) Clone() *Config {
 	cp := *cfg
-	if cp.tls != nil {
-		cp.tls = cfg.tls.Clone()
+	if cp.TLS != nil {
+		cp.TLS = cfg.TLS.Clone()
 	}
 	if len(cp.Params) > 0 {
 		cp.Params = make(map[string]string, len(cfg.Params))
@@ -119,24 +120,29 @@ func (cfg *Config) normalize() error {
 		cfg.Addr = ensureHavePort(cfg.Addr)
 	}
 
-	switch cfg.TLSConfig {
-	case "false", "":
-		// don't set anything
-	case "true":
-		cfg.tls = &tls.Config{}
-	case "skip-verify", "preferred":
-		cfg.tls = &tls.Config{InsecureSkipVerify: true}
-	default:
-		cfg.tls = getTLSConfigClone(cfg.TLSConfig)
-		if cfg.tls == nil {
-			return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
+	if cfg.TLS == nil {
+		switch cfg.TLSConfig {
+		case "false", "":
+			// don't set anything
+		case "true":
+			cfg.TLS = &tls.Config{}
+		case "skip-verify":
+			cfg.TLS = &tls.Config{InsecureSkipVerify: true}
+		case "preferred":
+			cfg.TLS = &tls.Config{InsecureSkipVerify: true}
+			cfg.AllowFallbackToPlaintext = true
+		default:
+			cfg.TLS = getTLSConfigClone(cfg.TLSConfig)
+			if cfg.TLS == nil {
+				return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
+			}
 		}
 	}
 
-	if cfg.tls != nil && cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify {
+	if cfg.TLS != nil && cfg.TLS.ServerName == "" && !cfg.TLS.InsecureSkipVerify {
 		host, _, err := net.SplitHostPort(cfg.Addr)
 		if err == nil {
-			cfg.tls.ServerName = host
+			cfg.TLS.ServerName = host
 		}
 	}
 
@@ -204,6 +210,10 @@ func (cfg *Config) FormatDSN() string {
 		writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true")
 	}
 
+	if cfg.AllowFallbackToPlaintext {
+		writeDSNParam(&buf, &hasParam, "allowFallbackToPlaintext", "true")
+	}
+
 	if !cfg.AllowNativePasswords {
 		writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false")
 	}
@@ -375,7 +385,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 		// cfg params
 		switch value := param[1]; param[0] {
-		// Disable INFILE whitelist / enable all files
+		// Disable INFILE allowlist / enable all files
 		case "allowAllFiles":
 			var isBool bool
 			cfg.AllowAllFiles, isBool = readBool(value)
@@ -391,6 +401,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				return errors.New("invalid bool value: " + value)
 			}
 
+		// Allow fallback to unencrypted connection if server does not support TLS
+		case "allowFallbackToPlaintext":
+			var isBool bool
+			cfg.AllowFallbackToPlaintext, isBool = readBool(value)
+			if !isBool {
+				return errors.New("invalid bool value: " + value)
+			}
+
 		// Use native password authentication
 		case "allowNativePasswords":
 			var isBool bool
@@ -426,7 +444,6 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 		// Collation
 		case "collation":
 			cfg.Collation = value
-			break
 
 		case "columnsWithAlias":
 			var isBool bool
diff --git a/vendor/github.com/go-sql-driver/mysql/errors.go b/vendor/github.com/go-sql-driver/mysql/errors.go
index 760782ff2..7c037e7d6 100644
--- a/vendor/github.com/go-sql-driver/mysql/errors.go
+++ b/vendor/github.com/go-sql-driver/mysql/errors.go
@@ -56,10 +56,22 @@ func SetLogger(logger Logger) error {
 
 // MySQLError is an error type which represents a single MySQL error
 type MySQLError struct {
-	Number  uint16
-	Message string
+	Number   uint16
+	SQLState [5]byte
+	Message  string
 }
 
 func (me *MySQLError) Error() string {
+	if me.SQLState != [5]byte{} {
+		return fmt.Sprintf("Error %d (%s): %s", me.Number, me.SQLState, me.Message)
+	}
+
 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
 }
+
+func (me *MySQLError) Is(err error) bool {
+	if merr, ok := err.(*MySQLError); ok {
+		return merr.Number == me.Number
+	}
+	return false
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/fields.go b/vendor/github.com/go-sql-driver/mysql/fields.go
index e1e2ece4b..e0654a83d 100644
--- a/vendor/github.com/go-sql-driver/mysql/fields.go
+++ b/vendor/github.com/go-sql-driver/mysql/fields.go
@@ -41,6 +41,9 @@ func (mf *mysqlField) typeDatabaseName() string {
 	case fieldTypeJSON:
 		return "JSON"
 	case fieldTypeLong:
+		if mf.flags&flagUnsigned != 0 {
+			return "UNSIGNED INT"
+		}
 		return "INT"
 	case fieldTypeLongBLOB:
 		if mf.charSet != collations[binaryCollation] {
@@ -48,6 +51,9 @@ func (mf *mysqlField) typeDatabaseName() string {
 		}
 		return "LONGBLOB"
 	case fieldTypeLongLong:
+		if mf.flags&flagUnsigned != 0 {
+			return "UNSIGNED BIGINT"
+		}
 		return "BIGINT"
 	case fieldTypeMediumBLOB:
 		if mf.charSet != collations[binaryCollation] {
@@ -63,6 +69,9 @@ func (mf *mysqlField) typeDatabaseName() string {
 	case fieldTypeSet:
 		return "SET"
 	case fieldTypeShort:
+		if mf.flags&flagUnsigned != 0 {
+			return "UNSIGNED SMALLINT"
+		}
 		return "SMALLINT"
 	case fieldTypeString:
 		if mf.charSet == collations[binaryCollation] {
@@ -74,6 +83,9 @@ func (mf *mysqlField) typeDatabaseName() string {
 	case fieldTypeTimestamp:
 		return "TIMESTAMP"
 	case fieldTypeTiny:
+		if mf.flags&flagUnsigned != 0 {
+			return "UNSIGNED TINYINT"
+		}
 		return "TINYINT"
 	case fieldTypeTinyBLOB:
 		if mf.charSet != collations[binaryCollation] {
@@ -106,7 +118,7 @@ var (
 	scanTypeInt64     = reflect.TypeOf(int64(0))
 	scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
 	scanTypeNullInt   = reflect.TypeOf(sql.NullInt64{})
-	scanTypeNullTime  = reflect.TypeOf(NullTime{})
+	scanTypeNullTime  = reflect.TypeOf(sql.NullTime{})
 	scanTypeUint8     = reflect.TypeOf(uint8(0))
 	scanTypeUint16    = reflect.TypeOf(uint16(0))
 	scanTypeUint32    = reflect.TypeOf(uint32(0))
diff --git a/vendor/github.com/go-sql-driver/mysql/fuzz.go b/vendor/github.com/go-sql-driver/mysql/fuzz.go
new file mode 100644
index 000000000..3a4ec25a9
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/fuzz.go
@@ -0,0 +1,25 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
+//
+// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+//go:build gofuzz
+// +build gofuzz
+
+package mysql
+
+import (
+	"database/sql"
+)
+
+func Fuzz(data []byte) int {
+	db, err := sql.Open("mysql", string(data))
+	if err != nil {
+		return 0
+	}
+	db.Close()
+	return 1
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/infile.go b/vendor/github.com/go-sql-driver/mysql/infile.go
index 273cb0ba5..3279dcffd 100644
--- a/vendor/github.com/go-sql-driver/mysql/infile.go
+++ b/vendor/github.com/go-sql-driver/mysql/infile.go
@@ -23,17 +23,16 @@ var (
 	readerRegisterLock sync.RWMutex
 )
 
-// RegisterLocalFile adds the given file to the file whitelist,
+// RegisterLocalFile adds the given file to the file allowlist,
 // so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
 // Alternatively you can allow the use of all local files with
 // the DSN parameter 'allowAllFiles=true'
 //
-//  filePath := "/home/gopher/data.csv"
-//  mysql.RegisterLocalFile(filePath)
-//  err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
-//  if err != nil {
-//  ...
-//
+//	filePath := "/home/gopher/data.csv"
+//	mysql.RegisterLocalFile(filePath)
+//	err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
+//	if err != nil {
+//	...
 func RegisterLocalFile(filePath string) {
 	fileRegisterLock.Lock()
 	// lazy map init
@@ -45,7 +44,7 @@ func RegisterLocalFile(filePath string) {
 	fileRegisterLock.Unlock()
 }
 
-// DeregisterLocalFile removes the given filepath from the whitelist.
+// DeregisterLocalFile removes the given filepath from the allowlist.
 func DeregisterLocalFile(filePath string) {
 	fileRegisterLock.Lock()
 	delete(fileRegister, strings.Trim(filePath, `"`))
@@ -58,15 +57,14 @@ func DeregisterLocalFile(filePath string) {
 // If the handler returns a io.ReadCloser Close() is called when the
 // request is finished.
 //
-//  mysql.RegisterReaderHandler("data", func() io.Reader {
-//  	var csvReader io.Reader // Some Reader that returns CSV data
-//  	... // Open Reader here
-//  	return csvReader
-//  })
-//  err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
-//  if err != nil {
-//  ...
-//
+//	mysql.RegisterReaderHandler("data", func() io.Reader {
+//		var csvReader io.Reader // Some Reader that returns CSV data
+//		... // Open Reader here
+//		return csvReader
+//	})
+//	err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
+//	if err != nil {
+//	...
 func RegisterReaderHandler(name string, handler func() io.Reader) {
 	readerRegisterLock.Lock()
 	// lazy map init
@@ -93,10 +91,12 @@ func deferredClose(err *error, closer io.Closer) {
 	}
 }
 
+const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
+
 func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
 	var rdr io.Reader
 	var data []byte
-	packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
+	packetSize := defaultPacketSize
 	if mc.maxWriteSize < packetSize {
 		packetSize = mc.maxWriteSize
 	}
diff --git a/vendor/github.com/go-sql-driver/mysql/nulltime.go b/vendor/github.com/go-sql-driver/mysql/nulltime.go
index afa8a89e9..36c8a42c5 100644
--- a/vendor/github.com/go-sql-driver/mysql/nulltime.go
+++ b/vendor/github.com/go-sql-driver/mysql/nulltime.go
@@ -9,11 +9,32 @@
 package mysql
 
 import (
+	"database/sql"
 	"database/sql/driver"
 	"fmt"
 	"time"
 )
 
+// NullTime represents a time.Time that may be NULL.
+// NullTime implements the Scanner interface so
+// it can be used as a scan destination:
+//
+//	var nt NullTime
+//	err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
+//	...
+//	if nt.Valid {
+//	   // use nt.Time
+//	} else {
+//	   // NULL value
+//	}
+//
+// # This NullTime implementation is not driver-specific
+//
+// Deprecated: NullTime doesn't honor the loc DSN parameter.
+// NullTime.Scan interprets a time as UTC, not the loc DSN parameter.
+// Use sql.NullTime instead.
+type NullTime sql.NullTime
+
 // Scan implements the Scanner interface.
 // The value type must be time.Time or string / []byte (formatted time-string),
 // otherwise Scan fails.
@@ -28,11 +49,11 @@ func (nt *NullTime) Scan(value interface{}) (err error) {
 		nt.Time, nt.Valid = v, true
 		return
 	case []byte:
-		nt.Time, err = parseDateTime(string(v), time.UTC)
+		nt.Time, err = parseDateTime(v, time.UTC)
 		nt.Valid = (err == nil)
 		return
 	case string:
-		nt.Time, err = parseDateTime(v, time.UTC)
+		nt.Time, err = parseDateTime([]byte(v), time.UTC)
 		nt.Valid = (err == nil)
 		return
 	}
diff --git a/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go b/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go
deleted file mode 100644
index c392594dd..000000000
--- a/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// +build go1.13
-
-package mysql
-
-import (
-	"database/sql"
-)
-
-// NullTime represents a time.Time that may be NULL.
-// NullTime implements the Scanner interface so
-// it can be used as a scan destination:
-//
-//  var nt NullTime
-//  err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
-//  ...
-//  if nt.Valid {
-//     // use nt.Time
-//  } else {
-//     // NULL value
-//  }
-//
-// This NullTime implementation is not driver-specific
-type NullTime sql.NullTime
diff --git a/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go b/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go
deleted file mode 100644
index 86d159d44..000000000
--- a/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// +build !go1.13
-
-package mysql
-
-import (
-	"time"
-)
-
-// NullTime represents a time.Time that may be NULL.
-// NullTime implements the Scanner interface so
-// it can be used as a scan destination:
-//
-//  var nt NullTime
-//  err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
-//  ...
-//  if nt.Valid {
-//     // use nt.Time
-//  } else {
-//     // NULL value
-//  }
-//
-// This NullTime implementation is not driver-specific
-type NullTime struct {
-	Time  time.Time
-	Valid bool // Valid is true if Time is not NULL
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/packets.go b/vendor/github.com/go-sql-driver/mysql/packets.go
index 82ad7a200..ee05c95a8 100644
--- a/vendor/github.com/go-sql-driver/mysql/packets.go
+++ b/vendor/github.com/go-sql-driver/mysql/packets.go
@@ -13,6 +13,7 @@ import (
 	"crypto/tls"
 	"database/sql/driver"
 	"encoding/binary"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -109,14 +110,13 @@ func (mc *mysqlConn) writePacket(data []byte) error {
 			conn = mc.rawConn
 		}
 		var err error
-		// If this connection has a ReadTimeout which we've been setting on
-		// reads, reset it to its default value before we attempt a non-blocking
-		// read, otherwise the scheduler will just time us out before we can read
-		if mc.cfg.ReadTimeout != 0 {
-			err = conn.SetReadDeadline(time.Time{})
-		}
-		if err == nil && mc.cfg.CheckConnLiveness {
-			err = connCheck(conn)
+		if mc.cfg.CheckConnLiveness {
+			if mc.cfg.ReadTimeout != 0 {
+				err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout))
+			}
+			if err == nil {
+				err = connCheck(conn)
+			}
 		}
 		if err != nil {
 			errLog.Print("closing bad idle connection: ", err)
@@ -222,9 +222,9 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
 	if mc.flags&clientProtocol41 == 0 {
 		return nil, "", ErrOldProtocol
 	}
-	if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
-		if mc.cfg.TLSConfig == "preferred" {
-			mc.cfg.tls = nil
+	if mc.flags&clientSSL == 0 && mc.cfg.TLS != nil {
+		if mc.cfg.AllowFallbackToPlaintext {
+			mc.cfg.TLS = nil
 		} else {
 			return nil, "", ErrNoTLS
 		}
@@ -292,7 +292,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
 	}
 
 	// To enable TLS / SSL
-	if mc.cfg.tls != nil {
+	if mc.cfg.TLS != nil {
 		clientFlags |= clientSSL
 	}
 
@@ -348,16 +348,22 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
 		return errors.New("unknown collation")
 	}
 
+	// Filler [23 bytes] (all 0x00)
+	pos := 13
+	for ; pos < 13+23; pos++ {
+		data[pos] = 0
+	}
+
 	// SSL Connection Request Packet
 	// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
-	if mc.cfg.tls != nil {
+	if mc.cfg.TLS != nil {
 		// Send TLS / SSL request packet
 		if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
 			return err
 		}
 
 		// Switch to TLS
-		tlsConn := tls.Client(mc.netConn, mc.cfg.tls)
+		tlsConn := tls.Client(mc.netConn, mc.cfg.TLS)
 		if err := tlsConn.Handshake(); err != nil {
 			return err
 		}
@@ -366,12 +372,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
 		mc.buf.nc = tlsConn
 	}
 
-	// Filler [23 bytes] (all 0x00)
-	pos := 13
-	for ; pos < 13+23; pos++ {
-		data[pos] = 0
-	}
-
 	// User [null terminated string]
 	if len(mc.cfg.User) > 0 {
 		pos += copy(data[pos:], mc.cfg.User)
@@ -587,19 +587,20 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
 		return driver.ErrBadConn
 	}
 
+	me := &MySQLError{Number: errno}
+
 	pos := 3
 
 	// SQL State [optional: # + 5bytes string]
 	if data[3] == 0x23 {
-		//sqlstate := string(data[4 : 4+5])
+		copy(me.SQLState[:], data[4:4+5])
 		pos = 9
 	}
 
 	// Error Message [string]
-	return &MySQLError{
-		Number:  errno,
-		Message: string(data[pos:]),
-	}
+	me.Message = string(data[pos:])
+
+	return me
 }
 
 func readStatus(b []byte) statusFlag {
@@ -760,40 +761,40 @@ func (rows *textRows) readRow(dest []driver.Value) error {
 	}
 
 	// RowSet Packet
-	var n int
-	var isNull bool
-	pos := 0
+	var (
+		n      int
+		isNull bool
+		pos    int = 0
+	)
 
 	for i := range dest {
 		// Read bytes and convert to string
 		dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
 		pos += n
-		if err == nil {
-			if !isNull {
-				if !mc.parseTime {
-					continue
-				} else {
-					switch rows.rs.columns[i].fieldType {
-					case fieldTypeTimestamp, fieldTypeDateTime,
-						fieldTypeDate, fieldTypeNewDate:
-						dest[i], err = parseDateTime(
-							string(dest[i].([]byte)),
-							mc.cfg.Loc,
-						)
-						if err == nil {
-							continue
-						}
-					default:
-						continue
-					}
-				}
 
-			} else {
-				dest[i] = nil
-				continue
+		if err != nil {
+			return err
+		}
+
+		if isNull {
+			dest[i] = nil
+			continue
+		}
+
+		if !mc.parseTime {
+			continue
+		}
+
+		// Parse time field
+		switch rows.rs.columns[i].fieldType {
+		case fieldTypeTimestamp,
+			fieldTypeDateTime,
+			fieldTypeDate,
+			fieldTypeNewDate:
+			if dest[i], err = parseDateTime(dest[i].([]byte), mc.cfg.Loc); err != nil {
+				return err
 			}
 		}
-		return err // err != nil
 	}
 
 	return nil
@@ -1003,6 +1004,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				continue
 			}
 
+			if v, ok := arg.(json.RawMessage); ok {
+				arg = []byte(v)
+			}
 			// cache types and values
 			switch v := arg.(type) {
 			case int64:
@@ -1112,7 +1116,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				if v.IsZero() {
 					b = append(b, "0000-00-00"...)
 				} else {
-					b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
+					b, err = appendDateTime(b, v.In(mc.cfg.Loc))
+					if err != nil {
+						return err
+					}
 				}
 
 				paramValues = appendLengthEncodedInteger(paramValues,
diff --git a/vendor/github.com/go-sql-driver/mysql/statement.go b/vendor/github.com/go-sql-driver/mysql/statement.go
index f7e370939..10ece8bd6 100644
--- a/vendor/github.com/go-sql-driver/mysql/statement.go
+++ b/vendor/github.com/go-sql-driver/mysql/statement.go
@@ -10,6 +10,7 @@ package mysql
 
 import (
 	"database/sql/driver"
+	"encoding/json"
 	"fmt"
 	"io"
 	"reflect"
@@ -22,7 +23,7 @@ type mysqlStmt struct {
 }
 
 func (stmt *mysqlStmt) Close() error {
-	if stmt.mc == nil || stmt.mc.closed.IsSet() {
+	if stmt.mc == nil || stmt.mc.closed.Load() {
 		// driver.Stmt.Close can be called more than once, thus this function
 		// has to be idempotent.
 		// See also Issue #450 and golang/go#16019.
@@ -43,8 +44,13 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
 	return converter{}
 }
 
+func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
+	nv.Value, err = converter{}.ConvertValue(nv.Value)
+	return
+}
+
 func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
-	if stmt.mc.closed.IsSet() {
+	if stmt.mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -92,7 +98,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
 }
 
 func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
-	if stmt.mc.closed.IsSet() {
+	if stmt.mc.closed.Load() {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -129,6 +135,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
 	return rows, err
 }
 
+var jsonType = reflect.TypeOf(json.RawMessage{})
+
 type converter struct{}
 
 // ConvertValue mirrors the reference/default converter in database/sql/driver
@@ -146,12 +154,17 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 		if err != nil {
 			return nil, err
 		}
-		if !driver.IsValue(sv) {
-			return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
+		if driver.IsValue(sv) {
+			return sv, nil
 		}
-		return sv, nil
+		// A value returned from the Valuer interface can be "a type handled by
+		// a database driver's NamedValueChecker interface" so we should accept
+		// uint64 here as well.
+		if u, ok := sv.(uint64); ok {
+			return u, nil
+		}
+		return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
 	}
-
 	rv := reflect.ValueOf(v)
 	switch rv.Kind() {
 	case reflect.Ptr:
@@ -170,11 +183,14 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 	case reflect.Bool:
 		return rv.Bool(), nil
 	case reflect.Slice:
-		ek := rv.Type().Elem().Kind()
-		if ek == reflect.Uint8 {
+		switch t := rv.Type(); {
+		case t == jsonType:
+			return v, nil
+		case t.Elem().Kind() == reflect.Uint8:
 			return rv.Bytes(), nil
+		default:
+			return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
 		}
-		return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
 	case reflect.String:
 		return rv.String(), nil
 	}
diff --git a/vendor/github.com/go-sql-driver/mysql/transaction.go b/vendor/github.com/go-sql-driver/mysql/transaction.go
index 417d72793..4a4b61001 100644
--- a/vendor/github.com/go-sql-driver/mysql/transaction.go
+++ b/vendor/github.com/go-sql-driver/mysql/transaction.go
@@ -13,7 +13,7 @@ type mysqlTx struct {
 }
 
 func (tx *mysqlTx) Commit() (err error) {
-	if tx.mc == nil || tx.mc.closed.IsSet() {
+	if tx.mc == nil || tx.mc.closed.Load() {
 		return ErrInvalidConn
 	}
 	err = tx.mc.exec("COMMIT")
@@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
 }
 
 func (tx *mysqlTx) Rollback() (err error) {
-	if tx.mc == nil || tx.mc.closed.IsSet() {
+	if tx.mc == nil || tx.mc.closed.Load() {
 		return ErrInvalidConn
 	}
 	err = tx.mc.exec("ROLLBACK")
diff --git a/vendor/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/go-sql-driver/mysql/utils.go
index 9552e80b5..15dbd8d16 100644
--- a/vendor/github.com/go-sql-driver/mysql/utils.go
+++ b/vendor/github.com/go-sql-driver/mysql/utils.go
@@ -35,26 +35,25 @@ var (
 // Note: The provided tls.Config is exclusively owned by the driver after
 // registering it.
 //
-//  rootCertPool := x509.NewCertPool()
-//  pem, err := ioutil.ReadFile("/path/ca-cert.pem")
-//  if err != nil {
-//      log.Fatal(err)
-//  }
-//  if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
-//      log.Fatal("Failed to append PEM.")
-//  }
-//  clientCert := make([]tls.Certificate, 0, 1)
-//  certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem")
-//  if err != nil {
-//      log.Fatal(err)
-//  }
-//  clientCert = append(clientCert, certs)
-//  mysql.RegisterTLSConfig("custom", &tls.Config{
-//      RootCAs: rootCertPool,
-//      Certificates: clientCert,
-//  })
-//  db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
-//
+//	rootCertPool := x509.NewCertPool()
+//	pem, err := ioutil.ReadFile("/path/ca-cert.pem")
+//	if err != nil {
+//	    log.Fatal(err)
+//	}
+//	if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
+//	    log.Fatal("Failed to append PEM.")
+//	}
+//	clientCert := make([]tls.Certificate, 0, 1)
+//	certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem")
+//	if err != nil {
+//	    log.Fatal(err)
+//	}
+//	clientCert = append(clientCert, certs)
+//	mysql.RegisterTLSConfig("custom", &tls.Config{
+//	    RootCAs: rootCertPool,
+//	    Certificates: clientCert,
+//	})
+//	db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
 func RegisterTLSConfig(key string, config *tls.Config) error {
 	if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" || strings.ToLower(key) == "preferred" {
 		return fmt.Errorf("key '%s' is reserved", key)
@@ -106,27 +105,126 @@ func readBool(input string) (value bool, valid bool) {
 *                           Time related utils                                *
 ******************************************************************************/
 
-func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
-	base := "0000-00-00 00:00:00.0000000"
-	switch len(str) {
+func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
+	const base = "0000-00-00 00:00:00.000000"
+	switch len(b) {
 	case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
-		if str == base[:len(str)] {
-			return
+		if string(b) == base[:len(b)] {
+			return time.Time{}, nil
+		}
+
+		year, err := parseByteYear(b)
+		if err != nil {
+			return time.Time{}, err
+		}
+		if b[4] != '-' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4])
+		}
+
+		m, err := parseByte2Digits(b[5], b[6])
+		if err != nil {
+			return time.Time{}, err
+		}
+		month := time.Month(m)
+
+		if b[7] != '-' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7])
+		}
+
+		day, err := parseByte2Digits(b[8], b[9])
+		if err != nil {
+			return time.Time{}, err
+		}
+		if len(b) == 10 {
+			return time.Date(year, month, day, 0, 0, 0, 0, loc), nil
+		}
+
+		if b[10] != ' ' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[10])
+		}
+
+		hour, err := parseByte2Digits(b[11], b[12])
+		if err != nil {
+			return time.Time{}, err
+		}
+		if b[13] != ':' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[13])
+		}
+
+		min, err := parseByte2Digits(b[14], b[15])
+		if err != nil {
+			return time.Time{}, err
+		}
+		if b[16] != ':' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[16])
+		}
+
+		sec, err := parseByte2Digits(b[17], b[18])
+		if err != nil {
+			return time.Time{}, err
 		}
-		t, err = time.Parse(timeFormat[:len(str)], str)
+		if len(b) == 19 {
+			return time.Date(year, month, day, hour, min, sec, 0, loc), nil
+		}
+
+		if b[19] != '.' {
+			return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[19])
+		}
+		nsec, err := parseByteNanoSec(b[20:])
+		if err != nil {
+			return time.Time{}, err
+		}
+		return time.Date(year, month, day, hour, min, sec, nsec, loc), nil
 	default:
-		err = fmt.Errorf("invalid time string: %s", str)
-		return
+		return time.Time{}, fmt.Errorf("invalid time bytes: %s", b)
 	}
+}
 
-	// Adjust location
-	if err == nil && loc != time.UTC {
-		y, mo, d := t.Date()
-		h, mi, s := t.Clock()
-		t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
+func parseByteYear(b []byte) (int, error) {
+	year, n := 0, 1000
+	for i := 0; i < 4; i++ {
+		v, err := bToi(b[i])
+		if err != nil {
+			return 0, err
+		}
+		year += v * n
+		n /= 10
 	}
+	return year, nil
+}
 
-	return
+func parseByte2Digits(b1, b2 byte) (int, error) {
+	d1, err := bToi(b1)
+	if err != nil {
+		return 0, err
+	}
+	d2, err := bToi(b2)
+	if err != nil {
+		return 0, err
+	}
+	return d1*10 + d2, nil
+}
+
+func parseByteNanoSec(b []byte) (int, error) {
+	ns, digit := 0, 100000 // max is 6-digits
+	for i := 0; i < len(b); i++ {
+		v, err := bToi(b[i])
+		if err != nil {
+			return 0, err
+		}
+		ns += v * digit
+		digit /= 10
+	}
+	// nanoseconds has 10-digits. (needs to scale digits)
+	// 10 - 6 = 4, so we have to multiple 1000.
+	return ns * 1000, nil
+}
+
+func bToi(b byte) (int, error) {
+	if b < '0' || b > '9' {
+		return 0, errors.New("not [0-9]")
+	}
+	return int(b - '0'), nil
 }
 
 func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
@@ -167,6 +265,64 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
 	return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
 }
 
+func appendDateTime(buf []byte, t time.Time) ([]byte, error) {
+	year, month, day := t.Date()
+	hour, min, sec := t.Clock()
+	nsec := t.Nanosecond()
+
+	if year < 1 || year > 9999 {
+		return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
+	}
+	year100 := year / 100
+	year1 := year % 100
+
+	var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape
+	localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]
+	localBuf[4] = '-'
+	localBuf[5], localBuf[6] = digits10[month], digits01[month]
+	localBuf[7] = '-'
+	localBuf[8], localBuf[9] = digits10[day], digits01[day]
+
+	if hour == 0 && min == 0 && sec == 0 && nsec == 0 {
+		return append(buf, localBuf[:10]...), nil
+	}
+
+	localBuf[10] = ' '
+	localBuf[11], localBuf[12] = digits10[hour], digits01[hour]
+	localBuf[13] = ':'
+	localBuf[14], localBuf[15] = digits10[min], digits01[min]
+	localBuf[16] = ':'
+	localBuf[17], localBuf[18] = digits10[sec], digits01[sec]
+
+	if nsec == 0 {
+		return append(buf, localBuf[:19]...), nil
+	}
+	nsec100000000 := nsec / 100000000
+	nsec1000000 := (nsec / 1000000) % 100
+	nsec10000 := (nsec / 10000) % 100
+	nsec100 := (nsec / 100) % 100
+	nsec1 := nsec % 100
+	localBuf[19] = '.'
+
+	// milli second
+	localBuf[20], localBuf[21], localBuf[22] =
+		digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000]
+	// micro second
+	localBuf[23], localBuf[24], localBuf[25] =
+		digits10[nsec10000], digits01[nsec10000], digits10[nsec100]
+	// nano second
+	localBuf[26], localBuf[27], localBuf[28] =
+		digits01[nsec100], digits10[nsec1], digits01[nsec1]
+
+	// trim trailing zeros
+	n := len(localBuf)
+	for n > 0 && localBuf[n-1] == '0' {
+		n--
+	}
+
+	return append(buf, localBuf[:n]...), nil
+}
+
 // zeroDateTime is used in formatBinaryDateTime to avoid an allocation
 // if the DATE or DATETIME has the zero value.
 // It must never be changed.
@@ -375,7 +531,7 @@ func stringToInt(b []byte) int {
 	return val
 }
 
-// returns the string read as a bytes slice, wheter the value is NULL,
+// returns the string read as a bytes slice, whether the value is NULL,
 // the number of bytes read and an error, in case the string is longer than
 // the input slice
 func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
@@ -485,32 +641,32 @@ func escapeBytesBackslash(buf, v []byte) []byte {
 	for _, c := range v {
 		switch c {
 		case '\x00':
-			buf[pos] = '\\'
 			buf[pos+1] = '0'
+			buf[pos] = '\\'
 			pos += 2
 		case '\n':
-			buf[pos] = '\\'
 			buf[pos+1] = 'n'
+			buf[pos] = '\\'
 			pos += 2
 		case '\r':
-			buf[pos] = '\\'
 			buf[pos+1] = 'r'
+			buf[pos] = '\\'
 			pos += 2
 		case '\x1a':
-			buf[pos] = '\\'
 			buf[pos+1] = 'Z'
+			buf[pos] = '\\'
 			pos += 2
 		case '\'':
-			buf[pos] = '\\'
 			buf[pos+1] = '\''
+			buf[pos] = '\\'
 			pos += 2
 		case '"':
-			buf[pos] = '\\'
 			buf[pos+1] = '"'
+			buf[pos] = '\\'
 			pos += 2
 		case '\\':
-			buf[pos] = '\\'
 			buf[pos+1] = '\\'
+			buf[pos] = '\\'
 			pos += 2
 		default:
 			buf[pos] = c
@@ -530,32 +686,32 @@ func escapeStringBackslash(buf []byte, v string) []byte {
 		c := v[i]
 		switch c {
 		case '\x00':
-			buf[pos] = '\\'
 			buf[pos+1] = '0'
+			buf[pos] = '\\'
 			pos += 2
 		case '\n':
-			buf[pos] = '\\'
 			buf[pos+1] = 'n'
+			buf[pos] = '\\'
 			pos += 2
 		case '\r':
-			buf[pos] = '\\'
 			buf[pos+1] = 'r'
+			buf[pos] = '\\'
 			pos += 2
 		case '\x1a':
-			buf[pos] = '\\'
 			buf[pos+1] = 'Z'
+			buf[pos] = '\\'
 			pos += 2
 		case '\'':
-			buf[pos] = '\\'
 			buf[pos+1] = '\''
+			buf[pos] = '\\'
 			pos += 2
 		case '"':
-			buf[pos] = '\\'
 			buf[pos+1] = '"'
+			buf[pos] = '\\'
 			pos += 2
 		case '\\':
-			buf[pos] = '\\'
 			buf[pos+1] = '\\'
+			buf[pos] = '\\'
 			pos += 2
 		default:
 			buf[pos] = c
@@ -577,8 +733,8 @@ func escapeBytesQuotes(buf, v []byte) []byte {
 
 	for _, c := range v {
 		if c == '\'' {
-			buf[pos] = '\''
 			buf[pos+1] = '\''
+			buf[pos] = '\''
 			pos += 2
 		} else {
 			buf[pos] = c
@@ -597,8 +753,8 @@ func escapeStringQuotes(buf []byte, v string) []byte {
 	for i := 0; i < len(v); i++ {
 		c := v[i]
 		if c == '\'' {
-			buf[pos] = '\''
 			buf[pos+1] = '\''
+			buf[pos] = '\''
 			pos += 2
 		} else {
 			buf[pos] = c
@@ -623,39 +779,16 @@ type noCopy struct{}
 // Lock is a no-op used by -copylocks checker from `go vet`.
 func (*noCopy) Lock() {}
 
-// atomicBool is a wrapper around uint32 for usage as a boolean value with
-// atomic access.
-type atomicBool struct {
-	_noCopy noCopy
-	value   uint32
-}
-
-// IsSet returns whether the current boolean value is true
-func (ab *atomicBool) IsSet() bool {
-	return atomic.LoadUint32(&ab.value) > 0
-}
-
-// Set sets the value of the bool regardless of the previous value
-func (ab *atomicBool) Set(value bool) {
-	if value {
-		atomic.StoreUint32(&ab.value, 1)
-	} else {
-		atomic.StoreUint32(&ab.value, 0)
-	}
-}
-
-// TrySet sets the value of the bool and returns whether the value changed
-func (ab *atomicBool) TrySet(value bool) bool {
-	if value {
-		return atomic.SwapUint32(&ab.value, 1) == 0
-	}
-	return atomic.SwapUint32(&ab.value, 0) > 0
-}
+// Unlock is a no-op used by -copylocks checker from `go vet`.
+// noCopy should implement sync.Locker from Go 1.11
+// https://github.com/golang/go/commit/c2eba53e7f80df21d51285879d51ab81bcfbf6bc
+// https://github.com/golang/go/issues/26165
+func (*noCopy) Unlock() {}
 
 // atomicError is a wrapper for atomically accessed error values
 type atomicError struct {
-	_noCopy noCopy
-	value   atomic.Value
+	_     noCopy
+	value atomic.Value
 }
 
 // Set sets the error value regardless of the previous value.
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go
index de30de6da..a912b75a0 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go
@@ -140,12 +140,13 @@ func (c *counter) get() float64 {
 }
 
 func (c *counter) Write(out *dto.Metric) error {
-	val := c.get()
-
+	// Read the Exemplar first and the value second. This is to avoid a race condition
+	// where users see an exemplar for a not-yet-existing observation.
 	var exemplar *dto.Exemplar
 	if e := c.exemplar.Load(); e != nil {
 		exemplar = e.(*dto.Exemplar)
 	}
+	val := c.get()
 
 	return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
 }
@@ -245,7 +246,8 @@ func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
 // GetMetricWithLabelValues would have returned an error. Not returning an
 // error allows shortcuts like
-//     myVec.WithLabelValues("404", "GET").Add(42)
+//
+//	myVec.WithLabelValues("404", "GET").Add(42)
 func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
 	c, err := v.GetMetricWithLabelValues(lvs...)
 	if err != nil {
@@ -256,7 +258,8 @@ func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
 
 // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 // returned an error. Not returning an error allows shortcuts like
-//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
+//
+//	myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 func (v *CounterVec) With(labels Labels) Counter {
 	c, err := v.GetMetricWith(labels)
 	if err != nil {
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/doc.go b/vendor/github.com/prometheus/client_golang/prometheus/doc.go
index 98450125d..811072cbd 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/doc.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/doc.go
@@ -21,55 +21,66 @@
 // All exported functions and methods are safe to be used concurrently unless
 // specified otherwise.
 //
-// A Basic Example
+// # A Basic Example
 //
 // As a starting point, a very basic usage example:
 //
-//    package main
-//
-//    import (
-//    	"log"
-//    	"net/http"
-//
-//    	"github.com/prometheus/client_golang/prometheus"
-//    	"github.com/prometheus/client_golang/prometheus/promhttp"
-//    )
-//
-//    var (
-//    	cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
-//    		Name: "cpu_temperature_celsius",
-//    		Help: "Current temperature of the CPU.",
-//    	})
-//    	hdFailures = prometheus.NewCounterVec(
-//    		prometheus.CounterOpts{
-//    			Name: "hd_errors_total",
-//    			Help: "Number of hard-disk errors.",
-//    		},
-//    		[]string{"device"},
-//    	)
-//    )
-//
-//    func init() {
-//    	// Metrics have to be registered to be exposed:
-//    	prometheus.MustRegister(cpuTemp)
-//    	prometheus.MustRegister(hdFailures)
-//    }
-//
-//    func main() {
-//    	cpuTemp.Set(65.3)
-//    	hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
-//
-//    	// The Handler function provides a default handler to expose metrics
-//    	// via an HTTP server. "/metrics" is the usual endpoint for that.
-//    	http.Handle("/metrics", promhttp.Handler())
-//    	log.Fatal(http.ListenAndServe(":8080", nil))
-//    }
-//
+//	package main
+//
+//	import (
+//		"log"
+//		"net/http"
+//
+//		"github.com/prometheus/client_golang/prometheus"
+//		"github.com/prometheus/client_golang/prometheus/promhttp"
+//	)
+//
+//	type metrics struct {
+//		cpuTemp  prometheus.Gauge
+//	  hdFailures *prometheus.CounterVec
+//	}
+//
+//	func NewMetrics(reg prometheus.Registerer) *metrics {
+//	  m := &metrics{
+//	    cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{
+//	      Name: "cpu_temperature_celsius",
+//	      Help: "Current temperature of the CPU.",
+//	    }),
+//	    hdFailures: prometheus.NewCounterVec(
+//	      prometheus.CounterOpts{
+//	        Name: "hd_errors_total",
+//	        Help: "Number of hard-disk errors.",
+//	      },
+//	      []string{"device"},
+//	    ),
+//	  }
+//	  reg.MustRegister(m.cpuTemp)
+//	  reg.MustRegister(m.hdFailures)
+//	  return m
+//	}
+//
+//	func main() {
+//	  // Create a non-global registry.
+//	  reg := prometheus.NewRegistry()
+//
+//	  // Create new metrics and register them using the custom registry.
+//	  m := NewMetrics(reg)
+//	  // Set values for the new created metrics.
+//		m.cpuTemp.Set(65.3)
+//		m.hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
+//
+//		// Expose metrics and custom registry via an HTTP server
+//		// using the HandleFor function. "/metrics" is the usual endpoint for that.
+//		http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
+//		log.Fatal(http.ListenAndServe(":8080", nil))
+//	}
 //
 // This is a complete program that exports two metrics, a Gauge and a Counter,
 // the latter with a label attached to turn it into a (one-dimensional) vector.
+// It register the metrics using a custom registry and exposes them via an HTTP server
+// on the /metrics endpoint.
 //
-// Metrics
+// # Metrics
 //
 // The number of exported identifiers in this package might appear a bit
 // overwhelming. However, in addition to the basic plumbing shown in the example
@@ -100,7 +111,7 @@
 // To create instances of Metrics and their vector versions, you need a suitable
 // …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, or HistogramOpts.
 //
-// Custom Collectors and constant Metrics
+// # Custom Collectors and constant Metrics
 //
 // While you could create your own implementations of Metric, most likely you
 // will only ever implement the Collector interface on your own. At a first
@@ -141,7 +152,7 @@
 // a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
 // shortcuts.
 //
-// Advanced Uses of the Registry
+// # Advanced Uses of the Registry
 //
 // While MustRegister is the by far most common way of registering a Collector,
 // sometimes you might want to handle the errors the registration might cause.
@@ -176,23 +187,23 @@
 // NewProcessCollector). With a custom registry, you are in control and decide
 // yourself about the Collectors to register.
 //
-// HTTP Exposition
+// # HTTP Exposition
 //
 // The Registry implements the Gatherer interface. The caller of the Gather
 // method can then expose the gathered metrics in some way. Usually, the metrics
 // are served via HTTP on the /metrics endpoint. That's happening in the example
 // above. The tools to expose metrics via HTTP are in the promhttp sub-package.
 //
-// Pushing to the Pushgateway
+// # Pushing to the Pushgateway
 //
 // Function for pushing to the Pushgateway can be found in the push sub-package.
 //
-// Graphite Bridge
+// # Graphite Bridge
 //
 // Functions and examples to push metrics from a Gatherer to Graphite can be
 // found in the graphite sub-package.
 //
-// Other Means of Exposition
+// # Other Means of Exposition
 //
 // More ways of exposing metrics can easily be added by following the approaches
 // of the existing implementations.
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
index bd0733d6a..21271a5bb 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
@@ -210,7 +210,8 @@ func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
 // GetMetricWithLabelValues would have returned an error. Not returning an
 // error allows shortcuts like
-//     myVec.WithLabelValues("404", "GET").Add(42)
+//
+//	myVec.WithLabelValues("404", "GET").Add(42)
 func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
 	g, err := v.GetMetricWithLabelValues(lvs...)
 	if err != nil {
@@ -221,7 +222,8 @@ func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
 
 // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 // returned an error. Not returning an error allows shortcuts like
-//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
+//
+//	myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 func (v *GaugeVec) With(labels Labels) Gauge {
 	g, err := v.GetMetricWith(labels)
 	if err != nil {
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
index 0d47fecdc..4c873a01c 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
@@ -28,19 +28,216 @@ import (
 	dto "github.com/prometheus/client_model/go"
 )
 
+// nativeHistogramBounds for the frac of observed values. Only relevant for
+// schema > 0. The position in the slice is the schema. (0 is never used, just
+// here for convenience of using the schema directly as the index.)
+//
+// TODO(beorn7): Currently, we do a binary search into these slices. There are
+// ways to turn it into a small number of simple array lookups. It probably only
+// matters for schema 5 and beyond, but should be investigated. See this comment
+// as a starting point:
+// https://github.com/open-telemetry/opentelemetry-specification/issues/1776#issuecomment-870164310
+var nativeHistogramBounds = [][]float64{
+	// Schema "0":
+	{0.5},
+	// Schema 1:
+	{0.5, 0.7071067811865475},
+	// Schema 2:
+	{0.5, 0.5946035575013605, 0.7071067811865475, 0.8408964152537144},
+	// Schema 3:
+	{
+		0.5, 0.5452538663326288, 0.5946035575013605, 0.6484197773255048,
+		0.7071067811865475, 0.7711054127039704, 0.8408964152537144, 0.9170040432046711,
+	},
+	// Schema 4:
+	{
+		0.5, 0.5221368912137069, 0.5452538663326288, 0.5693943173783458,
+		0.5946035575013605, 0.620928906036742, 0.6484197773255048, 0.6771277734684463,
+		0.7071067811865475, 0.7384130729697496, 0.7711054127039704, 0.805245165974627,
+		0.8408964152537144, 0.8781260801866495, 0.9170040432046711, 0.9576032806985735,
+	},
+	// Schema 5:
+	{
+		0.5, 0.5109485743270583, 0.5221368912137069, 0.5335702003384117,
+		0.5452538663326288, 0.5571933712979462, 0.5693943173783458, 0.5818624293887887,
+		0.5946035575013605, 0.6076236799902344, 0.620928906036742, 0.6345254785958666,
+		0.6484197773255048, 0.6626183215798706, 0.6771277734684463, 0.6919549409819159,
+		0.7071067811865475, 0.7225904034885232, 0.7384130729697496, 0.7545822137967112,
+		0.7711054127039704, 0.7879904225539431, 0.805245165974627, 0.8228777390769823,
+		0.8408964152537144, 0.8593096490612387, 0.8781260801866495, 0.8973545375015533,
+		0.9170040432046711, 0.9370838170551498, 0.9576032806985735, 0.9785720620876999,
+	},
+	// Schema 6:
+	{
+		0.5, 0.5054446430258502, 0.5109485743270583, 0.5165124395106142,
+		0.5221368912137069, 0.5278225891802786, 0.5335702003384117, 0.5393803988785598,
+		0.5452538663326288, 0.5511912916539204, 0.5571933712979462, 0.5632608093041209,
+		0.5693943173783458, 0.5755946149764913, 0.5818624293887887, 0.5881984958251406,
+		0.5946035575013605, 0.6010783657263515, 0.6076236799902344, 0.6142402680534349,
+		0.620928906036742, 0.6276903785123455, 0.6345254785958666, 0.6414350080393891,
+		0.6484197773255048, 0.6554806057623822, 0.6626183215798706, 0.6698337620266515,
+		0.6771277734684463, 0.6845012114872953, 0.6919549409819159, 0.6994898362691555,
+		0.7071067811865475, 0.7148066691959849, 0.7225904034885232, 0.7304588970903234,
+		0.7384130729697496, 0.7464538641456323, 0.7545822137967112, 0.762799075372269,
+		0.7711054127039704, 0.7795022001189185, 0.7879904225539431, 0.7965710756711334,
+		0.805245165974627, 0.8140137109286738, 0.8228777390769823, 0.8318382901633681,
+		0.8408964152537144, 0.8500531768592616, 0.8593096490612387, 0.8686669176368529,
+		0.8781260801866495, 0.8876882462632604, 0.8973545375015533, 0.9071260877501991,
+		0.9170040432046711, 0.9269895625416926, 0.9370838170551498, 0.9472879907934827,
+		0.9576032806985735, 0.9680308967461471, 0.9785720620876999, 0.9892280131939752,
+	},
+	// Schema 7:
+	{
+		0.5, 0.5027149505564014, 0.5054446430258502, 0.5081891574554764,
+		0.5109485743270583, 0.5137229745593818, 0.5165124395106142, 0.5193170509806894,
+		0.5221368912137069, 0.5249720429003435, 0.5278225891802786, 0.5306886136446309,
+		0.5335702003384117, 0.5364674337629877, 0.5393803988785598, 0.5423091811066545,
+		0.5452538663326288, 0.5482145409081883, 0.5511912916539204, 0.5541842058618393,
+		0.5571933712979462, 0.5602188762048033, 0.5632608093041209, 0.5663192597993595,
+		0.5693943173783458, 0.572486072215902, 0.5755946149764913, 0.5787200368168754,
+		0.5818624293887887, 0.585021884841625, 0.5881984958251406, 0.5913923554921704,
+		0.5946035575013605, 0.5978321960199137, 0.6010783657263515, 0.6043421618132907,
+		0.6076236799902344, 0.6109230164863786, 0.6142402680534349, 0.6175755319684665,
+		0.620928906036742, 0.6243004885946023, 0.6276903785123455, 0.6310986751971253,
+		0.6345254785958666, 0.637970889198196, 0.6414350080393891, 0.6449179367033329,
+		0.6484197773255048, 0.6519406325959679, 0.6554806057623822, 0.659039800633032,
+		0.6626183215798706, 0.6662162735415805, 0.6698337620266515, 0.6734708931164728,
+		0.6771277734684463, 0.6808045103191123, 0.6845012114872953, 0.688217985377265,
+		0.6919549409819159, 0.6957121878859629, 0.6994898362691555, 0.7032879969095076,
+		0.7071067811865475, 0.7109463010845827, 0.7148066691959849, 0.718687998724491,
+		0.7225904034885232, 0.7265139979245261, 0.7304588970903234, 0.7344252166684908,
+		0.7384130729697496, 0.7424225829363761, 0.7464538641456323, 0.7505070348132126,
+		0.7545822137967112, 0.7586795205991071, 0.762799075372269, 0.7669409989204777,
+		0.7711054127039704, 0.7752924388424999, 0.7795022001189185, 0.7837348199827764,
+		0.7879904225539431, 0.7922691326262467, 0.7965710756711334, 0.8008963778413465,
+		0.805245165974627, 0.8096175675974316, 0.8140137109286738, 0.8184337248834821,
+		0.8228777390769823, 0.8273458838280969, 0.8318382901633681, 0.8363550898207981,
+		0.8408964152537144, 0.8454623996346523, 0.8500531768592616, 0.8546688815502312,
+		0.8593096490612387, 0.8639756154809185, 0.8686669176368529, 0.8733836930995842,
+		0.8781260801866495, 0.8828942179666361, 0.8876882462632604, 0.8925083056594671,
+		0.8973545375015533, 0.9022270839033115, 0.9071260877501991, 0.9120516927035263,
+		0.9170040432046711, 0.9219832844793128, 0.9269895625416926, 0.9320230241988943,
+		0.9370838170551498, 0.9421720895161669, 0.9472879907934827, 0.9524316709088368,
+		0.9576032806985735, 0.9628029718180622, 0.9680308967461471, 0.9732872087896164,
+		0.9785720620876999, 0.9838856116165875, 0.9892280131939752, 0.9945994234836328,
+	},
+	// Schema 8:
+	{
+		0.5, 0.5013556375251013, 0.5027149505564014, 0.5040779490592088,
+		0.5054446430258502, 0.5068150424757447, 0.5081891574554764, 0.509566998038869,
+		0.5109485743270583, 0.5123338964485679, 0.5137229745593818, 0.5151158188430205,
+		0.5165124395106142, 0.5179128468009786, 0.5193170509806894, 0.520725062344158,
+		0.5221368912137069, 0.5235525479396449, 0.5249720429003435, 0.526395386502313,
+		0.5278225891802786, 0.5292536613972564, 0.5306886136446309, 0.5321274564422321,
+		0.5335702003384117, 0.5350168559101208, 0.5364674337629877, 0.5379219445313954,
+		0.5393803988785598, 0.5408428074966075, 0.5423091811066545, 0.5437795304588847,
+		0.5452538663326288, 0.5467321995364429, 0.5482145409081883, 0.549700901315111,
+		0.5511912916539204, 0.5526857228508706, 0.5541842058618393, 0.5556867516724088,
+		0.5571933712979462, 0.5587040757836845, 0.5602188762048033, 0.5617377836665098,
+		0.5632608093041209, 0.564787964283144, 0.5663192597993595, 0.5678547070789026,
+		0.5693943173783458, 0.5709381019847808, 0.572486072215902, 0.5740382394200894,
+		0.5755946149764913, 0.5771552102951081, 0.5787200368168754, 0.5802891060137493,
+		0.5818624293887887, 0.5834400184762408, 0.585021884841625, 0.5866080400818185,
+		0.5881984958251406, 0.5897932637314379, 0.5913923554921704, 0.5929957828304968,
+		0.5946035575013605, 0.5962156912915756, 0.5978321960199137, 0.5994530835371903,
+		0.6010783657263515, 0.6027080545025619, 0.6043421618132907, 0.6059806996384005,
+		0.6076236799902344, 0.6092711149137041, 0.6109230164863786, 0.6125793968185725,
+		0.6142402680534349, 0.6159056423670379, 0.6175755319684665, 0.6192499490999082,
+		0.620928906036742, 0.622612415087629, 0.6243004885946023, 0.6259931389331581,
+		0.6276903785123455, 0.6293922197748583, 0.6310986751971253, 0.6328097572894031,
+		0.6345254785958666, 0.6362458516947014, 0.637970889198196, 0.6397006037528346,
+		0.6414350080393891, 0.6431741147730128, 0.6449179367033329, 0.6466664866145447,
+		0.6484197773255048, 0.6501778216898253, 0.6519406325959679, 0.6537082229673385,
+		0.6554806057623822, 0.6572577939746774, 0.659039800633032, 0.6608266388015788,
+		0.6626183215798706, 0.6644148621029772, 0.6662162735415805, 0.6680225691020727,
+		0.6698337620266515, 0.6716498655934177, 0.6734708931164728, 0.6752968579460171,
+		0.6771277734684463, 0.6789636531064505, 0.6808045103191123, 0.6826503586020058,
+		0.6845012114872953, 0.6863570825438342, 0.688217985377265, 0.690083933630119,
+		0.6919549409819159, 0.6938310211492645, 0.6957121878859629, 0.6975984549830999,
+		0.6994898362691555, 0.7013863456101023, 0.7032879969095076, 0.7051948041086352,
+		0.7071067811865475, 0.7090239421602076, 0.7109463010845827, 0.7128738720527471,
+		0.7148066691959849, 0.7167447066838943, 0.718687998724491, 0.7206365595643126,
+		0.7225904034885232, 0.7245495448210174, 0.7265139979245261, 0.7284837772007218,
+		0.7304588970903234, 0.7324393720732029, 0.7344252166684908, 0.7364164454346837,
+		0.7384130729697496, 0.7404151139112358, 0.7424225829363761, 0.7444354947621984,
+		0.7464538641456323, 0.7484777058836176, 0.7505070348132126, 0.7525418658117031,
+		0.7545822137967112, 0.7566280937263048, 0.7586795205991071, 0.7607365094544071,
+		0.762799075372269, 0.7648672334736434, 0.7669409989204777, 0.7690203869158282,
+		0.7711054127039704, 0.7731960915705107, 0.7752924388424999, 0.7773944698885442,
+		0.7795022001189185, 0.7816156449856788, 0.7837348199827764, 0.7858597406461707,
+		0.7879904225539431, 0.7901268813264122, 0.7922691326262467, 0.7944171921585818,
+		0.7965710756711334, 0.7987307989543135, 0.8008963778413465, 0.8030678282083853,
+		0.805245165974627, 0.8074284071024302, 0.8096175675974316, 0.8118126635086642,
+		0.8140137109286738, 0.8162207259936375, 0.8184337248834821, 0.820652723822003,
+		0.8228777390769823, 0.8251087869603088, 0.8273458838280969, 0.8295890460808079,
+		0.8318382901633681, 0.8340936325652911, 0.8363550898207981, 0.8386226785089391,
+		0.8408964152537144, 0.8431763167241966, 0.8454623996346523, 0.8477546807446661,
+		0.8500531768592616, 0.8523579048290255, 0.8546688815502312, 0.8569861239649629,
+		0.8593096490612387, 0.8616394738731368, 0.8639756154809185, 0.8663180910111553,
+		0.8686669176368529, 0.871022112577578, 0.8733836930995842, 0.8757516765159389,
+		0.8781260801866495, 0.8805069215187917, 0.8828942179666361, 0.8852879870317771,
+		0.8876882462632604, 0.890095013257712, 0.8925083056594671, 0.8949281411607002,
+		0.8973545375015533, 0.8997875124702672, 0.9022270839033115, 0.9046732696855155,
+		0.9071260877501991, 0.909585556079304, 0.9120516927035263, 0.9145245157024483,
+		0.9170040432046711, 0.9194902933879467, 0.9219832844793128, 0.9244830347552253,
+		0.9269895625416926, 0.92950288621441, 0.9320230241988943, 0.9345499949706191,
+		0.9370838170551498, 0.93962450902828, 0.9421720895161669, 0.9447265771954693,
+		0.9472879907934827, 0.9498563490882775, 0.9524316709088368, 0.9550139751351947,
+		0.9576032806985735, 0.9601996065815236, 0.9628029718180622, 0.9654133954938133,
+		0.9680308967461471, 0.9706554947643201, 0.9732872087896164, 0.9759260581154889,
+		0.9785720620876999, 0.9812252401044634, 0.9838856116165875, 0.9865531961276168,
+		0.9892280131939752, 0.9919100824251095, 0.9945994234836328, 0.9972960560854698,
+	},
+}
+
+// The nativeHistogramBounds above can be generated with the code below.
+//
+// TODO(beorn7): It's tempting to actually use `go generate` to generate the
+// code above. However, this could lead to slightly different numbers on
+// different architectures. We still need to come to terms if we are fine with
+// that, or if we might prefer to specify precise numbers in the standard.
+//
+// var nativeHistogramBounds [][]float64 = make([][]float64, 9)
+//
+// func init() {
+// 	// Populate nativeHistogramBounds.
+// 	numBuckets := 1
+// 	for i := range nativeHistogramBounds {
+// 		bounds := []float64{0.5}
+// 		factor := math.Exp2(math.Exp2(float64(-i)))
+// 		for j := 0; j < numBuckets-1; j++ {
+// 			var bound float64
+// 			if (j+1)%2 == 0 {
+// 				// Use previously calculated value for increased precision.
+// 				bound = nativeHistogramBounds[i-1][j/2+1]
+// 			} else {
+// 				bound = bounds[j] * factor
+// 			}
+// 			bounds = append(bounds, bound)
+// 		}
+// 		numBuckets *= 2
+// 		nativeHistogramBounds[i] = bounds
+// 	}
+// }
+
 // A Histogram counts individual observations from an event or sample stream in
-// configurable buckets. Similar to a summary, it also provides a sum of
-// observations and an observation count.
+// configurable static buckets (or in dynamic sparse buckets as part of the
+// experimental Native Histograms, see below for more details). Similar to a
+// Summary, it also provides a sum of observations and an observation count.
 //
 // On the Prometheus server, quantiles can be calculated from a Histogram using
-// the histogram_quantile function in the query language.
+// the histogram_quantile PromQL function.
+//
+// Note that Histograms, in contrast to Summaries, can be aggregated in PromQL
+// (see the documentation for detailed procedures). However, Histograms require
+// the user to pre-define suitable buckets, and they are in general less
+// accurate. (Both problems are addressed by the experimental Native
+// Histograms. To use them, configure a NativeHistogramBucketFactor in the
+// HistogramOpts. They also require a Prometheus server v2.40+ with the
+// corresponding feature flag enabled.)
 //
-// Note that Histograms, in contrast to Summaries, can be aggregated with the
-// Prometheus query language (see the documentation for detailed
-// procedures). However, Histograms require the user to pre-define suitable
-// buckets, and they are in general less accurate. The Observe method of a
-// Histogram has a very low performance overhead in comparison with the Observe
-// method of a Summary.
+// The Observe method of a Histogram has a very low performance overhead in
+// comparison with the Observe method of a Summary.
 //
 // To create Histogram instances, use NewHistogram.
 type Histogram interface {
@@ -50,7 +247,8 @@ type Histogram interface {
 	// Observe adds a single observation to the histogram. Observations are
 	// usually positive or zero. Negative observations are accepted but
 	// prevent current versions of Prometheus from properly detecting
-	// counter resets in the sum of observations. See
+	// counter resets in the sum of observations. (The experimental Native
+	// Histograms handle negative observations properly.) See
 	// https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
 	// for details.
 	Observe(float64)
@@ -64,18 +262,28 @@ const bucketLabel = "le"
 // tailored to broadly measure the response time (in seconds) of a network
 // service. Most likely, however, you will be required to define buckets
 // customized to your use case.
-var (
-	DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
+var DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
 
-	errBucketLabelNotAllowed = fmt.Errorf(
-		"%q is not allowed as label name in histograms", bucketLabel,
-	)
+// DefNativeHistogramZeroThreshold is the default value for
+// NativeHistogramZeroThreshold in the HistogramOpts.
+//
+// The value is 2^-128 (or 0.5*2^-127 in the actual IEEE 754 representation),
+// which is a bucket boundary at all possible resolutions.
+const DefNativeHistogramZeroThreshold = 2.938735877055719e-39
+
+// NativeHistogramZeroThresholdZero can be used as NativeHistogramZeroThreshold
+// in the HistogramOpts to create a zero bucket of width zero, i.e. a zero
+// bucket that only receives observations of precisely zero.
+const NativeHistogramZeroThresholdZero = -1
+
+var errBucketLabelNotAllowed = fmt.Errorf(
+	"%q is not allowed as label name in histograms", bucketLabel,
 )
 
-// LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
-// bucket has an upper bound of 'start'. The final +Inf bucket is not counted
-// and not included in the returned slice. The returned slice is meant to be
-// used for the Buckets field of HistogramOpts.
+// LinearBuckets creates 'count' regular buckets, each 'width' wide, where the
+// lowest bucket has an upper bound of 'start'. The final +Inf bucket is not
+// counted and not included in the returned slice. The returned slice is meant
+// to be used for the Buckets field of HistogramOpts.
 //
 // The function panics if 'count' is zero or negative.
 func LinearBuckets(start, width float64, count int) []float64 {
@@ -90,11 +298,11 @@ func LinearBuckets(start, width float64, count int) []float64 {
 	return buckets
 }
 
-// ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
-// upper bound of 'start' and each following bucket's upper bound is 'factor'
-// times the previous bucket's upper bound. The final +Inf bucket is not counted
-// and not included in the returned slice. The returned slice is meant to be
-// used for the Buckets field of HistogramOpts.
+// ExponentialBuckets creates 'count' regular buckets, where the lowest bucket
+// has an upper bound of 'start' and each following bucket's upper bound is
+// 'factor' times the previous bucket's upper bound. The final +Inf bucket is
+// not counted and not included in the returned slice. The returned slice is
+// meant to be used for the Buckets field of HistogramOpts.
 //
 // The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
 // or if 'factor' is less than or equal 1.
@@ -180,8 +388,85 @@ type HistogramOpts struct {
 	// element in the slice is the upper inclusive bound of a bucket. The
 	// values must be sorted in strictly increasing order. There is no need
 	// to add a highest bucket with +Inf bound, it will be added
-	// implicitly. The default value is DefBuckets.
+	// implicitly. If Buckets is left as nil or set to a slice of length
+	// zero, it is replaced by default buckets. The default buckets are
+	// DefBuckets if no buckets for a native histogram (see below) are used,
+	// otherwise the default is no buckets. (In other words, if you want to
+	// use both reguler buckets and buckets for a native histogram, you have
+	// to define the regular buckets here explicitly.)
 	Buckets []float64
+
+	// If NativeHistogramBucketFactor is greater than one, so-called sparse
+	// buckets are used (in addition to the regular buckets, if defined
+	// above). A Histogram with sparse buckets will be ingested as a Native
+	// Histogram by a Prometheus server with that feature enabled (requires
+	// Prometheus v2.40+). Sparse buckets are exponential buckets covering
+	// the whole float64 range (with the exception of the “zero” bucket, see
+	// SparseBucketsZeroThreshold below). From any one bucket to the next,
+	// the width of the bucket grows by a constant
+	// factor. NativeHistogramBucketFactor provides an upper bound for this
+	// factor (exception see below). The smaller
+	// NativeHistogramBucketFactor, the more buckets will be used and thus
+	// the more costly the histogram will become. A generally good trade-off
+	// between cost and accuracy is a value of 1.1 (each bucket is at most
+	// 10% wider than the previous one), which will result in each power of
+	// two divided into 8 buckets (e.g. there will be 8 buckets between 1
+	// and 2, same as between 2 and 4, and 4 and 8, etc.).
+	//
+	// Details about the actually used factor: The factor is calculated as
+	// 2^(2^n), where n is an integer number between (and including) -8 and
+	// 4. n is chosen so that the resulting factor is the largest that is
+	// still smaller or equal to NativeHistogramBucketFactor. Note that the
+	// smallest possible factor is therefore approx. 1.00271 (i.e. 2^(2^-8)
+	// ). If NativeHistogramBucketFactor is greater than 1 but smaller than
+	// 2^(2^-8), then the actually used factor is still 2^(2^-8) even though
+	// it is larger than the provided NativeHistogramBucketFactor.
+	//
+	// NOTE: Native Histograms are still an experimental feature. Their
+	// behavior might still change without a major version
+	// bump. Subsequently, all NativeHistogram... options here might still
+	// change their behavior or name (or might completely disappear) without
+	// a major version bump.
+	NativeHistogramBucketFactor float64
+	// All observations with an absolute value of less or equal
+	// NativeHistogramZeroThreshold are accumulated into a “zero”
+	// bucket. For best results, this should be close to a bucket
+	// boundary. This is usually the case if picking a power of two. If
+	// NativeHistogramZeroThreshold is left at zero,
+	// DefSparseBucketsZeroThreshold is used as the threshold. To configure
+	// a zero bucket with an actual threshold of zero (i.e. only
+	// observations of precisely zero will go into the zero bucket), set
+	// NativeHistogramZeroThreshold to the NativeHistogramZeroThresholdZero
+	// constant (or any negative float value).
+	NativeHistogramZeroThreshold float64
+
+	// The remaining fields define a strategy to limit the number of
+	// populated sparse buckets. If NativeHistogramMaxBucketNumber is left
+	// at zero, the number of buckets is not limited. (Note that this might
+	// lead to unbounded memory consumption if the values observed by the
+	// Histogram are sufficiently wide-spread. In particular, this could be
+	// used as a DoS attack vector. Where the observed values depend on
+	// external inputs, it is highly recommended to set a
+	// NativeHistogramMaxBucketNumber.)  Once the set
+	// NativeHistogramMaxBucketNumber is exceeded, the following strategy is
+	// enacted: First, if the last reset (or the creation) of the histogram
+	// is at least NativeHistogramMinResetDuration ago, then the whole
+	// histogram is reset to its initial state (including regular
+	// buckets). If less time has passed, or if
+	// NativeHistogramMinResetDuration is zero, no reset is
+	// performed. Instead, the zero threshold is increased sufficiently to
+	// reduce the number of buckets to or below
+	// NativeHistogramMaxBucketNumber, but not to more than
+	// NativeHistogramMaxZeroThreshold. Thus, if
+	// NativeHistogramMaxZeroThreshold is already at or below the current
+	// zero threshold, nothing happens at this step. After that, if the
+	// number of buckets still exceeds NativeHistogramMaxBucketNumber, the
+	// resolution of the histogram is reduced by doubling the width of the
+	// sparse buckets (up to a growth factor between one bucket to the next
+	// of 2^(2^4) = 65536, see above).
+	NativeHistogramMaxBucketNumber  uint32
+	NativeHistogramMinResetDuration time.Duration
+	NativeHistogramMaxZeroThreshold float64
 }
 
 // NewHistogram creates a new Histogram based on the provided HistogramOpts. It
@@ -218,16 +503,29 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
 		}
 	}
 
-	if len(opts.Buckets) == 0 {
-		opts.Buckets = DefBuckets
-	}
-
 	h := &histogram{
-		desc:        desc,
-		upperBounds: opts.Buckets,
-		labelPairs:  MakeLabelPairs(desc, labelValues),
-		counts:      [2]*histogramCounts{{}, {}},
-		now:         time.Now,
+		desc:                            desc,
+		upperBounds:                     opts.Buckets,
+		labelPairs:                      MakeLabelPairs(desc, labelValues),
+		nativeHistogramMaxBuckets:       opts.NativeHistogramMaxBucketNumber,
+		nativeHistogramMaxZeroThreshold: opts.NativeHistogramMaxZeroThreshold,
+		nativeHistogramMinResetDuration: opts.NativeHistogramMinResetDuration,
+		lastResetTime:                   time.Now(),
+		now:                             time.Now,
+	}
+	if len(h.upperBounds) == 0 && opts.NativeHistogramBucketFactor <= 1 {
+		h.upperBounds = DefBuckets
+	}
+	if opts.NativeHistogramBucketFactor <= 1 {
+		h.nativeHistogramSchema = math.MinInt32 // To mark that there are no sparse buckets.
+	} else {
+		switch {
+		case opts.NativeHistogramZeroThreshold > 0:
+			h.nativeHistogramZeroThreshold = opts.NativeHistogramZeroThreshold
+		case opts.NativeHistogramZeroThreshold == 0:
+			h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
+		} // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
+		h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
 	}
 	for i, upperBound := range h.upperBounds {
 		if i < len(h.upperBounds)-1 {
@@ -246,8 +544,16 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
 	}
 	// Finally we know the final length of h.upperBounds and can make buckets
 	// for both counts as well as exemplars:
-	h.counts[0].buckets = make([]uint64, len(h.upperBounds))
-	h.counts[1].buckets = make([]uint64, len(h.upperBounds))
+	h.counts[0] = &histogramCounts{
+		buckets:                          make([]uint64, len(h.upperBounds)),
+		nativeHistogramZeroThresholdBits: math.Float64bits(h.nativeHistogramZeroThreshold),
+		nativeHistogramSchema:            h.nativeHistogramSchema,
+	}
+	h.counts[1] = &histogramCounts{
+		buckets:                          make([]uint64, len(h.upperBounds)),
+		nativeHistogramZeroThresholdBits: math.Float64bits(h.nativeHistogramZeroThreshold),
+		nativeHistogramSchema:            h.nativeHistogramSchema,
+	}
 	h.exemplars = make([]atomic.Value, len(h.upperBounds)+1)
 
 	h.init(h) // Init self-collection.
@@ -255,13 +561,98 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
 }
 
 type histogramCounts struct {
+	// Order in this struct matters for the alignment required by atomic
+	// operations, see http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+
 	// sumBits contains the bits of the float64 representing the sum of all
-	// observations. sumBits and count have to go first in the struct to
-	// guarantee alignment for atomic operations.
-	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+	// observations.
 	sumBits uint64
 	count   uint64
+
+	// nativeHistogramZeroBucket counts all (positive and negative)
+	// observations in the zero bucket (with an absolute value less or equal
+	// the current threshold, see next field.
+	nativeHistogramZeroBucket uint64
+	// nativeHistogramZeroThresholdBits is the bit pattern of the current
+	// threshold for the zero bucket. It's initially equal to
+	// nativeHistogramZeroThreshold but may change according to the bucket
+	// count limitation strategy.
+	nativeHistogramZeroThresholdBits uint64
+	// nativeHistogramSchema may change over time according to the bucket
+	// count limitation strategy and therefore has to be saved here.
+	nativeHistogramSchema int32
+	// Number of (positive and negative) sparse buckets.
+	nativeHistogramBucketsNumber uint32
+
+	// Regular buckets.
 	buckets []uint64
+
+	// The sparse buckets for native histograms are implemented with a
+	// sync.Map for now. A dedicated data structure will likely be more
+	// efficient. There are separate maps for negative and positive
+	// observations. The map's value is an *int64, counting observations in
+	// that bucket. (Note that we don't use uint64 as an int64 won't
+	// overflow in practice, and working with signed numbers from the
+	// beginning simplifies the handling of deltas.) The map's key is the
+	// index of the bucket according to the used
+	// nativeHistogramSchema. Index 0 is for an upper bound of 1.
+	nativeHistogramBucketsPositive, nativeHistogramBucketsNegative sync.Map
+}
+
+// observe manages the parts of observe that only affects
+// histogramCounts. doSparse is true if sparse buckets should be done,
+// too.
+func (hc *histogramCounts) observe(v float64, bucket int, doSparse bool) {
+	if bucket < len(hc.buckets) {
+		atomic.AddUint64(&hc.buckets[bucket], 1)
+	}
+	atomicAddFloat(&hc.sumBits, v)
+	if doSparse && !math.IsNaN(v) {
+		var (
+			key                  int
+			schema               = atomic.LoadInt32(&hc.nativeHistogramSchema)
+			zeroThreshold        = math.Float64frombits(atomic.LoadUint64(&hc.nativeHistogramZeroThresholdBits))
+			bucketCreated, isInf bool
+		)
+		if math.IsInf(v, 0) {
+			// Pretend v is MaxFloat64 but later increment key by one.
+			if math.IsInf(v, +1) {
+				v = math.MaxFloat64
+			} else {
+				v = -math.MaxFloat64
+			}
+			isInf = true
+		}
+		frac, exp := math.Frexp(math.Abs(v))
+		if schema > 0 {
+			bounds := nativeHistogramBounds[schema]
+			key = sort.SearchFloat64s(bounds, frac) + (exp-1)*len(bounds)
+		} else {
+			key = exp
+			if frac == 0.5 {
+				key--
+			}
+			div := 1 << -schema
+			key = (key + div - 1) / div
+		}
+		if isInf {
+			key++
+		}
+		switch {
+		case v > zeroThreshold:
+			bucketCreated = addToBucket(&hc.nativeHistogramBucketsPositive, key, 1)
+		case v < -zeroThreshold:
+			bucketCreated = addToBucket(&hc.nativeHistogramBucketsNegative, key, 1)
+		default:
+			atomic.AddUint64(&hc.nativeHistogramZeroBucket, 1)
+		}
+		if bucketCreated {
+			atomic.AddUint32(&hc.nativeHistogramBucketsNumber, 1)
+		}
+	}
+	// Increment count last as we take it as a signal that the observation
+	// is complete.
+	atomic.AddUint64(&hc.count, 1)
 }
 
 type histogram struct {
@@ -276,7 +667,7 @@ type histogram struct {
 	// perspective of the histogram) swap the hot–cold under the writeMtx
 	// lock. A cooldown is awaited (while locked) by comparing the number of
 	// observations with the initiation count. Once they match, then the
-	// last observation on the now cool one has completed. All cool fields must
+	// last observation on the now cool one has completed. All cold fields must
 	// be merged into the new hot before releasing writeMtx.
 	//
 	// Fields with atomic access first! See alignment constraint:
@@ -284,8 +675,10 @@ type histogram struct {
 	countAndHotIdx uint64
 
 	selfCollector
-	desc     *Desc
-	writeMtx sync.Mutex // Only used in the Write method.
+	desc *Desc
+
+	// Only used in the Write method and for sparse bucket management.
+	mtx sync.Mutex
 
 	// Two counts, one is "hot" for lock-free observations, the other is
 	// "cold" for writing out a dto.Metric. It has to be an array of
@@ -293,9 +686,15 @@ type histogram struct {
 	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
 	counts [2]*histogramCounts
 
-	upperBounds []float64
-	labelPairs  []*dto.LabelPair
-	exemplars   []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
+	upperBounds                     []float64
+	labelPairs                      []*dto.LabelPair
+	exemplars                       []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
+	nativeHistogramSchema           int32          // The initial schema. Set to math.MinInt32 if no sparse buckets are used.
+	nativeHistogramZeroThreshold    float64        // The initial zero threshold.
+	nativeHistogramMaxZeroThreshold float64
+	nativeHistogramMaxBuckets       uint32
+	nativeHistogramMinResetDuration time.Duration
+	lastResetTime                   time.Time // Protected by mtx.
 
 	now func() time.Time // To mock out time.Now() for testing.
 }
@@ -319,8 +718,8 @@ func (h *histogram) Write(out *dto.Metric) error {
 	// the hot path, i.e. Observe is called much more often than Write. The
 	// complication of making Write lock-free isn't worth it, if possible at
 	// all.
-	h.writeMtx.Lock()
-	defer h.writeMtx.Unlock()
+	h.mtx.Lock()
+	defer h.mtx.Unlock()
 
 	// Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
 	// without touching the count bits. See the struct comments for a full
@@ -333,16 +732,16 @@ func (h *histogram) Write(out *dto.Metric) error {
 	hotCounts := h.counts[n>>63]
 	coldCounts := h.counts[(^n)>>63]
 
-	// Await cooldown.
-	for count != atomic.LoadUint64(&coldCounts.count) {
-		runtime.Gosched() // Let observations get work done.
-	}
+	waitForCooldown(count, coldCounts)
 
 	his := &dto.Histogram{
 		Bucket:      make([]*dto.Bucket, len(h.upperBounds)),
 		SampleCount: proto.Uint64(count),
 		SampleSum:   proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
 	}
+	out.Histogram = his
+	out.Label = h.labelPairs
+
 	var cumCount uint64
 	for i, upperBound := range h.upperBounds {
 		cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
@@ -363,25 +762,21 @@ func (h *histogram) Write(out *dto.Metric) error {
 		}
 		his.Bucket = append(his.Bucket, b)
 	}
-
-	out.Histogram = his
-	out.Label = h.labelPairs
-
-	// Finally add all the cold counts to the new hot counts and reset the cold counts.
-	atomic.AddUint64(&hotCounts.count, count)
-	atomic.StoreUint64(&coldCounts.count, 0)
-	for {
-		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
-		newBits := math.Float64bits(math.Float64frombits(oldBits) + his.GetSampleSum())
-		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
-			atomic.StoreUint64(&coldCounts.sumBits, 0)
-			break
-		}
-	}
-	for i := range h.upperBounds {
-		atomic.AddUint64(&hotCounts.buckets[i], atomic.LoadUint64(&coldCounts.buckets[i]))
-		atomic.StoreUint64(&coldCounts.buckets[i], 0)
+	if h.nativeHistogramSchema > math.MinInt32 {
+		his.ZeroThreshold = proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.nativeHistogramZeroThresholdBits)))
+		his.Schema = proto.Int32(atomic.LoadInt32(&coldCounts.nativeHistogramSchema))
+		zeroBucket := atomic.LoadUint64(&coldCounts.nativeHistogramZeroBucket)
+
+		defer func() {
+			coldCounts.nativeHistogramBucketsPositive.Range(addAndReset(&hotCounts.nativeHistogramBucketsPositive, &hotCounts.nativeHistogramBucketsNumber))
+			coldCounts.nativeHistogramBucketsNegative.Range(addAndReset(&hotCounts.nativeHistogramBucketsNegative, &hotCounts.nativeHistogramBucketsNumber))
+		}()
+
+		his.ZeroCount = proto.Uint64(zeroBucket)
+		his.NegativeSpan, his.NegativeDelta = makeBuckets(&coldCounts.nativeHistogramBucketsNegative)
+		his.PositiveSpan, his.PositiveDelta = makeBuckets(&coldCounts.nativeHistogramBucketsPositive)
 	}
+	addAndResetCounts(hotCounts, coldCounts)
 	return nil
 }
 
@@ -402,25 +797,216 @@ func (h *histogram) findBucket(v float64) int {
 
 // observe is the implementation for Observe without the findBucket part.
 func (h *histogram) observe(v float64, bucket int) {
+	// Do not add to sparse buckets for NaN observations.
+	doSparse := h.nativeHistogramSchema > math.MinInt32 && !math.IsNaN(v)
 	// We increment h.countAndHotIdx so that the counter in the lower
 	// 63 bits gets incremented. At the same time, we get the new value
 	// back, which we can use to find the currently-hot counts.
 	n := atomic.AddUint64(&h.countAndHotIdx, 1)
 	hotCounts := h.counts[n>>63]
+	hotCounts.observe(v, bucket, doSparse)
+	if doSparse {
+		h.limitBuckets(hotCounts, v, bucket)
+	}
+}
 
-	if bucket < len(h.upperBounds) {
-		atomic.AddUint64(&hotCounts.buckets[bucket], 1)
+// limitSparsebuckets applies a strategy to limit the number of populated sparse
+// buckets. It's generally best effort, and there are situations where the
+// number can go higher (if even the lowest resolution isn't enough to reduce
+// the number sufficiently, or if the provided counts aren't fully updated yet
+// by a concurrently happening Write call).
+func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket int) {
+	if h.nativeHistogramMaxBuckets == 0 {
+		return // No limit configured.
 	}
-	for {
-		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
-		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
-		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
-			break
+	if h.nativeHistogramMaxBuckets >= atomic.LoadUint32(&counts.nativeHistogramBucketsNumber) {
+		return // Bucket limit not exceeded yet.
+	}
+
+	h.mtx.Lock()
+	defer h.mtx.Unlock()
+
+	// The hot counts might have been swapped just before we acquired the
+	// lock. Re-fetch the hot counts first...
+	n := atomic.LoadUint64(&h.countAndHotIdx)
+	hotIdx := n >> 63
+	coldIdx := (^n) >> 63
+	hotCounts := h.counts[hotIdx]
+	coldCounts := h.counts[coldIdx]
+	// ...and then check again if we really have to reduce the bucket count.
+	if h.nativeHistogramMaxBuckets >= atomic.LoadUint32(&hotCounts.nativeHistogramBucketsNumber) {
+		return // Bucket limit not exceeded after all.
+	}
+	// Try the various strategies in order.
+	if h.maybeReset(hotCounts, coldCounts, coldIdx, value, bucket) {
+		return
+	}
+	if h.maybeWidenZeroBucket(hotCounts, coldCounts) {
+		return
+	}
+	h.doubleBucketWidth(hotCounts, coldCounts)
+}
+
+// maybeReset resests the whole histogram if at least h.nativeHistogramMinResetDuration
+// has been passed. It returns true if the histogram has been reset. The caller
+// must have locked h.mtx.
+func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int) bool {
+	// We are using the possibly mocked h.now() rather than
+	// time.Since(h.lastResetTime) to enable testing.
+	if h.nativeHistogramMinResetDuration == 0 || h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
+		return false
+	}
+	// Completely reset coldCounts.
+	h.resetCounts(cold)
+	// Repeat the latest observation to not lose it completely.
+	cold.observe(value, bucket, true)
+	// Make coldCounts the new hot counts while ressetting countAndHotIdx.
+	n := atomic.SwapUint64(&h.countAndHotIdx, (coldIdx<<63)+1)
+	count := n & ((1 << 63) - 1)
+	waitForCooldown(count, hot)
+	// Finally, reset the formerly hot counts, too.
+	h.resetCounts(hot)
+	h.lastResetTime = h.now()
+	return true
+}
+
+// maybeWidenZeroBucket widens the zero bucket until it includes the existing
+// buckets closest to the zero bucket (which could be two, if an equidistant
+// negative and a positive bucket exists, but usually it's only one bucket to be
+// merged into the new wider zero bucket). h.nativeHistogramMaxZeroThreshold
+// limits how far the zero bucket can be extended, and if that's not enough to
+// include an existing bucket, the method returns false. The caller must have
+// locked h.mtx.
+func (h *histogram) maybeWidenZeroBucket(hot, cold *histogramCounts) bool {
+	currentZeroThreshold := math.Float64frombits(atomic.LoadUint64(&hot.nativeHistogramZeroThresholdBits))
+	if currentZeroThreshold >= h.nativeHistogramMaxZeroThreshold {
+		return false
+	}
+	// Find the key of the bucket closest to zero.
+	smallestKey := findSmallestKey(&hot.nativeHistogramBucketsPositive)
+	smallestNegativeKey := findSmallestKey(&hot.nativeHistogramBucketsNegative)
+	if smallestNegativeKey < smallestKey {
+		smallestKey = smallestNegativeKey
+	}
+	if smallestKey == math.MaxInt32 {
+		return false
+	}
+	newZeroThreshold := getLe(smallestKey, atomic.LoadInt32(&hot.nativeHistogramSchema))
+	if newZeroThreshold > h.nativeHistogramMaxZeroThreshold {
+		return false // New threshold would exceed the max threshold.
+	}
+	atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold))
+	// Remove applicable buckets.
+	if _, loaded := cold.nativeHistogramBucketsNegative.LoadAndDelete(smallestKey); loaded {
+		atomicDecUint32(&cold.nativeHistogramBucketsNumber)
+	}
+	if _, loaded := cold.nativeHistogramBucketsPositive.LoadAndDelete(smallestKey); loaded {
+		atomicDecUint32(&cold.nativeHistogramBucketsNumber)
+	}
+	// Make cold counts the new hot counts.
+	n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
+	count := n & ((1 << 63) - 1)
+	// Swap the pointer names to represent the new roles and make
+	// the rest less confusing.
+	hot, cold = cold, hot
+	waitForCooldown(count, cold)
+	// Add all the now cold counts to the new hot counts...
+	addAndResetCounts(hot, cold)
+	// ...adjust the new zero threshold in the cold counts, too...
+	atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold))
+	// ...and then merge the newly deleted buckets into the wider zero
+	// bucket.
+	mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v interface{}) bool {
+		return func(k, v interface{}) bool {
+			key := k.(int)
+			bucket := v.(*int64)
+			if key == smallestKey {
+				// Merge into hot zero bucket...
+				atomic.AddUint64(&hot.nativeHistogramZeroBucket, uint64(atomic.LoadInt64(bucket)))
+				// ...and delete from cold counts.
+				coldBuckets.Delete(key)
+				atomicDecUint32(&cold.nativeHistogramBucketsNumber)
+			} else {
+				// Add to corresponding hot bucket...
+				if addToBucket(hotBuckets, key, atomic.LoadInt64(bucket)) {
+					atomic.AddUint32(&hot.nativeHistogramBucketsNumber, 1)
+				}
+				// ...and reset cold bucket.
+				atomic.StoreInt64(bucket, 0)
+			}
+			return true
 		}
 	}
-	// Increment count last as we take it as a signal that the observation
-	// is complete.
-	atomic.AddUint64(&hotCounts.count, 1)
+
+	cold.nativeHistogramBucketsPositive.Range(mergeAndDeleteOrAddAndReset(&hot.nativeHistogramBucketsPositive, &cold.nativeHistogramBucketsPositive))
+	cold.nativeHistogramBucketsNegative.Range(mergeAndDeleteOrAddAndReset(&hot.nativeHistogramBucketsNegative, &cold.nativeHistogramBucketsNegative))
+	return true
+}
+
+// doubleBucketWidth doubles the bucket width (by decrementing the schema
+// number). Note that very sparse buckets could lead to a low reduction of the
+// bucket count (or even no reduction at all). The method does nothing if the
+// schema is already -4.
+func (h *histogram) doubleBucketWidth(hot, cold *histogramCounts) {
+	coldSchema := atomic.LoadInt32(&cold.nativeHistogramSchema)
+	if coldSchema == -4 {
+		return // Already at lowest resolution.
+	}
+	coldSchema--
+	atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema)
+	// Play it simple and just delete all cold buckets.
+	atomic.StoreUint32(&cold.nativeHistogramBucketsNumber, 0)
+	deleteSyncMap(&cold.nativeHistogramBucketsNegative)
+	deleteSyncMap(&cold.nativeHistogramBucketsPositive)
+	// Make coldCounts the new hot counts.
+	n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
+	count := n & ((1 << 63) - 1)
+	// Swap the pointer names to represent the new roles and make
+	// the rest less confusing.
+	hot, cold = cold, hot
+	waitForCooldown(count, cold)
+	// Add all the now cold counts to the new hot counts...
+	addAndResetCounts(hot, cold)
+	// ...adjust the schema in the cold counts, too...
+	atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema)
+	// ...and then merge the cold buckets into the wider hot buckets.
+	merge := func(hotBuckets *sync.Map) func(k, v interface{}) bool {
+		return func(k, v interface{}) bool {
+			key := k.(int)
+			bucket := v.(*int64)
+			// Adjust key to match the bucket to merge into.
+			if key > 0 {
+				key++
+			}
+			key /= 2
+			// Add to corresponding hot bucket.
+			if addToBucket(hotBuckets, key, atomic.LoadInt64(bucket)) {
+				atomic.AddUint32(&hot.nativeHistogramBucketsNumber, 1)
+			}
+			return true
+		}
+	}
+
+	cold.nativeHistogramBucketsPositive.Range(merge(&hot.nativeHistogramBucketsPositive))
+	cold.nativeHistogramBucketsNegative.Range(merge(&hot.nativeHistogramBucketsNegative))
+	// Play it simple again and just delete all cold buckets.
+	atomic.StoreUint32(&cold.nativeHistogramBucketsNumber, 0)
+	deleteSyncMap(&cold.nativeHistogramBucketsNegative)
+	deleteSyncMap(&cold.nativeHistogramBucketsPositive)
+}
+
+func (h *histogram) resetCounts(counts *histogramCounts) {
+	atomic.StoreUint64(&counts.sumBits, 0)
+	atomic.StoreUint64(&counts.count, 0)
+	atomic.StoreUint64(&counts.nativeHistogramZeroBucket, 0)
+	atomic.StoreUint64(&counts.nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold))
+	atomic.StoreInt32(&counts.nativeHistogramSchema, h.nativeHistogramSchema)
+	atomic.StoreUint32(&counts.nativeHistogramBucketsNumber, 0)
+	for i := range h.upperBounds {
+		atomic.StoreUint64(&counts.buckets[i], 0)
+	}
+	deleteSyncMap(&counts.nativeHistogramBucketsNegative)
+	deleteSyncMap(&counts.nativeHistogramBucketsPositive)
 }
 
 // updateExemplar replaces the exemplar for the provided bucket. With empty
@@ -516,7 +1102,8 @@ func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
 // GetMetricWithLabelValues would have returned an error. Not returning an
 // error allows shortcuts like
-//     myVec.WithLabelValues("404", "GET").Observe(42.21)
+//
+//	myVec.WithLabelValues("404", "GET").Observe(42.21)
 func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
 	h, err := v.GetMetricWithLabelValues(lvs...)
 	if err != nil {
@@ -527,7 +1114,8 @@ func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
 
 // With works as GetMetricWith but panics where GetMetricWithLabels would have
 // returned an error. Not returning an error allows shortcuts like
-//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
+//
+//	myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 func (v *HistogramVec) With(labels Labels) Observer {
 	h, err := v.GetMetricWith(labels)
 	if err != nil {
@@ -613,7 +1201,7 @@ func (h *constHistogram) Write(out *dto.Metric) error {
 // to send it to Prometheus in the Collect method.
 //
 // buckets is a map of upper bounds to cumulative counts, excluding the +Inf
-// bucket.
+// bucket. The +Inf bucket is implicit, and its value is equal to the provided count.
 //
 // NewConstHistogram returns an error if the length of labelValues is not
 // consistent with the variable labels in Desc or if Desc is invalid.
@@ -668,3 +1256,229 @@ func (s buckSort) Swap(i, j int) {
 func (s buckSort) Less(i, j int) bool {
 	return s[i].GetUpperBound() < s[j].GetUpperBound()
 }
+
+// pickSchema returns the largest number n between -4 and 8 such that
+// 2^(2^-n) is less or equal the provided bucketFactor.
+//
+// Special cases:
+//   - bucketFactor <= 1: panics.
+//   - bucketFactor < 2^(2^-8) (but > 1): still returns 8.
+func pickSchema(bucketFactor float64) int32 {
+	if bucketFactor <= 1 {
+		panic(fmt.Errorf("bucketFactor %f is <=1", bucketFactor))
+	}
+	floor := math.Floor(math.Log2(math.Log2(bucketFactor)))
+	switch {
+	case floor <= -8:
+		return 8
+	case floor >= 4:
+		return -4
+	default:
+		return -int32(floor)
+	}
+}
+
+func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) {
+	var ii []int
+	buckets.Range(func(k, v interface{}) bool {
+		ii = append(ii, k.(int))
+		return true
+	})
+	sort.Ints(ii)
+
+	if len(ii) == 0 {
+		return nil, nil
+	}
+
+	var (
+		spans     []*dto.BucketSpan
+		deltas    []int64
+		prevCount int64
+		nextI     int
+	)
+
+	appendDelta := func(count int64) {
+		*spans[len(spans)-1].Length++
+		deltas = append(deltas, count-prevCount)
+		prevCount = count
+	}
+
+	for n, i := range ii {
+		v, _ := buckets.Load(i)
+		count := atomic.LoadInt64(v.(*int64))
+		// Multiple spans with only small gaps in between are probably
+		// encoded more efficiently as one larger span with a few empty
+		// buckets. Needs some research to find the sweet spot. For now,
+		// we assume that gaps of one ore two buckets should not create
+		// a new span.
+		iDelta := int32(i - nextI)
+		if n == 0 || iDelta > 2 {
+			// We have to create a new span, either because we are
+			// at the very beginning, or because we have found a gap
+			// of more than two buckets.
+			spans = append(spans, &dto.BucketSpan{
+				Offset: proto.Int32(iDelta),
+				Length: proto.Uint32(0),
+			})
+		} else {
+			// We have found a small gap (or no gap at all).
+			// Insert empty buckets as needed.
+			for j := int32(0); j < iDelta; j++ {
+				appendDelta(0)
+			}
+		}
+		appendDelta(count)
+		nextI = i + 1
+	}
+	return spans, deltas
+}
+
+// addToBucket increments the sparse bucket at key by the provided amount. It
+// returns true if a new sparse bucket had to be created for that.
+func addToBucket(buckets *sync.Map, key int, increment int64) bool {
+	if existingBucket, ok := buckets.Load(key); ok {
+		// Fast path without allocation.
+		atomic.AddInt64(existingBucket.(*int64), increment)
+		return false
+	}
+	// Bucket doesn't exist yet. Slow path allocating new counter.
+	newBucket := increment // TODO(beorn7): Check if this is sufficient to not let increment escape.
+	if actualBucket, loaded := buckets.LoadOrStore(key, &newBucket); loaded {
+		// The bucket was created concurrently in another goroutine.
+		// Have to increment after all.
+		atomic.AddInt64(actualBucket.(*int64), increment)
+		return false
+	}
+	return true
+}
+
+// addAndReset returns a function to be used with sync.Map.Range of spare
+// buckets in coldCounts. It increments the buckets in the provided hotBuckets
+// according to the buckets ranged through. It then resets all buckets ranged
+// through to 0 (but leaves them in place so that they don't need to get
+// recreated on the next scrape).
+func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface{}) bool {
+	return func(k, v interface{}) bool {
+		bucket := v.(*int64)
+		if addToBucket(hotBuckets, k.(int), atomic.LoadInt64(bucket)) {
+			atomic.AddUint32(bucketNumber, 1)
+		}
+		atomic.StoreInt64(bucket, 0)
+		return true
+	}
+}
+
+func deleteSyncMap(m *sync.Map) {
+	m.Range(func(k, v interface{}) bool {
+		m.Delete(k)
+		return true
+	})
+}
+
+func findSmallestKey(m *sync.Map) int {
+	result := math.MaxInt32
+	m.Range(func(k, v interface{}) bool {
+		key := k.(int)
+		if key < result {
+			result = key
+		}
+		return true
+	})
+	return result
+}
+
+func getLe(key int, schema int32) float64 {
+	// Here a bit of context about the behavior for the last bucket counting
+	// regular numbers (called simply "last bucket" below) and the bucket
+	// counting observations of ±Inf (called "inf bucket" below, with a key
+	// one higher than that of the "last bucket"):
+	//
+	// If we apply the usual formula to the last bucket, its upper bound
+	// would be calculated as +Inf. The reason is that the max possible
+	// regular float64 number (math.MaxFloat64) doesn't coincide with one of
+	// the calculated bucket boundaries. So the calculated boundary has to
+	// be larger than math.MaxFloat64, and the only float64 larger than
+	// math.MaxFloat64 is +Inf. However, we want to count actual
+	// observations of ±Inf in the inf bucket. Therefore, we have to treat
+	// the upper bound of the last bucket specially and set it to
+	// math.MaxFloat64. (The upper bound of the inf bucket, with its key
+	// being one higher than that of the last bucket, naturally comes out as
+	// +Inf by the usual formula. So that's fine.)
+	//
+	// math.MaxFloat64 has a frac of 0.9999999999999999 and an exp of
+	// 1024. If there were a float64 number following math.MaxFloat64, it
+	// would have a frac of 1.0 and an exp of 1024, or equivalently a frac
+	// of 0.5 and an exp of 1025. However, since frac must be smaller than
+	// 1, and exp must be smaller than 1025, either representation overflows
+	// a float64. (Which, in turn, is the reason that math.MaxFloat64 is the
+	// largest possible float64. Q.E.D.) However, the formula for
+	// calculating the upper bound from the idx and schema of the last
+	// bucket results in precisely that. It is either frac=1.0 & exp=1024
+	// (for schema < 0) or frac=0.5 & exp=1025 (for schema >=0). (This is,
+	// by the way, a power of two where the exponent itself is a power of
+	// two, 2¹⁰ in fact, which coinicides with a bucket boundary in all
+	// schemas.) So these are the special cases we have to catch below.
+	if schema < 0 {
+		exp := key << -schema
+		if exp == 1024 {
+			// This is the last bucket before the overflow bucket
+			// (for ±Inf observations). Return math.MaxFloat64 as
+			// explained above.
+			return math.MaxFloat64
+		}
+		return math.Ldexp(1, exp)
+	}
+
+	fracIdx := key & ((1 << schema) - 1)
+	frac := nativeHistogramBounds[schema][fracIdx]
+	exp := (key >> schema) + 1
+	if frac == 0.5 && exp == 1025 {
+		// This is the last bucket before the overflow bucket (for ±Inf
+		// observations). Return math.MaxFloat64 as explained above.
+		return math.MaxFloat64
+	}
+	return math.Ldexp(frac, exp)
+}
+
+// waitForCooldown returns after the count field in the provided histogramCounts
+// has reached the provided count value.
+func waitForCooldown(count uint64, counts *histogramCounts) {
+	for count != atomic.LoadUint64(&counts.count) {
+		runtime.Gosched() // Let observations get work done.
+	}
+}
+
+// atomicAddFloat adds the provided float atomically to another float
+// represented by the bit pattern the bits pointer is pointing to.
+func atomicAddFloat(bits *uint64, v float64) {
+	for {
+		loadedBits := atomic.LoadUint64(bits)
+		newBits := math.Float64bits(math.Float64frombits(loadedBits) + v)
+		if atomic.CompareAndSwapUint64(bits, loadedBits, newBits) {
+			break
+		}
+	}
+}
+
+// atomicDecUint32 atomically decrements the uint32 p points to.  See
+// https://pkg.go.dev/sync/atomic#AddUint32 to understand how this is done.
+func atomicDecUint32(p *uint32) {
+	atomic.AddUint32(p, ^uint32(0))
+}
+
+// addAndResetCounts adds certain fields (count, sum, conventional buckets, zero
+// bucket) from the cold counts to the corresponding fields in the hot
+// counts. Those fields are then reset to 0 in the cold counts.
+func addAndResetCounts(hot, cold *histogramCounts) {
+	atomic.AddUint64(&hot.count, atomic.LoadUint64(&cold.count))
+	atomic.StoreUint64(&cold.count, 0)
+	coldSum := math.Float64frombits(atomic.LoadUint64(&cold.sumBits))
+	atomicAddFloat(&hot.sumBits, coldSum)
+	atomic.StoreUint64(&cold.sumBits, 0)
+	for i := range hot.buckets {
+		atomic.AddUint64(&hot.buckets[i], atomic.LoadUint64(&cold.buckets[i]))
+		atomic.StoreUint64(&cold.buckets[i], 0)
+	}
+	atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
+	atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/almost_equal.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/almost_equal.go
new file mode 100644
index 000000000..1ed5abe74
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/almost_equal.go
@@ -0,0 +1,60 @@
+// Copyright (c) 2015 Björn Rabenstein
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// The code in this package is copy/paste to avoid a dependency. Hence this file
+// carries the copyright of the original repo.
+// https://github.com/beorn7/floats
+package internal
+
+import (
+	"math"
+)
+
+// minNormalFloat64 is the smallest positive normal value of type float64.
+var minNormalFloat64 = math.Float64frombits(0x0010000000000000)
+
+// AlmostEqualFloat64 returns true if a and b are equal within a relative error
+// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the
+// details of the applied method.
+func AlmostEqualFloat64(a, b, epsilon float64) bool {
+	if a == b {
+		return true
+	}
+	absA := math.Abs(a)
+	absB := math.Abs(b)
+	diff := math.Abs(a - b)
+	if a == 0 || b == 0 || absA+absB < minNormalFloat64 {
+		return diff < epsilon*minNormalFloat64
+	}
+	return diff/math.Min(absA+absB, math.MaxFloat64) < epsilon
+}
+
+// AlmostEqualFloat64s is the slice form of AlmostEqualFloat64.
+func AlmostEqualFloat64s(a, b []float64, epsilon float64) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i := range a {
+		if !AlmostEqualFloat64(a[i], b[i], epsilon) {
+			return false
+		}
+	}
+	return true
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go
index fd45cadc0..fd0750f2c 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go
@@ -201,12 +201,15 @@ func (m *SequenceMatcher) isBJunk(s string) bool {
 // If IsJunk is not defined:
 //
 // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
-//     alo <= i <= i+k <= ahi
-//     blo <= j <= j+k <= bhi
+//
+//	alo <= i <= i+k <= ahi
+//	blo <= j <= j+k <= bhi
+//
 // and for all (i',j',k') meeting those conditions,
-//     k >= k'
-//     i <= i'
-//     and if i == i', j <= j'
+//
+//	k >= k'
+//	i <= i'
+//	and if i == i', j <= j'
 //
 // In other words, of all maximal matching blocks, return one that
 // starts earliest in a, and of all those maximal matching blocks that
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go
index 6eee198fe..c1b8fad36 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go
@@ -25,7 +25,8 @@ import (
 // Labels represents a collection of label name -> value mappings. This type is
 // commonly used with the With(Labels) and GetMetricWith(Labels) methods of
 // metric vector Collectors, e.g.:
-//     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
+//
+//	myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 //
 // The other use-case is the specification of constant label pairs in Opts or to
 // create a Desc.
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go
index f0941f6f0..b5119c504 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go
@@ -187,7 +187,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error {
 			} else {
 				// The +Inf bucket should be explicitly added if there is an exemplar for it, similar to non-const histogram logic in https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L357-L365.
 				b := &dto.Bucket{
-					CumulativeCount: proto.Uint64(pb.Histogram.Bucket[len(pb.Histogram.GetBucket())-1].GetCumulativeCount()),
+					CumulativeCount: proto.Uint64(pb.Histogram.GetSampleCount()),
 					UpperBound:      proto.Float64(math.Inf(1)),
 					Exemplar:        e,
 				}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
index 097aff2df..210867816 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
@@ -73,12 +73,11 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou
 	return func(r *http.Request) (*http.Response, error) {
 		resp, err := next.RoundTrip(r)
 		if err == nil {
-			exemplarAdd(
+			addWithExemplar(
 				counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)),
 				1,
 				rtOpts.getExemplarFn(r.Context()),
 			)
-			counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc()
 		}
 		return resp, err
 	}
@@ -117,7 +116,7 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT
 		start := time.Now()
 		resp, err := next.RoundTrip(r)
 		if err == nil {
-			exemplarObserve(
+			observeWithExemplar(
 				obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)),
 				time.Since(start).Seconds(),
 				rtOpts.getExemplarFn(r.Context()),
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
index bfe500987..cca67a78a 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
@@ -28,7 +28,9 @@ import (
 // magicString is used for the hacky label test in checkLabels. Remove once fixed.
 const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa"
 
-func exemplarObserve(obs prometheus.Observer, val float64, labels map[string]string) {
+// observeWithExemplar is a wrapper for [prometheus.ExemplarAdder.ExemplarObserver],
+// which falls back to [prometheus.Observer.Observe] if no labels are provided.
+func observeWithExemplar(obs prometheus.Observer, val float64, labels map[string]string) {
 	if labels == nil {
 		obs.Observe(val)
 		return
@@ -36,7 +38,9 @@ func exemplarObserve(obs prometheus.Observer, val float64, labels map[string]str
 	obs.(prometheus.ExemplarObserver).ObserveWithExemplar(val, labels)
 }
 
-func exemplarAdd(obs prometheus.Counter, val float64, labels map[string]string) {
+// addWithExemplar is a wrapper for [prometheus.ExemplarAdder.AddWithExemplar],
+// which falls back to [prometheus.Counter.Add] if no labels are provided.
+func addWithExemplar(obs prometheus.Counter, val float64, labels map[string]string) {
 	if labels == nil {
 		obs.Add(val)
 		return
@@ -91,7 +95,7 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op
 			d := newDelegator(w, nil)
 			next.ServeHTTP(d, r)
 
-			exemplarObserve(
+			observeWithExemplar(
 				obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)),
 				time.Since(now).Seconds(),
 				hOpts.getExemplarFn(r.Context()),
@@ -103,7 +107,7 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op
 		now := time.Now()
 		next.ServeHTTP(w, r)
 
-		exemplarObserve(
+		observeWithExemplar(
 			obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)),
 			time.Since(now).Seconds(),
 			hOpts.getExemplarFn(r.Context()),
@@ -141,7 +145,7 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler,
 			d := newDelegator(w, nil)
 			next.ServeHTTP(d, r)
 
-			exemplarAdd(
+			addWithExemplar(
 				counter.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)),
 				1,
 				hOpts.getExemplarFn(r.Context()),
@@ -151,7 +155,7 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler,
 
 	return func(w http.ResponseWriter, r *http.Request) {
 		next.ServeHTTP(w, r)
-		exemplarAdd(
+		addWithExemplar(
 			counter.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)),
 			1,
 			hOpts.getExemplarFn(r.Context()),
@@ -192,7 +196,7 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha
 	return func(w http.ResponseWriter, r *http.Request) {
 		now := time.Now()
 		d := newDelegator(w, func(status int) {
-			exemplarObserve(
+			observeWithExemplar(
 				obs.With(labels(code, method, r.Method, status, hOpts.extraMethods...)),
 				time.Since(now).Seconds(),
 				hOpts.getExemplarFn(r.Context()),
@@ -233,7 +237,7 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler,
 			d := newDelegator(w, nil)
 			next.ServeHTTP(d, r)
 			size := computeApproximateRequestSize(r)
-			exemplarObserve(
+			observeWithExemplar(
 				obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)),
 				float64(size),
 				hOpts.getExemplarFn(r.Context()),
@@ -244,7 +248,7 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler,
 	return func(w http.ResponseWriter, r *http.Request) {
 		next.ServeHTTP(w, r)
 		size := computeApproximateRequestSize(r)
-		exemplarObserve(
+		observeWithExemplar(
 			obs.With(labels(code, method, r.Method, 0, hOpts.extraMethods...)),
 			float64(size),
 			hOpts.getExemplarFn(r.Context()),
@@ -282,7 +286,7 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		d := newDelegator(w, nil)
 		next.ServeHTTP(d, r)
-		exemplarObserve(
+		observeWithExemplar(
 			obs.With(labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)),
 			float64(d.Written()),
 			hOpts.getExemplarFn(r.Context()),
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
index 325f665ff..09e34d307 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
@@ -252,9 +252,12 @@ func (errs MultiError) MaybeUnwrap() error {
 }
 
 // Registry registers Prometheus collectors, collects their metrics, and gathers
-// them into MetricFamilies for exposition. It implements both Registerer and
-// Gatherer. The zero value is not usable. Create instances with NewRegistry or
-// NewPedanticRegistry.
+// them into MetricFamilies for exposition. It implements Registerer, Gatherer,
+// and Collector. The zero value is not usable. Create instances with
+// NewRegistry or NewPedanticRegistry.
+//
+// Registry implements Collector to allow it to be used for creating groups of
+// metrics. See the Grouping example for how this can be done.
 type Registry struct {
 	mtx                   sync.RWMutex
 	collectorsByID        map[uint64]Collector // ID is a hash of the descIDs.
@@ -556,6 +559,31 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
 	return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
 }
 
+// Describe implements Collector.
+func (r *Registry) Describe(ch chan<- *Desc) {
+	r.mtx.RLock()
+	defer r.mtx.RUnlock()
+
+	// Only report the checked Collectors; unchecked collectors don't report any
+	// Desc.
+	for _, c := range r.collectorsByID {
+		c.Describe(ch)
+	}
+}
+
+// Collect implements Collector.
+func (r *Registry) Collect(ch chan<- Metric) {
+	r.mtx.RLock()
+	defer r.mtx.RUnlock()
+
+	for _, c := range r.collectorsByID {
+		c.Collect(ch)
+	}
+	for _, c := range r.uncheckedCollectors {
+		c.Collect(ch)
+	}
+}
+
 // WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
 // Prometheus text format, and writes it to a temporary file. Upon success, the
 // temporary file is renamed to the provided filename.
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go
index c5fa8ed7c..7bc448a89 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go
@@ -603,7 +603,8 @@ func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
 // GetMetricWithLabelValues would have returned an error. Not returning an
 // error allows shortcuts like
-//     myVec.WithLabelValues("404", "GET").Observe(42.21)
+//
+//	myVec.WithLabelValues("404", "GET").Observe(42.21)
 func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
 	s, err := v.GetMetricWithLabelValues(lvs...)
 	if err != nil {
@@ -614,7 +615,8 @@ func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
 
 // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 // returned an error. Not returning an error allows shortcuts like
-//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
+//
+//	myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 func (v *SummaryVec) With(labels Labels) Observer {
 	s, err := v.GetMetricWith(labels)
 	if err != nil {
@@ -701,7 +703,8 @@ func (s *constSummary) Write(out *dto.Metric) error {
 //
 // quantiles maps ranks to quantile values. For example, a median latency of
 // 0.23s and a 99th percentile latency of 0.56s would be expressed as:
-//     map[float64]float64{0.5: 0.23, 0.99: 0.56}
+//
+//	map[float64]float64{0.5: 0.23, 0.99: 0.56}
 //
 // NewConstSummary returns an error if the length of labelValues is not
 // consistent with the variable labels in Desc or if Desc is invalid.
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go
index 8d5f10523..f28a76f3a 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/timer.go
@@ -25,11 +25,12 @@ type Timer struct {
 // NewTimer creates a new Timer. The provided Observer is used to observe a
 // duration in seconds. Timer is usually used to time a function call in the
 // following way:
-//    func TimeMe() {
-//        timer := NewTimer(myHistogram)
-//        defer timer.ObserveDuration()
-//        // Do actual work.
-//    }
+//
+//	func TimeMe() {
+//	    timer := NewTimer(myHistogram)
+//	    defer timer.ObserveDuration()
+//	    // Do actual work.
+//	}
 func NewTimer(o Observer) *Timer {
 	return &Timer{
 		begin:    time.Now(),
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 5d8ee32df..014307ad4 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,4 +1,4 @@
-# github.com/aws/aws-sdk-go-v2 v1.17.3
+# github.com/aws/aws-sdk-go-v2 v1.17.4
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2
 github.com/aws/aws-sdk-go-v2/aws
@@ -40,16 +40,16 @@ github.com/aws/aws-sdk-go-v2/credentials/stscreds
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds/internal/config
-# github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27
+# github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/internal/configsources
-# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21
+# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2
 # github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/internal/ini
-# github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18
+# github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/internal/v4a
 github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto
@@ -57,18 +57,18 @@ github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4
 # github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding
-# github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22
+# github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/service/internal/checksum
-# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21
+# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url
-# github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21
+# github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared/arn
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared/config
-# github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0
+# github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2
 ## explicit; go 1.15
 github.com/aws/aws-sdk-go-v2/service/s3
 github.com/aws/aws-sdk-go-v2/service/s3/internal/arn
@@ -151,8 +151,8 @@ github.com/go-redis/redis/v8/internal/pool
 github.com/go-redis/redis/v8/internal/proto
 github.com/go-redis/redis/v8/internal/rand
 github.com/go-redis/redis/v8/internal/util
-# github.com/go-sql-driver/mysql v1.5.0
-## explicit; go 1.10
+# github.com/go-sql-driver/mysql v1.7.0
+## explicit; go 1.13
 github.com/go-sql-driver/mysql
 # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
 ## explicit
@@ -234,7 +234,7 @@ github.com/miekg/pkcs11
 # github.com/pelletier/go-toml v1.9.3
 ## explicit; go 1.12
 github.com/pelletier/go-toml
-# github.com/prometheus/client_golang v1.13.0
+# github.com/prometheus/client_golang v1.14.0
 ## explicit; go 1.17
 github.com/prometheus/client_golang/prometheus
 github.com/prometheus/client_golang/prometheus/collectors
diff --git a/web/context.go b/web/context.go
index aef35f0c7..3dbb610d3 100644
--- a/web/context.go
+++ b/web/context.go
@@ -39,9 +39,8 @@ type RequestEvent struct {
 	Contacts       []string `json:",omitempty"`
 	UserAgent      string   `json:"ua,omitempty"`
 	// Origin is sent by the browser from XHR-based clients.
-	Origin  string                 `json:",omitempty"`
-	Payload string                 `json:",omitempty"`
-	Extra   map[string]interface{} `json:",omitempty"`
+	Origin string                 `json:",omitempty"`
+	Extra  map[string]interface{} `json:",omitempty"`
 
 	// For endpoints that create objects, the ID of the newly created object.
 	Created string `json:",omitempty"`
@@ -49,8 +48,12 @@ type RequestEvent struct {
 	// For challenge and authorization GETs and POSTs:
 	// the status of the authorization at the time the request began.
 	Status string `json:",omitempty"`
-	// The DNS name, if applicable
+	// The DNS name, if there is a single relevant name, for instance
+	// in an authorization or challenge request.
 	DNSName string `json:",omitempty"`
+	// The set of DNS names, if there are potentially multiple relevant
+	// names, for instance in a new-order, finalize, or revoke request.
+	DNSNames []string `json:",omitempty"`
 
 	// For challenge POSTs, the challenge type.
 	ChallengeType string `json:",omitempty"`
diff --git a/wfe2/verify.go b/wfe2/verify.go
index 1f2188d26..69c5fe4e4 100644
--- a/wfe2/verify.go
+++ b/wfe2/verify.go
@@ -21,6 +21,7 @@ import (
 	berrors "github.com/letsencrypt/boulder/errors"
 	"github.com/letsencrypt/boulder/grpc"
 	"github.com/letsencrypt/boulder/nonce"
+	noncepb "github.com/letsencrypt/boulder/nonce/proto"
 	"github.com/letsencrypt/boulder/probs"
 	sapb "github.com/letsencrypt/boulder/sa/proto"
 	"github.com/letsencrypt/boulder/web"
@@ -195,9 +196,25 @@ func (wfe *WebFrontEndImpl) validNonce(ctx context.Context, jws *jose.JSONWebSig
 		wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMissingNonce"}).Inc()
 		return probs.BadNonce("JWS has no anti-replay nonce")
 	}
-	valid, err := nonce.RemoteRedeem(ctx, wfe.noncePrefixMap, header.Nonce)
-	if err != nil {
-		return web.ProblemDetailsForError(err, "failed to redeem nonce")
+	var valid bool
+	var err error
+	if wfe.noncePrefixMap == nil {
+		// Dispatch nonce redemption RPCs dynamically.
+		ctx = context.WithValue(ctx, nonce.PrefixCtxKey{}, header.Nonce[:nonce.PrefixLen])
+		ctx = context.WithValue(ctx, nonce.HMACKeyCtxKey{}, wfe.rncKey)
+		resp, err := wfe.rnc.Redeem(ctx, &noncepb.NonceMessage{Nonce: header.Nonce})
+		if err != nil {
+			return web.ProblemDetailsForError(err, "failed to redeem nonce")
+		}
+		valid = resp.Valid
+	} else {
+		// Dispatch nonce redpemption RPCs using a static mapping.
+		//
+		// TODO(#6610) Remove code below and the `npm` mapping.
+		valid, err = nonce.RemoteRedeem(ctx, wfe.noncePrefixMap, header.Nonce)
+		if err != nil {
+			return web.ProblemDetailsForError(err, "failed to redeem nonce")
+		}
 	}
 	if !valid {
 		wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSInvalidNonce"}).Inc()
@@ -500,8 +517,7 @@ func (wfe *WebFrontEndImpl) validJWSForKey(
 	ctx context.Context,
 	jws *jose.JSONWebSignature,
 	jwk *jose.JSONWebKey,
-	request *http.Request,
-	logEvent *web.RequestEvent) ([]byte, *probs.ProblemDetails) {
+	request *http.Request) ([]byte, *probs.ProblemDetails) {
 
 	// Check that the public key and JWS algorithms match expected
 	err := checkAlgorithm(jwk, jws)
@@ -521,9 +537,6 @@ func (wfe *WebFrontEndImpl) validJWSForKey(
 		wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSVerifyFailed"}).Inc()
 		return nil, probs.Malformed("JWS verification error")
 	}
-	// Store the verified payload in the logEvent
-	logEvent.Payload = string(payload)
-	beeline.AddFieldToTrace(ctx, "payload", string(payload))
 
 	// Check that the JWS contains a correct Nonce header
 	if prob := wfe.validNonce(ctx, jws); prob != nil {
@@ -569,7 +582,7 @@ func (wfe *WebFrontEndImpl) validJWSForAccount(
 	}
 
 	// Verify the JWS with the JWK from the SA
-	payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request, logEvent)
+	payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request)
 	if prob != nil {
 		return nil, nil, nil, prob
 	}
@@ -641,8 +654,7 @@ func (wfe *WebFrontEndImpl) validPOSTAsGETForAccount(
 func (wfe *WebFrontEndImpl) validSelfAuthenticatedJWS(
 	ctx context.Context,
 	jws *jose.JSONWebSignature,
-	request *http.Request,
-	logEvent *web.RequestEvent) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
+	request *http.Request) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
 	// Extract the embedded JWK from the parsed JWS
 	pubKey, prob := wfe.extractJWK(jws)
 	if prob != nil {
@@ -650,7 +662,7 @@ func (wfe *WebFrontEndImpl) validSelfAuthenticatedJWS(
 	}
 
 	// Verify the JWS with the embedded JWK
-	payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request, logEvent)
+	payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request)
 	if prob != nil {
 		return nil, nil, prob
 	}
@@ -663,8 +675,7 @@ func (wfe *WebFrontEndImpl) validSelfAuthenticatedJWS(
 // goodkey policies (key algorithm, length, blocklist, etc).
 func (wfe *WebFrontEndImpl) validSelfAuthenticatedPOST(
 	ctx context.Context,
-	request *http.Request,
-	logEvent *web.RequestEvent) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
+	request *http.Request) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
 	// Parse the JWS from the POST request
 	jws, prob := wfe.parseJWSRequest(request)
 	if prob != nil {
@@ -672,7 +683,7 @@ func (wfe *WebFrontEndImpl) validSelfAuthenticatedPOST(
 	}
 
 	// Extract and validate the embedded JWK from the parsed JWS
-	payload, pubKey, prob := wfe.validSelfAuthenticatedJWS(ctx, jws, request, logEvent)
+	payload, pubKey, prob := wfe.validSelfAuthenticatedJWS(ctx, jws, request)
 	if prob != nil {
 		return nil, nil, prob
 	}
diff --git a/wfe2/wfe.go b/wfe2/wfe.go
index 743095c2f..c5da22663 100644
--- a/wfe2/wfe.go
+++ b/wfe2/wfe.go
@@ -26,11 +26,14 @@ import (
 	"github.com/letsencrypt/boulder/features"
 	"github.com/letsencrypt/boulder/goodkey"
 	bgrpc "github.com/letsencrypt/boulder/grpc"
+	"github.com/letsencrypt/boulder/nonce"
+
+	// 'grpc/noncebalancer' is imported for its init function.
+	_ "github.com/letsencrypt/boulder/grpc/noncebalancer"
 	"github.com/letsencrypt/boulder/identifier"
 	"github.com/letsencrypt/boulder/issuance"
 	blog "github.com/letsencrypt/boulder/log"
 	"github.com/letsencrypt/boulder/metrics/measured_http"
-	noncepb "github.com/letsencrypt/boulder/nonce/proto"
 	"github.com/letsencrypt/boulder/probs"
 	rapb "github.com/letsencrypt/boulder/ra/proto"
 	"github.com/letsencrypt/boulder/revocation"
@@ -84,8 +87,25 @@ var errIncompleteGRPCResponse = errors.New("incomplete gRPC response message")
 // plus a few other data items used in ACME.  Its methods are primarily handlers
 // for HTTPS requests for the various ACME functions.
 type WebFrontEndImpl struct {
-	ra            rapb.RegistrationAuthorityClient
-	sa            sapb.StorageAuthorityReadOnlyClient
+	ra rapb.RegistrationAuthorityClient
+	sa sapb.StorageAuthorityReadOnlyClient
+	// gnc is a nonce-service client used exclusively for the issuance of
+	// nonces. It's configured to route requests to backends colocated with the
+	// WFE.
+	gnc nonce.Getter
+	// Register of anti-replay nonces
+	//
+	// Deprecated: See `rnc`, above.
+	noncePrefixMap map[string]nonce.Redeemer
+	// rnc is a nonce-service client used exclusively for the redemption of
+	// nonces. It uses a custom RPC load balancer which is configured to route
+	// requests to backends based on the prefix and HMAC key passed as in the
+	// context of the request. The HMAC and prefix are passed using context keys
+	// `nonce.HMACKeyCtxKey` and `nonce.PrefixCtxKey`.
+	rnc nonce.Redeemer
+	// rncKey is the HMAC key used to derive the prefix of nonce backends used
+	// for nonce redemption.
+	rncKey        string
 	accountGetter AccountGetter
 	log           blog.Logger
 	clk           clock.Clock
@@ -119,10 +139,6 @@ type WebFrontEndImpl struct {
 	// `LegacyKeyIDPrefix` for more information.
 	LegacyKeyIDPrefix string
 
-	// Register of anti-replay nonces
-	remoteNonceService noncepb.NonceServiceClient
-	noncePrefixMap     map[string]noncepb.NonceServiceClient
-
 	// Key policy.
 	keyPolicy goodkey.KeyPolicy
 
@@ -153,8 +169,6 @@ func NewWebFrontEndImpl(
 	keyPolicy goodkey.KeyPolicy,
 	certificateChains map[issuance.IssuerNameID][][]byte,
 	issuerCertificates map[issuance.IssuerNameID]*issuance.Certificate,
-	remoteNonceService noncepb.NonceServiceClient,
-	noncePrefixMap map[string]noncepb.NonceServiceClient,
 	logger blog.Logger,
 	requestTimeout time.Duration,
 	staleTimeout time.Duration,
@@ -162,6 +176,10 @@ func NewWebFrontEndImpl(
 	pendingAuthorizationLifetime time.Duration,
 	rac rapb.RegistrationAuthorityClient,
 	sac sapb.StorageAuthorityReadOnlyClient,
+	gnc nonce.Getter,
+	noncePrefixMap map[string]nonce.Redeemer,
+	rnc nonce.Redeemer,
+	rncKey string,
 	accountGetter AccountGetter,
 ) (WebFrontEndImpl, error) {
 	if len(issuerCertificates) == 0 {
@@ -172,12 +190,13 @@ func NewWebFrontEndImpl(
 		return WebFrontEndImpl{}, errors.New("must provide at least one certificate chain")
 	}
 
-	if remoteNonceService == nil {
+	if gnc == nil {
 		return WebFrontEndImpl{}, errors.New("must provide a service for nonce issuance")
 	}
 
-	if noncePrefixMap == nil {
-		return WebFrontEndImpl{}, errors.New("must provide a set of services for nonce redemption")
+	// TODO(#6610): Remove the check for the map.
+	if noncePrefixMap == nil && rnc == nil {
+		return WebFrontEndImpl{}, errors.New("must provide a service for nonce redemption")
 	}
 
 	wfe := WebFrontEndImpl{
@@ -187,14 +206,16 @@ func NewWebFrontEndImpl(
 		certificateChains:            certificateChains,
 		issuerCertificates:           issuerCertificates,
 		stats:                        initStats(stats),
-		remoteNonceService:           remoteNonceService,
-		noncePrefixMap:               noncePrefixMap,
 		requestTimeout:               requestTimeout,
 		staleTimeout:                 staleTimeout,
 		authorizationLifetime:        authorizationLifetime,
 		pendingAuthorizationLifetime: pendingAuthorizationLifetime,
 		ra:                           rac,
 		sa:                           sac,
+		gnc:                          gnc,
+		noncePrefixMap:               noncePrefixMap,
+		rnc:                          rnc,
+		rncKey:                       rncKey,
 		accountGetter:                accountGetter,
 	}
 
@@ -244,7 +265,7 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h web
 				return
 			}
 			if request.Method != "GET" || pattern == newNoncePath {
-				nonceMsg, err := wfe.remoteNonceService.Nonce(ctx, &emptypb.Empty{})
+				nonceMsg, err := wfe.gnc.Nonce(ctx, &emptypb.Empty{})
 				if err != nil {
 					wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "unable to get nonce"), err)
 					return
@@ -595,7 +616,7 @@ func (wfe *WebFrontEndImpl) NewAccount(
 	// NewAccount uses `validSelfAuthenticatedPOST` instead of
 	// `validPOSTforAccount` because there is no account to authenticate against
 	// until after it is created!
-	body, key, prob := wfe.validSelfAuthenticatedPOST(ctx, request, logEvent)
+	body, key, prob := wfe.validSelfAuthenticatedPOST(ctx, request)
 	if prob != nil {
 		// validSelfAuthenticatedPOST handles its own setting of logEvent.Errors
 		wfe.sendError(response, logEvent, prob, nil)
@@ -794,6 +815,9 @@ func (wfe *WebFrontEndImpl) parseRevocation(
 	// Compute and record the serial number of the provided certificate
 	serial := core.SerialToString(parsedCertificate.SerialNumber)
 	logEvent.Extra["CertificateSerial"] = serial
+	if revokeRequest.Reason != nil {
+		logEvent.Extra["RevocationReason"] = *revokeRequest.Reason
+	}
 	beeline.AddFieldToTrace(ctx, "cert.serial", serial)
 
 	// Try to validate the signature on the provided cert using its corresponding
@@ -807,8 +831,8 @@ func (wfe *WebFrontEndImpl) parseRevocation(
 	if err != nil {
 		return nil, 0, probs.NotFound("No such certificate")
 	}
-	logEvent.Extra["CertificateDNSNames"] = parsedCertificate.DNSNames
-	beeline.AddFieldToTrace(ctx, "cert.dnsnames", parsedCertificate.DNSNames)
+	logEvent.DNSNames = parsedCertificate.DNSNames
+	beeline.AddFieldToTrace(ctx, "dnsnames", parsedCertificate.DNSNames)
 
 	if parsedCertificate.NotAfter.Before(wfe.clk.Now()) {
 		return nil, 0, probs.Unauthorized("Certificate is expired")
@@ -895,7 +919,7 @@ func (wfe *WebFrontEndImpl) revokeCertByCertKey(
 	// `validSelfAuthenticatedJWS` similar to new-reg and key rollover.
 	// We do *not* use `validSelfAuthenticatedPOST` here because we've already
 	// read the HTTP request body in `parseJWSRequest` and it is now empty.
-	jwsBody, jwk, prob := wfe.validSelfAuthenticatedJWS(ctx, outerJWS, request, logEvent)
+	jwsBody, jwk, prob := wfe.validSelfAuthenticatedJWS(ctx, outerJWS, request)
 	if prob != nil {
 		return prob
 	}
@@ -1123,10 +1147,6 @@ func (wfe *WebFrontEndImpl) prepAuthorizationForDisplay(request *http.Request, a
 	authz.ID = ""
 	authz.RegistrationID = 0
 
-	// Combinations are a relic of the V1 API. Since they are tagged omitempty we
-	// can set this field to nil to avoid sending it to users of the V2 API.
-	authz.Combinations = nil
-
 	// The ACME spec forbids allowing "*" in authorization identifiers. Boulder
 	// allows this internally as a means of tracking when an authorization
 	// corresponds to a wildcard request (e.g. to handle CAA properly). We strip
@@ -2018,6 +2038,8 @@ func (wfe *WebFrontEndImpl) NewOrder(
 		return
 	}
 
+	logEvent.DNSNames = names
+
 	order, err := wfe.ra.NewOrder(ctx, &rapb.NewOrderRequest{
 		RegistrationID: acct.ID,
 		Names:          names,
@@ -2213,12 +2235,8 @@ func (wfe *WebFrontEndImpl) FinalizeOrder(ctx context.Context, logEvent *web.Req
 		return
 	}
 
-	logEvent.Extra["CSRDNSNames"] = csr.DNSNames
-	beeline.AddFieldToTrace(ctx, "csr.dnsnames", csr.DNSNames)
-	logEvent.Extra["CSREmailAddresses"] = csr.EmailAddresses
-	beeline.AddFieldToTrace(ctx, "csr.email_addrs", csr.EmailAddresses)
-	logEvent.Extra["CSRIPAddresses"] = csr.IPAddresses
-	beeline.AddFieldToTrace(ctx, "csr.ip_addrs", csr.IPAddresses)
+	logEvent.DNSNames = order.Names
+	beeline.AddFieldToTrace(ctx, "dnsnames", csr.DNSNames)
 	logEvent.Extra["KeyType"] = web.KeyTypeToString(csr.PublicKey)
 	beeline.AddFieldToTrace(ctx, "csr.key_type", web.KeyTypeToString(csr.PublicKey))
 
@@ -2266,18 +2284,23 @@ func (wfe *WebFrontEndImpl) RenewalInfo(ctx context.Context, logEvent *web.Reque
 		return
 	}
 
+	if len(request.URL.Path) == 0 {
+		wfe.sendError(response, logEvent, probs.NotFound("Must specify a request path"), nil)
+		return
+	}
+
 	// The path prefix has already been stripped, so request.URL.Path here is just
 	// the base64url-encoded DER CertID sequence.
 	der, err := base64.RawURLEncoding.DecodeString(request.URL.Path)
 	if err != nil {
-		wfe.sendError(response, logEvent, probs.Malformed("Path was not base64url-encoded: %w", err), nil)
+		wfe.sendError(response, logEvent, probs.Malformed("Path was not base64url-encoded"), nil)
 		return
 	}
 
 	var id certID
 	rest, err := asn1.Unmarshal(der, &id)
 	if err != nil || len(rest) != 0 {
-		wfe.sendError(response, logEvent, probs.Malformed("Path was not a DER-encoded CertID sequence: %w", err), nil)
+		wfe.sendError(response, logEvent, probs.Malformed("Path was not a DER-encoded CertID sequence"), nil)
 		return
 	}
 



Test patch:
diff --git a/akamai/cache-client_test.go b/akamai/cache-client_test.go
index 246db64ac..5d9342c67 100644
--- a/akamai/cache-client_test.go
+++ b/akamai/cache-client_test.go
@@ -134,8 +134,7 @@ func TestV3Purge(t *testing.T) {
 		metrics.NoopRegisterer,
 	)
 	test.AssertNotError(t, err, "Failed to create CachePurgeClient")
-	fc := clock.NewFake()
-	client.clk = fc
+	client.clk = clock.NewFake()
 
 	err = client.Purge([]string{"http://test.com"})
 	test.AssertNotError(t, err, "Purge failed; expected 201 response")
@@ -145,7 +144,10 @@ func TestV3Purge(t *testing.T) {
 	err = client.Purge([]string{"http://test.com"})
 	test.AssertError(t, err, "Purge succeeded; expected 500 response")
 	t.Log(client.clk.Since(started))
-	test.Assert(t, client.clk.Since(started) > (time.Second*4), "Retries should've taken at least 4.4 seconds")
+	// Given 3 retries, with a retry interval of 1 second, a growth factor of 1.3,
+	// and a jitter of 0.2, the minimum amount of elapsed time is:
+	// (1 * 0.8) + (1 * 1.3 * 0.8) + (1 * 1.3 * 1.3 * 0.8) = 3.192s
+	test.Assert(t, client.clk.Since(started) > (time.Second*3), "Retries should've taken at least 3.192 seconds")
 
 	started = client.clk.Now()
 	as.responseCode = http.StatusCreated
diff --git a/cmd/cert-checker/main_test.go b/cmd/cert-checker/main_test.go
index 03997d0f5..94bdf98b4 100644
--- a/cmd/cert-checker/main_test.go
+++ b/cmd/cert-checker/main_test.go
@@ -28,6 +28,7 @@ import (
 	"github.com/letsencrypt/boulder/core"
 	"github.com/letsencrypt/boulder/ctpolicy/loglist"
 	"github.com/letsencrypt/boulder/goodkey"
+	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
 	blog "github.com/letsencrypt/boulder/log"
 	"github.com/letsencrypt/boulder/metrics"
 	"github.com/letsencrypt/boulder/policy"
@@ -56,7 +57,7 @@ func init() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	kp, err = goodkey.NewKeyPolicy(&goodkey.Config{FermatRounds: 100}, nil)
+	kp, err = sagoodkey.NewKeyPolicy(&goodkey.Config{FermatRounds: 100}, nil)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/cmd/rocsp-tool/client_test.go b/cmd/rocsp-tool/client_test.go
index 17ecf7b0d..0c522af5c 100644
--- a/cmd/rocsp-tool/client_test.go
+++ b/cmd/rocsp-tool/client_test.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"math/big"
-	"os"
 	"testing"
 	"time"
 
@@ -39,8 +38,8 @@ func makeClient() (*rocsp.RWClient, clock.Clock) {
 
 	rdb := redis.NewRing(&redis.RingOptions{
 		Addrs: map[string]string{
-			"shard1": "10.33.33.8:4218",
-			"shard2": "10.33.33.9:4218",
+			"shard1": "10.33.33.2:4218",
+			"shard2": "10.33.33.3:4218",
 		},
 		Username:  "unittest-rw",
 		Password:  "824968fa490f4ecec1e52d5e34916bdb60d45f8d",
@@ -50,32 +49,6 @@ func makeClient() (*rocsp.RWClient, clock.Clock) {
 	return rocsp.NewWritingClient(rdb, 500*time.Millisecond, clk, metrics.NoopRegisterer), clk
 }
 
-// TODO(#6517) remove this helper.
-func makeClusterClient() (*rocsp.CRWClient, clock.Clock) {
-	CACertFile := "../../test/redis-tls/minica.pem"
-	CertFile := "../../test/redis-tls/boulder/cert.pem"
-	KeyFile := "../../test/redis-tls/boulder/key.pem"
-	tlsConfig := cmd.TLSConfig{
-		CACertFile: &CACertFile,
-		CertFile:   &CertFile,
-		KeyFile:    &KeyFile,
-	}
-	tlsConfig2, err := tlsConfig.Load()
-	if err != nil {
-		panic(err)
-	}
-
-	rdb := redis.NewClusterClient(&redis.ClusterOptions{
-		Addrs:     []string{"10.33.33.2:4218"},
-		Username:  "unittest-rw",
-		Password:  "824968fa490f4ecec1e52d5e34916bdb60d45f8d",
-		TLSConfig: tlsConfig2,
-	})
-	clk := clock.NewFake()
-
-	return rocsp.NewClusterWritingClient(rdb, 5*time.Second, clk, metrics.NoopRegisterer), clk
-}
-
 func TestGetStartingID(t *testing.T) {
 	clk := clock.NewFake()
 	dbMap, err := sa.NewDbMap(vars.DBConnSAFullPerms, sa.DbSettings{})
@@ -109,14 +82,7 @@ func TestGetStartingID(t *testing.T) {
 }
 
 func TestStoreResponse(t *testing.T) {
-	// TODO(#6517) remove this block.
-	var redisClient rocsp.Writer
-	var clk clock.Clock
-	if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" {
-		redisClient, clk = makeClusterClient()
-	} else {
-		redisClient, clk = makeClient()
-	}
+	redisClient, clk := makeClient()
 
 	issuer, err := core.LoadCert("../../test/hierarchy/int-e1.cert.pem")
 	test.AssertNotError(t, err, "loading int-e1")
@@ -153,14 +119,7 @@ func (mog mockOCSPGenerator) GenerateOCSP(ctx context.Context, in *capb.Generate
 }
 
 func TestLoadFromDB(t *testing.T) {
-	// TODO(#6517) remove this block.
-	var redisClient rocsp.Writer
-	var clk clock.Clock
-	if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" {
-		redisClient, clk = makeClusterClient()
-	} else {
-		redisClient, clk = makeClient()
-	}
+	redisClient, clk := makeClient()
 
 	dbMap, err := sa.NewDbMap(vars.DBConnSA, sa.DbSettings{})
 	if err != nil {
diff --git a/db/map_test.go b/db/map_test.go
index 4c2e90f37..a7ab2fe55 100644
--- a/db/map_test.go
+++ b/db/map_test.go
@@ -48,7 +48,7 @@ func TestErrDatabaseOpError(t *testing.T) {
 	}
 }
 
-func TestErrDatabaseOpNoRows(t *testing.T) {
+func TestIsNoRows(t *testing.T) {
 	testCases := []struct {
 		name           string
 		err            ErrDatabaseOp
@@ -59,7 +59,7 @@ func TestErrDatabaseOpNoRows(t *testing.T) {
 			err: ErrDatabaseOp{
 				Op:    "test",
 				Table: "testTable",
-				Err:   sql.ErrNoRows,
+				Err:   fmt.Errorf("some wrapper around %w", sql.ErrNoRows),
 			},
 			expectedNoRows: true,
 		},
@@ -68,7 +68,7 @@ func TestErrDatabaseOpNoRows(t *testing.T) {
 			err: ErrDatabaseOp{
 				Op:    "test",
 				Table: "testTable",
-				Err:   errors.New("lots of rows. too many rows."),
+				Err:   fmt.Errorf("some wrapper around %w", errors.New("lots of rows. too many rows.")),
 			},
 			expectedNoRows: false,
 		},
@@ -76,12 +76,12 @@ func TestErrDatabaseOpNoRows(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			test.AssertEquals(t, tc.err.noRows(), tc.expectedNoRows)
+			test.AssertEquals(t, IsNoRows(tc.err), tc.expectedNoRows)
 		})
 	}
 }
 
-func TestErrDatabaseOpDuplicate(t *testing.T) {
+func TestIsDuplicate(t *testing.T) {
 	testCases := []struct {
 		name            string
 		err             ErrDatabaseOp
@@ -92,7 +92,7 @@ func TestErrDatabaseOpDuplicate(t *testing.T) {
 			err: ErrDatabaseOp{
 				Op:    "test",
 				Table: "testTable",
-				Err:   errors.New("Error 1062: Duplicate entry detected!!!!!!!"),
+				Err:   fmt.Errorf("some wrapper around %w", &mysql.MySQLError{Number: 1062}),
 			},
 			expectDuplicate: true,
 		},
@@ -101,7 +101,7 @@ func TestErrDatabaseOpDuplicate(t *testing.T) {
 			err: ErrDatabaseOp{
 				Op:    "test",
 				Table: "testTable",
-				Err:   errors.New("DB forgot to save your data."),
+				Err:   fmt.Errorf("some wrapper around %w", &mysql.MySQLError{Number: 1234}),
 			},
 			expectDuplicate: false,
 		},
@@ -109,7 +109,7 @@ func TestErrDatabaseOpDuplicate(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			test.AssertEquals(t, tc.err.duplicate(), tc.expectDuplicate)
+			test.AssertEquals(t, IsDuplicate(tc.err), tc.expectDuplicate)
 		})
 	}
 }
diff --git a/goodkey/good_key_test.go b/goodkey/good_key_test.go
index d490a70a7..a7d7c3c95 100644
--- a/goodkey/good_key_test.go
+++ b/goodkey/good_key_test.go
@@ -10,9 +10,7 @@ import (
 	"math/big"
 	"testing"
 
-	sapb "github.com/letsencrypt/boulder/sa/proto"
 	"github.com/letsencrypt/boulder/test"
-	"google.golang.org/grpc"
 )
 
 var testingPolicy = &KeyPolicy{
@@ -259,22 +257,25 @@ func TestNonRefKey(t *testing.T) {
 }
 
 func TestDBBlocklistAccept(t *testing.T) {
-	testCheck := func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error) {
-		return &sapb.Exists{Exists: false}, nil
+	for _, testCheck := range []BlockedKeyCheckFunc{
+		nil,
+		func(context.Context, []byte) (bool, error) {
+			return false, nil
+		},
+	} {
+		policy, err := NewKeyPolicy(&Config{}, testCheck)
+		test.AssertNotError(t, err, "NewKeyPolicy failed")
+
+		k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+		test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
+		err = policy.GoodKey(context.Background(), k.Public())
+		test.AssertNotError(t, err, "GoodKey failed with a non-blocked key")
 	}
-
-	policy, err := NewKeyPolicy(&Config{}, testCheck)
-	test.AssertNotError(t, err, "NewKeyPolicy failed")
-
-	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
-	err = policy.GoodKey(context.Background(), k.Public())
-	test.AssertNotError(t, err, "GoodKey failed with a non-blocked key")
 }
 
 func TestDBBlocklistReject(t *testing.T) {
-	testCheck := func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error) {
-		return &sapb.Exists{Exists: true}, nil
+	testCheck := func(context.Context, []byte) (bool, error) {
+		return true, nil
 	}
 
 	policy, err := NewKeyPolicy(&Config{}, testCheck)
diff --git a/goodkey/sagoodkey/good_key_test.go b/goodkey/sagoodkey/good_key_test.go
new file mode 100644
index 000000000..b7e47fcac
--- /dev/null
+++ b/goodkey/sagoodkey/good_key_test.go
@@ -0,0 +1,47 @@
+package sagoodkey
+
+import (
+	"context"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"testing"
+
+	"github.com/letsencrypt/boulder/goodkey"
+	sapb "github.com/letsencrypt/boulder/sa/proto"
+	"github.com/letsencrypt/boulder/test"
+	"google.golang.org/grpc"
+)
+
+func TestDBBlocklistAccept(t *testing.T) {
+	for _, testCheck := range []BlockedKeyCheckFunc{
+		nil,
+		func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error) {
+			return &sapb.Exists{Exists: false}, nil
+		},
+	} {
+		policy, err := NewKeyPolicy(&goodkey.Config{}, testCheck)
+		test.AssertNotError(t, err, "NewKeyPolicy failed")
+
+		k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+		test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
+		err = policy.GoodKey(context.Background(), k.Public())
+		test.AssertNotError(t, err, "GoodKey failed with a non-blocked key")
+	}
+}
+
+func TestDBBlocklistReject(t *testing.T) {
+	testCheck := func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error) {
+		return &sapb.Exists{Exists: true}, nil
+	}
+
+	policy, err := NewKeyPolicy(&goodkey.Config{}, testCheck)
+	test.AssertNotError(t, err, "NewKeyPolicy failed")
+
+	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
+	err = policy.GoodKey(context.Background(), k.Public())
+	test.AssertError(t, err, "GoodKey didn't fail with a blocked key")
+	test.AssertErrorIs(t, err, goodkey.ErrBadKey)
+	test.AssertEquals(t, err.Error(), "public key is forbidden")
+}
diff --git a/grpc/internal/resolver/dns/dns_resolver_test.go b/grpc/internal/resolver/dns/dns_resolver_test.go
index d5d3d3b14..dff7d17b6 100644
--- a/grpc/internal/resolver/dns/dns_resolver_test.go
+++ b/grpc/internal/resolver/dns/dns_resolver_test.go
@@ -223,7 +223,7 @@ func testDNSResolver(t *testing.T) {
 	}
 
 	for _, a := range tests {
-		b := NewBuilder()
+		b := NewDefaultSRVBuilder()
 		cc := &testClientConn{target: a.target}
 		r, err := b.Build(resolver.Target{Endpoint: a.target}, cc, resolver.BuildOptions{})
 		if err != nil {
@@ -266,7 +266,7 @@ func TestDNSResolverExponentialBackoff(t *testing.T) {
 	target := "foo.ipv4.single.fake"
 	wantAddr := []resolver.Address{{Addr: "2.4.6.8:1234", ServerName: "ipv4.single.fake"}}
 
-	b := NewBuilder()
+	b := NewDefaultSRVBuilder()
 	cc := &testClientConn{target: target}
 	// Cause ClientConn to return an error.
 	cc.updateStateErr = balancer.ErrBadResolverState
@@ -395,7 +395,7 @@ func testDNSResolveNow(t *testing.T) {
 	}
 
 	for _, a := range tests {
-		b := NewBuilder()
+		b := NewDefaultSRVBuilder()
 		cc := &testClientConn{target: a.target}
 		r, err := b.Build(resolver.Target{Endpoint: a.target}, cc, resolver.BuildOptions{})
 		if err != nil {
@@ -445,7 +445,7 @@ func TestDNSResolverRetry(t *testing.T) {
 		// Will never fire on its own, will protect from triggering exponential backoff.
 		return time.NewTimer(time.Hour)
 	}
-	b := NewBuilder()
+	b := NewDefaultSRVBuilder()
 	target := "foo.ipv4.single.fake"
 	cc := &testClientConn{target: target}
 	r, err := b.Build(resolver.Target{Endpoint: target}, cc, resolver.BuildOptions{})
@@ -586,7 +586,7 @@ func TestCustomAuthority(t *testing.T) {
 			}
 		}
 
-		b := NewBuilder()
+		b := NewDefaultSRVBuilder()
 		cc := &testClientConn{target: "foo.bar.com", errChan: make(chan error, 1)}
 		r, err := b.Build(resolver.Target{Endpoint: "foo.bar.com", Authority: a.authority}, cc, resolver.BuildOptions{})
 
@@ -640,7 +640,7 @@ func TestRateLimitedResolve(t *testing.T) {
 	defer nc()
 
 	target := "foo.ipv4.single.fake"
-	b := NewBuilder()
+	b := NewDefaultSRVBuilder()
 	cc := &testClientConn{target: target}
 
 	r, err := b.Build(resolver.Target{Endpoint: target}, cc, resolver.BuildOptions{})
@@ -751,7 +751,7 @@ func TestReportError(t *testing.T) {
 	}
 	cc := &testClientConn{target: target, errChan: make(chan error)}
 	totalTimesCalledError := 0
-	b := NewBuilder()
+	b := NewDefaultSRVBuilder()
 	r, err := b.Build(resolver.Target{Endpoint: target}, cc, resolver.BuildOptions{})
 	if err != nil {
 		t.Fatalf("Error building resolver for target %v: %v", target, err)
diff --git a/grpc/noncebalancer/noncebalancer_test.go b/grpc/noncebalancer/noncebalancer_test.go
new file mode 100644
index 000000000..63c44b874
--- /dev/null
+++ b/grpc/noncebalancer/noncebalancer_test.go
@@ -0,0 +1,126 @@
+package noncebalancer
+
+import (
+	"context"
+	"testing"
+
+	"github.com/letsencrypt/boulder/nonce"
+	"github.com/letsencrypt/boulder/test"
+	"google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/balancer/base"
+	"google.golang.org/grpc/resolver"
+)
+
+func TestPickerPicksCorrectBackend(t *testing.T) {
+	_, p, subConns := setupTest(false)
+	prefix := nonce.DerivePrefix(subConns[0].addrs[0].Addr, "Kala namak")
+
+	testCtx := context.WithValue(context.Background(), nonce.PrefixCtxKey{}, "HNmOnt8w")
+	testCtx = context.WithValue(testCtx, nonce.HMACKeyCtxKey{}, prefix)
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertNotError(t, err, "Pick failed")
+	test.AssertDeepEquals(t, subConns[0], gotPick.SubConn)
+}
+
+func TestPickerMissingPrefixInCtx(t *testing.T) {
+	_, p, subConns := setupTest(false)
+	prefix := nonce.DerivePrefix(subConns[0].addrs[0].Addr, "Kala namak")
+
+	testCtx := context.WithValue(context.Background(), nonce.HMACKeyCtxKey{}, prefix)
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, errMissingPrefixCtxKey)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func TestPickerInvalidPrefixInCtx(t *testing.T) {
+	_, p, _ := setupTest(false)
+
+	testCtx := context.WithValue(context.Background(), nonce.PrefixCtxKey{}, 9)
+	testCtx = context.WithValue(testCtx, nonce.HMACKeyCtxKey{}, "foobar")
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, errInvalidPrefixCtxKeyType)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func TestPickerMissingHMACKeyInCtx(t *testing.T) {
+	_, p, _ := setupTest(false)
+
+	testCtx := context.WithValue(context.Background(), nonce.PrefixCtxKey{}, "HNmOnt8w")
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, errMissingHMACKeyCtxKey)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func TestPickerInvalidHMACKeyInCtx(t *testing.T) {
+	_, p, _ := setupTest(false)
+
+	testCtx := context.WithValue(context.Background(), nonce.PrefixCtxKey{}, "HNmOnt8w")
+	testCtx = context.WithValue(testCtx, nonce.HMACKeyCtxKey{}, 9)
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, errInvalidHMACKeyCtxKeyType)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func TestPickerNoMatchingSubConnAvailable(t *testing.T) {
+	_, p, subConns := setupTest(false)
+	prefix := nonce.DerivePrefix(subConns[0].addrs[0].Addr, "Kala namak")
+
+	testCtx := context.WithValue(context.Background(), nonce.PrefixCtxKey{}, "rUsTrUin")
+	testCtx = context.WithValue(testCtx, nonce.HMACKeyCtxKey{}, prefix)
+	info := balancer.PickInfo{Ctx: testCtx}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, balancer.ErrNoSubConnAvailable)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func TestPickerNoSubConnsAvailable(t *testing.T) {
+	b, p, _ := setupTest(true)
+	b.Build(base.PickerBuildInfo{})
+	info := balancer.PickInfo{Ctx: context.Background()}
+
+	gotPick, err := p.Pick(info)
+	test.AssertErrorIs(t, err, balancer.ErrNoSubConnAvailable)
+	test.AssertNil(t, gotPick.SubConn, "subConn should be nil")
+}
+
+func setupTest(noSubConns bool) (*Balancer, balancer.Picker, []*subConn) {
+	var subConns []*subConn
+	bi := base.PickerBuildInfo{
+		ReadySCs: make(map[balancer.SubConn]base.SubConnInfo),
+	}
+
+	sc := &subConn{}
+	addr := resolver.Address{Addr: "10.77.77.77:8080"}
+	sc.UpdateAddresses([]resolver.Address{addr})
+
+	if !noSubConns {
+		bi.ReadySCs[sc] = base.SubConnInfo{Address: addr}
+		subConns = append(subConns, sc)
+	}
+
+	b := &Balancer{}
+	p := b.Build(bi)
+	return b, p, subConns
+}
+
+// subConn implements the balancer.SubConn interface.
+type subConn struct {
+	addrs []resolver.Address
+}
+
+func (s *subConn) UpdateAddresses(addrs []resolver.Address) {
+	s.addrs = addrs
+}
+
+func (s *subConn) Connect() {}
diff --git a/nonce/nonce_test.go b/nonce/nonce_test.go
index c54e4a4a9..f77dc4b0c 100644
--- a/nonce/nonce_test.go
+++ b/nonce/nonce_test.go
@@ -162,7 +162,7 @@ func TestRemoteRedeem(t *testing.T) {
 	test.AssertNotError(t, err, "RemoteRedeem failed")
 	test.Assert(t, !valid, "RemoteRedeem accepted an empty nonce")
 
-	prefixMap := map[string]noncepb.NonceServiceClient{
+	prefixMap := map[string]Redeemer{
 		"abcd": &malleableNonceClient{
 			redeem: func(ctx context.Context, in *noncepb.NonceMessage, opts ...grpc.CallOption) (*noncepb.ValidMessage, error) {
 				return nil, errors.New("wrong one!")
@@ -210,3 +210,8 @@ func TestNoncePrefixValidation(t *testing.T) {
 	_, err = NewNonceService(metrics.NoopRegisterer, 0, "heyy")
 	test.AssertNotError(t, err, "NewNonceService failed with valid nonce prefix")
 }
+
+func TestDerivePrefix(t *testing.T) {
+	prefix := DerivePrefix("192.168.1.1:8080", "3b8c758dd85e113ea340ce0b3a99f389d40a308548af94d1730a7692c1874f1f")
+	test.AssertEquals(t, prefix, "P9qQaK4o")
+}
diff --git a/ocsp/responder/redis/checked_redis_source_test.go b/ocsp/responder/redis/checked_redis_source_test.go
index c4b628dfa..999798fc0 100644
--- a/ocsp/responder/redis/checked_redis_source_test.go
+++ b/ocsp/responder/redis/checked_redis_source_test.go
@@ -2,6 +2,7 @@ package redis
 
 import (
 	"context"
+	"database/sql"
 	"errors"
 	"fmt"
 	"math/big"
@@ -9,8 +10,13 @@ import (
 	"time"
 
 	"github.com/go-gorp/gorp/v3"
+	"golang.org/x/crypto/ocsp"
+	"google.golang.org/grpc"
+	"google.golang.org/protobuf/types/known/timestamppb"
+
 	"github.com/letsencrypt/boulder/core"
 	"github.com/letsencrypt/boulder/db"
+	berrors "github.com/letsencrypt/boulder/errors"
 	blog "github.com/letsencrypt/boulder/log"
 	"github.com/letsencrypt/boulder/metrics"
 	"github.com/letsencrypt/boulder/mocks"
@@ -19,9 +25,6 @@ import (
 	"github.com/letsencrypt/boulder/sa"
 	sapb "github.com/letsencrypt/boulder/sa/proto"
 	"github.com/letsencrypt/boulder/test"
-	"golang.org/x/crypto/ocsp"
-	"google.golang.org/grpc"
-	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
 // echoSource implements rocspSourceInterface, returning the provided response
@@ -94,6 +97,20 @@ func (s errorSelector) WithContext(context.Context) gorp.SqlExecutor {
 	return s
 }
 
+// notFoundSelector always returns an NoRows error.
+type notFoundSelector struct {
+	db.MockSqlExecutor
+}
+
+func (s notFoundSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error {
+	return db.ErrDatabaseOp{Err: sql.ErrNoRows}
+}
+
+func (s notFoundSelector) WithContext(context.Context) gorp.SqlExecutor {
+	return s
+}
+
+// echoSA always returns the given revocation status.
 type echoSA struct {
 	mocks.StorageAuthorityReadOnly
 	status *sapb.RevocationStatus
@@ -103,6 +120,7 @@ func (s *echoSA) GetRevocationStatus(_ context.Context, req *sapb.Serial, _ ...g
 	return s.status, nil
 }
 
+// errorSA always returns an error.
 type errorSA struct {
 	mocks.StorageAuthorityReadOnly
 }
@@ -111,6 +129,15 @@ func (s *errorSA) GetRevocationStatus(_ context.Context, req *sapb.Serial, _ ...
 	return nil, errors.New("oops")
 }
 
+// notFoundSA always returns a NotFound error.
+type notFoundSA struct {
+	mocks.StorageAuthorityReadOnly
+}
+
+func (s *notFoundSA) GetRevocationStatus(_ context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*sapb.RevocationStatus, error) {
+	return nil, berrors.NotFoundError("purged")
+}
+
 func TestCheckedRedisSourceSuccess(t *testing.T) {
 	serial := big.NewInt(17777)
 	thisUpdate := time.Now().Truncate(time.Second).UTC()
@@ -149,6 +176,14 @@ func TestCheckedRedisSourceDBError(t *testing.T) {
 		SerialNumber: serial,
 	})
 	test.AssertError(t, err, "getting response")
+	test.AssertContains(t, err.Error(), "oops")
+
+	src = newCheckedRedisSource(echoSource{resp: resp}, notFoundSelector{}, nil, metrics.NoopRegisterer, blog.NewMock())
+	_, err = src.Response(context.Background(), &ocsp.Request{
+		SerialNumber: serial,
+	})
+	test.AssertError(t, err, "getting response")
+	test.AssertErrorIs(t, err, responder.ErrNotFound)
 }
 
 func TestCheckedRedisSourceSAError(t *testing.T) {
@@ -167,6 +202,14 @@ func TestCheckedRedisSourceSAError(t *testing.T) {
 		SerialNumber: serial,
 	})
 	test.AssertError(t, err, "getting response")
+	test.AssertContains(t, err.Error(), "oops")
+
+	src = newCheckedRedisSource(echoSource{resp: resp}, nil, &notFoundSA{}, metrics.NoopRegisterer, blog.NewMock())
+	_, err = src.Response(context.Background(), &ocsp.Request{
+		SerialNumber: serial,
+	})
+	test.AssertError(t, err, "getting response")
+	test.AssertErrorIs(t, err, responder.ErrNotFound)
 }
 
 func TestCheckedRedisSourceRedisError(t *testing.T) {
diff --git a/ra/ra_test.go b/ra/ra_test.go
index d6bc3bcb0..7db679450 100644
--- a/ra/ra_test.go
+++ b/ra/ra_test.go
@@ -370,11 +370,15 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho
 	ra.CA = ca
 	ra.OCSP = ca
 	ra.PA = pa
-	ra.reuseValidAuthz = true
-
 	return va, sa, ra, fc, cleanUp
 }
 
+func TestReuseValidAuthzDefaultIsTrue(t *testing.T) {
+	_, _, ra, _, cleanUp := initAuthorities(t)
+	defer cleanUp()
+	test.Assert(t, ra.reuseValidAuthz, "ReuseValidAuthz should be true")
+}
+
 func TestValidateContacts(t *testing.T) {
 	_, _, ra, _, cleanUp := initAuthorities(t)
 	defer cleanUp()
@@ -1751,22 +1755,22 @@ func TestRecheckCAADates(t *testing.T) {
 	// NOTE: The names provided here correspond to authorizations in the
 	// `mockSAWithRecentAndOlder`
 	names := []string{"recent.com", "older.com", "older2.com", "wildcard.com", "*.wildcard.com"}
-	err := ra.checkAuthorizationsCAA(context.Background(), names, authzs, fc.Now())
+	err := ra.checkAuthorizationsCAA(context.Background(), Registration.Id, names, authzs, fc.Now())
 	// We expect that there is no error rechecking authorizations for these names
 	if err != nil {
 		t.Errorf("expected nil err, got %s", err)
 	}
 
 	// Should error if a authorization has `!= 1` challenge
-	err = ra.checkAuthorizationsCAA(context.Background(), []string{"twochallenges.com"}, authzs, fc.Now())
+	err = ra.checkAuthorizationsCAA(context.Background(), Registration.Id, []string{"twochallenges.com"}, authzs, fc.Now())
 	test.AssertEquals(t, err.Error(), "authorization has incorrect number of challenges. 1 expected, 2 found for: id twochal")
 
 	// Should error if a authorization has `!= 1` challenge
-	err = ra.checkAuthorizationsCAA(context.Background(), []string{"nochallenges.com"}, authzs, fc.Now())
+	err = ra.checkAuthorizationsCAA(context.Background(), Registration.Id, []string{"nochallenges.com"}, authzs, fc.Now())
 	test.AssertEquals(t, err.Error(), "authorization has incorrect number of challenges. 1 expected, 0 found for: id nochal")
 
 	// Should error if authorization's challenge has no validated timestamp
-	err = ra.checkAuthorizationsCAA(context.Background(), []string{"novalidationtime.com"}, authzs, fc.Now())
+	err = ra.checkAuthorizationsCAA(context.Background(), Registration.Id, []string{"novalidationtime.com"}, authzs, fc.Now())
 	test.AssertEquals(t, err.Error(), "authorization's challenge has no validated timestamp for: id noval")
 
 	// Test to make sure the authorization lifetime codepath was not used
@@ -2127,6 +2131,38 @@ func TestNewOrderReuseInvalidAuthz(t *testing.T) {
 	test.AssertNotEquals(t, secondOrder.V2Authorizations[0], order.V2Authorizations[0])
 }
 
+// mockSACountPendingFails has a CountPendingAuthorizations2 implementation
+// that always returns error
+type mockSACountPendingFails struct {
+	mocks.StorageAuthority
+}
+
+func (mock *mockSACountPendingFails) CountPendingAuthorizations2(ctx context.Context, req *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Count, error) {
+	return nil, errors.New("counting is slow and boring")
+}
+
+// Ensure that we don't bother to call the SA to count pending authorizations
+// when an "unlimited" limit is set.
+func TestPendingAuthorizationsUnlimited(t *testing.T) {
+	_, _, ra, _, cleanUp := initAuthorities(t)
+	defer cleanUp()
+
+	ra.rlPolicies = &dummyRateLimitConfig{
+		PendingAuthorizationsPerAccountPolicy: ratelimit.RateLimitPolicy{
+			Threshold: 1,
+			Window:    cmd.ConfigDuration{Duration: 24 * time.Hour},
+			RegistrationOverrides: map[int64]int64{
+				13: -1,
+			},
+		},
+	}
+
+	ra.SA = &mockSACountPendingFails{}
+
+	err := ra.checkPendingAuthorizationLimit(context.Background(), 13)
+	test.AssertNotError(t, err, "checking pending authorization limit")
+}
+
 // Test that the failed authorizations limit is checked before authz reuse.
 func TestNewOrderCheckFailedAuthorizationsFirst(t *testing.T) {
 	_, _, ra, _, cleanUp := initAuthorities(t)
@@ -3116,6 +3152,109 @@ func TestIssueCertificateAuditLog(t *testing.T) {
 	}
 }
 
+func TestIssueCertificateCAACheckLog(t *testing.T) {
+	_, sa, ra, fc, cleanUp := initAuthorities(t)
+	defer cleanUp()
+
+	// Set up order and authz expiries.
+	ra.orderLifetime = 24 * time.Hour
+	ra.authorizationLifetime = 15 * time.Hour
+
+	exp := fc.Now().Add(24 * time.Hour)
+	recent := fc.Now().Add(-1 * time.Hour)
+	older := fc.Now().Add(-8 * time.Hour)
+
+	// Make some valid authzs for four names. Half of them were validated
+	// recently and half were validated in excess of our CAA recheck time.
+	names := []string{"not-example.com", "www.not-example.com", "still.not-example.com", "definitely.not-example.com"}
+	var authzIDs []int64
+	for i, name := range names {
+		attemptedAt := older
+		if i%2 == 0 {
+			attemptedAt = recent
+		}
+		authzIDs = append(authzIDs, createFinalizedAuthorization(t, sa, name, exp, core.ChallengeTypeHTTP01, attemptedAt))
+	}
+
+	// Create a pending order for all of the names.
+	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
+		NewOrder: &sapb.NewOrderRequest{
+			RegistrationID:   Registration.Id,
+			Expires:          exp.UnixNano(),
+			Names:            names,
+			V2Authorizations: authzIDs,
+		},
+	})
+	test.AssertNotError(t, err, "Could not add test order with finalized authz IDs")
+
+	// Generate a CSR covering the order names with a random RSA key.
+	testKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	test.AssertNotError(t, err, "error generating test key")
+	csr, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
+		PublicKey:          testKey.PublicKey,
+		SignatureAlgorithm: x509.SHA256WithRSA,
+		Subject:            pkix.Name{CommonName: "not-example.com"},
+		DNSNames:           names,
+	}, testKey)
+	test.AssertNotError(t, err, "Could not create test order CSR")
+
+	// Create a mock certificate for the fake CA to return.
+	template := &x509.Certificate{
+		SerialNumber: big.NewInt(12),
+		Subject: pkix.Name{
+			CommonName: "not-example.com",
+		},
+		DNSNames:              names,
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().AddDate(0, 0, 1),
+		BasicConstraintsValid: true,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
+	}
+	cert, err := x509.CreateCertificate(rand.Reader, template, template, testKey.Public(), testKey)
+	test.AssertNotError(t, err, "Failed to create mock cert for test CA")
+
+	// Set up the RA's CA with a mock that returns the cert from above.
+	ra.CA = &mocks.MockCA{
+		PEM: pem.EncodeToMemory(&pem.Block{
+			Bytes: cert,
+		}),
+	}
+
+	// Cast the RA's mock log so we can ensure its cleared and can access the
+	// matched log lines.
+	mockLog := ra.log.(*blog.Mock)
+	mockLog.Clear()
+
+	// Finalize the order with the CSR.
+	order.Status = string(core.StatusReady)
+	_, err = ra.FinalizeOrder(context.Background(), &rapb.FinalizeOrderRequest{
+		Order: order,
+		Csr:   csr,
+	})
+	test.AssertNotError(t, err, "Error finalizing test order")
+
+	// Get the logged lines from the mock logger.
+	loglines := mockLog.GetAllMatching("FinalizationCaaCheck JSON=")
+	// There should be exactly 1 matching log line.
+	test.AssertEquals(t, len(loglines), 1)
+
+	// Strip away the stuff before 'JSON='.
+	jsonContent := strings.TrimPrefix(loglines[0], "INFO: FinalizationCaaCheck JSON=")
+
+	// Unmarshal the JSON into an event object.
+	var event finalizationCAACheckEvent
+	err = json.Unmarshal([]byte(jsonContent), &event)
+	// The JSON should unmarshal without error.
+	test.AssertNotError(t, err, "Error unmarshalling logged JSON issuance event.")
+	// The event requester should be the expected registration ID.
+	test.AssertEquals(t, event.Requester, Registration.Id)
+	// The event should have the expected number of Authzs where CAA was reused.
+	test.AssertEquals(t, event.Reused, 2)
+	// The event should have the expected number of Authzs where CAA was
+	// rechecked.
+	test.AssertEquals(t, event.Rechecked, 2)
+}
+
 // TestUpdateMissingAuthorization tests the race condition where a challenge is
 // updated to valid concurrently with another attempt to have the challenge
 // updated. Previously this would return a `berrors.InternalServer` error when
diff --git a/rocsp/rocsp_test.go b/rocsp/rocsp_test.go
index dd539958c..c5b3f2f11 100644
--- a/rocsp/rocsp_test.go
+++ b/rocsp/rocsp_test.go
@@ -31,8 +31,8 @@ func makeClient() (*RWClient, clock.Clock) {
 
 	rdb := redis.NewRing(&redis.RingOptions{
 		Addrs: map[string]string{
-			"shard1": "10.33.33.8:4218",
-			"shard2": "10.33.33.9:4218",
+			"shard1": "10.33.33.2:4218",
+			"shard2": "10.33.33.3:4218",
 		},
 		Username:  "unittest-rw",
 		Password:  "824968fa490f4ecec1e52d5e34916bdb60d45f8d",
@@ -43,10 +43,6 @@ func makeClient() (*RWClient, clock.Clock) {
 }
 
 func TestSetAndGet(t *testing.T) {
-	// TODO(#6517) remove this block.
-	if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" {
-		t.Skip("Skipping test in config mode")
-	}
 	client, _ := makeClient()
 	fmt.Println(client.Ping(context.Background()))
 
diff --git a/rocsp/rocspcluster_test.go b/rocsp/rocspcluster_test.go
deleted file mode 100644
index e4f41dc87..000000000
--- a/rocsp/rocspcluster_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package rocsp
-
-import (
-	"bytes"
-	"context"
-	"os"
-	"testing"
-	"time"
-
-	"github.com/go-redis/redis/v8"
-	"github.com/jmhodges/clock"
-	"github.com/letsencrypt/boulder/cmd"
-	"github.com/letsencrypt/boulder/metrics"
-	"golang.org/x/crypto/ocsp"
-)
-
-func makeClusterClient() (*CRWClient, clock.Clock) {
-	CACertFile := "../test/redis-tls/minica.pem"
-	CertFile := "../test/redis-tls/boulder/cert.pem"
-	KeyFile := "../test/redis-tls/boulder/key.pem"
-	tlsConfig := cmd.TLSConfig{
-		CACertFile: &CACertFile,
-		CertFile:   &CertFile,
-		KeyFile:    &KeyFile,
-	}
-	tlsConfig2, err := tlsConfig.Load()
-	if err != nil {
-		panic(err)
-	}
-
-	rdb := redis.NewClusterClient(&redis.ClusterOptions{
-		Addrs:     []string{"10.33.33.2:4218"},
-		Username:  "unittest-rw",
-		Password:  "824968fa490f4ecec1e52d5e34916bdb60d45f8d",
-		TLSConfig: tlsConfig2,
-	})
-	clk := clock.NewFake()
-
-	return NewClusterWritingClient(rdb, 5*time.Second, clk, metrics.NoopRegisterer), clk
-}
-
-func TestClusterSetAndGet(t *testing.T) {
-	// TODO(#6517) remove this block.
-	if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" {
-		t.Skip("Skipping test in config-next mode")
-	}
-	client, _ := makeClusterClient()
-
-	respBytes, err := os.ReadFile("testdata/ocsp.response")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	response, err := ocsp.ParseResponse(respBytes, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = client.StoreResponse(context.Background(), response)
-	if err != nil {
-		t.Fatalf("storing response: %s", err)
-	}
-
-	serial := "ffaa13f9c34be80b8e2532b83afe063b59a6"
-	resp2, err := client.GetResponse(context.Background(), serial)
-	if err != nil {
-		t.Fatalf("getting response: %s", err)
-	}
-	if !bytes.Equal(resp2, respBytes) {
-		t.Errorf("response written and response retrieved were not equal")
-	}
-}
diff --git a/sa/sa_test.go b/sa/sa_test.go
index b92b151e8..403af0da6 100644
--- a/sa/sa_test.go
+++ b/sa/sa_test.go
@@ -991,6 +991,99 @@ func TestNewOrderAndAuthzs(t *testing.T) {
 	test.AssertDeepEquals(t, names, []string{"com.a", "com.b", "com.c", "com.d"})
 }
 
+// TestNewOrderAndAuthzs_NonNilInnerOrder verifies that a nil
+// sapb.NewOrderAndAuthzsRequest NewOrder object returns an error.
+func TestNewOrderAndAuthzs_NonNilInnerOrder(t *testing.T) {
+	sa, fc, cleanup := initSA(t)
+	defer cleanup()
+
+	key, _ := jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}}.MarshalJSON()
+	initialIP, _ := net.ParseIP("17.17.17.17").MarshalText()
+	reg, err := sa.NewRegistration(ctx, &corepb.Registration{
+		Key:       key,
+		InitialIP: initialIP,
+	})
+	test.AssertNotError(t, err, "Couldn't create test registration")
+
+	_, err = sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
+		NewAuthzs: []*corepb.Authorization{
+			{
+				Identifier:     "a.com",
+				RegistrationID: reg.Id,
+				Expires:        fc.Now().Add(2 * time.Hour).UnixNano(),
+				Status:         "pending",
+				Challenges:     []*corepb.Challenge{{Token: core.NewToken()}},
+			},
+		},
+	})
+	test.AssertErrorIs(t, err, errIncompleteRequest)
+}
+
+func TestNewOrderAndAuthzs_NewAuthzExpectedFields(t *testing.T) {
+	sa, fc, cleanup := initSA(t)
+	defer cleanup()
+
+	// Create a test registration to reference.
+	key, _ := jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}}.MarshalJSON()
+	initialIP, _ := net.ParseIP("17.17.17.17").MarshalText()
+	reg, err := sa.NewRegistration(ctx, &corepb.Registration{
+		Key:       key,
+		InitialIP: initialIP,
+	})
+	test.AssertNotError(t, err, "Couldn't create test registration")
+
+	expires := fc.Now().Add(time.Hour).UnixNano()
+	domain := "a.com"
+
+	// Create an authz that does not yet exist in the database with some invalid
+	// data smuggled in.
+	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
+		NewAuthzs: []*corepb.Authorization{
+			{
+				Identifier:     domain,
+				RegistrationID: reg.Id,
+				Expires:        expires,
+				Status:         string(core.StatusPending),
+				Challenges: []*corepb.Challenge{
+					{
+						Status: "real fake garbage data",
+						Token:  core.NewToken(),
+					},
+				},
+			},
+		},
+		NewOrder: &sapb.NewOrderRequest{
+			RegistrationID: reg.Id,
+			Expires:        expires,
+			Names:          []string{domain},
+		},
+	})
+	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
+
+	// Safely get the authz for the order we created above.
+	obj, err := sa.dbReadOnlyMap.Get(authzModel{}, order.V2Authorizations[0])
+	test.AssertNotError(t, err, fmt.Sprintf("authorization %d not found", order.V2Authorizations[0]))
+
+	// To access the data stored in obj at compile time, we type assert obj
+	// into a pointer to an authzModel.
+	am, ok := obj.(*authzModel)
+	test.Assert(t, ok, "Could not type assert obj into authzModel")
+
+	// If we're making a brand new authz, it should have the pending status
+	// regardless of what incorrect status value was passed in during construction.
+	test.AssertEquals(t, am.Status, statusUint(core.StatusPending))
+
+	// Testing for the existence of these boxed nils is a definite break from
+	// our paradigm of avoiding passing around boxed nils whenever possible.
+	// However, the existence of these boxed nils in relation to this test is
+	// actually expected. If these tests fail, then a possible SA refactor or RA
+	// bug placed incorrect data into brand new authz input fields.
+	test.AssertBoxedNil(t, am.Attempted, "am.Attempted should be nil")
+	test.AssertBoxedNil(t, am.AttemptedAt, "am.AttemptedAt should be nil")
+	test.AssertBoxedNil(t, am.ValidationError, "am.ValidationError should be nil")
+	test.AssertBoxedNil(t, am.ValidationRecord, "am.ValidationRecord should be nil")
+}
+
 func TestSetOrderProcessing(t *testing.T) {
 	sa, fc, cleanup := initSA(t)
 	defer cleanup()
diff --git a/test/akamai-test-srv/main.go b/test/akamai-test-srv/main.go
index 8fd7049ef..b9fd85c0f 100644
--- a/test/akamai-test-srv/main.go
+++ b/test/akamai-test-srv/main.go
@@ -92,6 +92,9 @@ func main() {
 		w.Write(resp)
 	})
 
+	// The gosec linter complains that timeouts cannot be set here. That's fine,
+	// because this is test-only code.
+	////nolint:gosec
 	go log.Fatal(http.ListenAndServe(*listenAddr, nil))
 	cmd.CatchSignals(nil, nil)
 }
diff --git a/test/asserts.go b/test/asserts.go
index 1b9514f27..955e487a7 100644
--- a/test/asserts.go
+++ b/test/asserts.go
@@ -49,6 +49,22 @@ func AssertNotNil(t *testing.T, obj interface{}, message string) {
 	}
 }
 
+// AssertBoxedNil checks that an inner object is nil. This is intentional for
+// testing purposes only.
+func AssertBoxedNil(t *testing.T, obj interface{}, message string) {
+	t.Helper()
+	typ := reflect.TypeOf(obj).Kind()
+	switch typ {
+	// .IsNil() only works on chan, func, interface, map, pointer, and slice.
+	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
+		if !reflect.ValueOf(obj).IsNil() {
+			t.Fatal(message)
+		}
+	default:
+		t.Fatalf("Cannot check type \"%s\". Needs to be of type chan, func, interface, map, pointer, or slice.", typ)
+	}
+}
+
 // AssertNotError checks that err is nil
 func AssertNotError(t *testing.T, err error, message string) {
 	t.Helper()
diff --git a/test/boulder-tools/build.sh b/test/boulder-tools/build.sh
index e4f041234..852e1ac82 100755
--- a/test/boulder-tools/build.sh
+++ b/test/boulder-tools/build.sh
@@ -45,7 +45,7 @@ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0
 go install github.com/rubenv/sql-migrate/...@v1.1.2
 go install golang.org/x/tools/cmd/stringer@latest
 go install github.com/letsencrypt/pebble/cmd/pebble-challtestsrv@master
-go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.1
+go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.0
 
 go clean -cache
 go clean -modcache
diff --git a/test/boulder-tools/tag_and_upload.sh b/test/boulder-tools/tag_and_upload.sh
index 6aed32fa2..59a4e7aa0 100755
--- a/test/boulder-tools/tag_and_upload.sh
+++ b/test/boulder-tools/tag_and_upload.sh
@@ -10,7 +10,7 @@ DOCKER_REPO="letsencrypt/boulder-tools"
 # When updating these GO_VERSIONS, please also update
 # .github/workflows/release.yml and
 # .github/workflows/try-release.yml if appropriate.
-GO_VERSIONS=( "1.19.2" "1.19.5" )
+GO_VERSIONS=( "1.19.5" "1.20" )
 
 echo "Please login to allow push to DockerHub"
 docker login
diff --git a/test/config-next/cert-checker.json b/test/config-next/cert-checker.json
index b0587b9d0..4b9e5ed38 100644
--- a/test/config-next/cert-checker.json
+++ b/test/config-next/cert-checker.json
@@ -16,7 +16,11 @@
     "ignoredLints": [
       "n_subject_common_name_included"
     ],
-    "ctLogListFile": "test/ct-test-srv/log_list.json"
+    "ctLogListFile": "test/ct-test-srv/log_list.json",
+    "features": {
+      "CertCheckerChecksValidations": true,
+      "CertCheckerRequiresValidations": true
+    }
   },
 
   "pa": {
diff --git a/test/config-next/nonce.json b/test/config-next/nonce-a.json
similarity index 88%
rename from test/config-next/nonce.json
rename to test/config-next/nonce-a.json
index 37d1813e7..cbcc61163 100644
--- a/test/config-next/nonce.json
+++ b/test/config-next/nonce-a.json
@@ -1,7 +1,8 @@
 {
   "NonceService": {
     "maxUsed": 131072,
-    "noncePrefix": "taro",
+    "useDerivablePrefix": true,
+    "noncePrefixKey": {"passwordFile": "test/secrets/nonce_prefix_key"},
     "syslog": {
       "stdoutLevel": 6,
       "syslogLevel": -1
diff --git a/test/config-next/nonce-b.json b/test/config-next/nonce-b.json
new file mode 100644
index 000000000..cbcc61163
--- /dev/null
+++ b/test/config-next/nonce-b.json
@@ -0,0 +1,38 @@
+{
+  "NonceService": {
+    "maxUsed": 131072,
+    "useDerivablePrefix": true,
+    "noncePrefixKey": {"passwordFile": "test/secrets/nonce_prefix_key"},
+    "syslog": {
+      "stdoutLevel": 6,
+      "syslogLevel": -1
+    },
+    "beeline": {
+      "mute": true,
+      "serviceName": "Test",
+      "writeKey": {"passwordFile": "test/secrets/honeycomb_fake_password"}
+    },
+    "debugAddr": ":8111",
+    "grpc": {
+      "maxConnectionAge": "30s",
+      "address": ":9101",
+      "services": {
+        "nonce.NonceService": {
+          "clientNames": [
+            "wfe.boulder"
+          ]
+        },
+        "grpc.health.v1.Health": {
+          "clientNames": [
+            "health-checker.boulder"
+          ]
+        }
+      }
+    },
+    "tls": {
+      "caCertFile": "test/grpc-creds/minica.pem",
+      "certFile": "test/grpc-creds/nonce.boulder/cert.pem",
+      "keyFile": "test/grpc-creds/nonce.boulder/key.pem"
+    }
+  }
+}
diff --git a/test/config-next/ocsp-responder.json b/test/config-next/ocsp-responder.json
index 4dfe02b93..64fc951ee 100644
--- a/test/config-next/ocsp-responder.json
+++ b/test/config-next/ocsp-responder.json
@@ -4,8 +4,8 @@
       "username": "ocsp-responder",
       "passwordFile": "test/secrets/ocsp_responder_redis_password",
       "shardAddrs": {
-        "shard1": "10.33.33.8:4218",
-        "shard2": "10.33.33.9:4218"
+        "shard1": "10.33.33.2:4218",
+        "shard2": "10.33.33.3:4218"
       },
       "timeout": "5s",
       "poolSize": 100,
diff --git a/test/config-next/ra.json b/test/config-next/ra.json
index 74cd02766..2dbf4ece7 100644
--- a/test/config-next/ra.json
+++ b/test/config-next/ra.json
@@ -5,7 +5,6 @@
     "debugAddr": ":8002",
     "hostnamePolicyFile": "test/hostname-policy.yaml",
     "maxNames": 100,
-    "reuseValidAuthz": true,
     "authorizationLifetimeDays": 30,
     "pendingAuthorizationLifetimeDays": 7,
     "goodkey": {
diff --git a/test/config-next/sa.json b/test/config-next/sa.json
index 8b4e9e668..54b0cbdec 100644
--- a/test/config-next/sa.json
+++ b/test/config-next/sa.json
@@ -50,8 +50,8 @@
       "username": "boulder-sa",
       "passwordFile": "test/secrets/sa_redis_password",
       "shardAddrs": {
-        "shard1": "10.33.33.8:4218",
-        "shard2": "10.33.33.9:4218"
+        "shard1": "10.33.33.2:4218",
+        "shard2": "10.33.33.3:4218"
       },
       "timeout": "5s",
       "tls": {
diff --git a/test/config-next/wfe2.json b/test/config-next/wfe2.json
index 794c48147..7beebd840 100644
--- a/test/config-next/wfe2.json
+++ b/test/config-next/wfe2.json
@@ -48,24 +48,22 @@
       "timeout": "15s",
       "hostOverride": "nonce.boulder"
     },
-    "redeemNonceServices": {
-      "taro": {
-        "srvLookup": {
+    "redeemNonceService": {
+      "srvLookups": [
+        {
           "service": "nonce1",
           "domain": "service.consul"
         },
-        "timeout": "15s",
-        "hostOverride": "nonce1.boulder"
-      },
-      "zinc": {
-        "srvLookup": {
+        {
           "service": "nonce2",
           "domain": "service.consul"
-        },
-        "timeout": "15s",
-        "hostOverride": "nonce2.boulder"
-      }
+        }
+      ],
+      "srvResolver": "nonce-srv",
+      "timeout": "15s",
+      "hostOverride": "nonce.boulder"
     },
+    "noncePrefixKey": {"passwordFile": "test/secrets/nonce_prefix_key"},
     "chains": [
       [
         "/hierarchy/intermediate-cert-rsa-a.pem",
diff --git a/test/config/nonce.json b/test/config/nonce-a.json
similarity index 100%
rename from test/config/nonce.json
rename to test/config/nonce-a.json
diff --git a/test/config/nonce-b.json b/test/config/nonce-b.json
new file mode 100644
index 000000000..f4184b7ab
--- /dev/null
+++ b/test/config/nonce-b.json
@@ -0,0 +1,29 @@
+{
+    "NonceService": {
+        "maxUsed": 131072,
+        "noncePrefix": "zinc",
+        "syslog": {
+            "stdoutLevel": 6,
+            "syslogLevel": 6
+        },
+        "beeline": {
+            "mute": true,
+            "serviceName": "Test",
+            "writeKey": {"passwordFile": "test/secrets/honeycomb_fake_password"}
+        },
+        "debugAddr": ":8111",
+        "grpc": {
+            "maxConnectionAge": "30s",
+            "address": ":9101",
+            "clientNames": [
+                "health-checker.boulder",
+                "wfe.boulder"
+            ]
+        },
+        "tls": {
+            "caCertFile": "test/grpc-creds/minica.pem",
+            "certFile": "test/grpc-creds/nonce.boulder/cert.pem",
+            "keyFile": "test/grpc-creds/nonce.boulder/key.pem"
+        }
+    }
+}
diff --git a/test/config/ocsp-responder.json b/test/config/ocsp-responder.json
index 4d28a28ca..f70c66e43 100644
--- a/test/config/ocsp-responder.json
+++ b/test/config/ocsp-responder.json
@@ -7,9 +7,10 @@
     "redis": {
       "username": "ocsp-responder",
       "passwordFile": "test/secrets/ocsp_responder_redis_password",
-      "addrs": [
-        "10.33.33.7:4218"
-      ],
+      "shardAddrs": {
+        "shard1": "10.33.33.2:4218",
+        "shard2": "10.33.33.3:4218"
+      },
       "timeout": "5s",
       "poolSize": 100,
       "routeRandomly": true,
diff --git a/test/config/rocsp-tool.json b/test/config/rocsp-tool.json
index e3db747d4..7f3daa95a 100644
--- a/test/config/rocsp-tool.json
+++ b/test/config/rocsp-tool.json
@@ -4,9 +4,10 @@
     "redis": {
       "username": "ocsp-updater",
       "passwordFile": "test/secrets/rocsp_tool_password",
-      "addrs": [
-        "10.33.33.7:4218"
-      ],
+      "shardAddrs": {
+        "shard1": "10.33.33.2:4218",
+        "shard2": "10.33.33.3:4218"
+      },
       "timeout": "5s",
       "tls": {
         "caCertFile": "test/redis-tls/minica.pem",
diff --git a/test/entrypoint.sh b/test/entrypoint.sh
index 34cb721bb..8f9664e06 100755
--- a/test/entrypoint.sh
+++ b/test/entrypoint.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -e -u
 
@@ -10,9 +10,8 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 rm -f /var/run/rsyslogd.pid
 service rsyslog start
 
-# make sure we can reach the mysqldb and Redis cluster is done being created.
+# make sure we can reach the mysqldb.
 ./test/wait-for-it.sh boulder-mysql 3306
-./test/wait-for-it.sh 10.33.33.10 4218
 
 # create the database
 MYSQL_CONTAINER=1 $DIR/create_db.sh
diff --git a/test/integration/orphan_finder_test.go b/test/integration/orphan_finder_test.go
index 9d33323be..acabbf563 100644
--- a/test/integration/orphan_finder_test.go
+++ b/test/integration/orphan_finder_test.go
@@ -23,8 +23,8 @@ import (
 	"github.com/letsencrypt/pkcs11key/v4"
 )
 
-var template = `[AUDIT] Failed RPC to store at SA, orphaning precertificate: serial=[%x] cert=[%x] err=[sa.StorageAuthority.AddCertificate timed out after 5000 ms], issuerID=[1], regID=[1], orderID=[1]
-[AUDIT] Failed RPC to store at SA, orphaning certificate: serial=[%x] cert=[%x] err=[sa.StorageAuthority.AddCertificate timed out after 5000 ms], issuerID=[1], regID=[1], orderID=[1]`
+var template = `[AUDIT] Failed RPC to store at SA, orphaning precertificate: serial=[%x], cert=[%x], issuerID=[1], regID=[1], orderID=[1], err=[sa.StorageAuthority.AddPrecertificate timed out after 5000 ms]
+[AUDIT] Failed RPC to store at SA, orphaning certificate: serial=[%x], cert=[%x], issuerID=[1], regID=[1], orderID=[1], err=[sa.StorageAuthority.AddCertificate timed out after 5000 ms]`
 
 // TestOrphanFinder runs the orphan-finder with an example input file. This must
 // be run after other tests so the account ID 1 exists (since the inserted
diff --git a/test/mail-test-srv/main.go b/test/mail-test-srv/main.go
index b56ac31dc..d3ee84e42 100644
--- a/test/mail-test-srv/main.go
+++ b/test/mail-test-srv/main.go
@@ -224,6 +224,9 @@ func main() {
 
 	srv.setupHTTP(http.DefaultServeMux)
 	go func() {
+		// The gosec linter complains that timeouts cannot be set here. That's fine,
+		// because this is test-only code.
+		////nolint:gosec
 		err := http.ListenAndServe(*listenAPI, http.DefaultServeMux)
 		if err != nil {
 			log.Fatalln("Couldn't start HTTP server", err)
diff --git a/test/ocsp/checkari/main.go b/test/ocsp/checkari/main.go
new file mode 100644
index 000000000..d859fd3d5
--- /dev/null
+++ b/test/ocsp/checkari/main.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+	"crypto"
+	_ "crypto/sha256"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/base64"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io"
+	"math/big"
+	"net/http"
+	"os"
+
+	"github.com/letsencrypt/boulder/core"
+)
+
+// certID matches the ASN.1 structure of the CertID sequence defined by RFC6960.
+type certID struct {
+	HashAlgorithm  pkix.AlgorithmIdentifier
+	IssuerNameHash []byte
+	IssuerKeyHash  []byte
+	SerialNumber   *big.Int
+}
+
+func createRequest(cert *x509.Certificate) ([]byte, error) {
+	if !crypto.SHA256.Available() {
+		return nil, x509.ErrUnsupportedAlgorithm
+	}
+	h := crypto.SHA256.New()
+
+	h.Write(cert.RawIssuer)
+	issuerNameHash := h.Sum(nil)
+
+	req := certID{
+		pkix.AlgorithmIdentifier{ // SHA256
+			Algorithm:  asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1},
+			Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
+		},
+		issuerNameHash,
+		cert.AuthorityKeyId,
+		cert.SerialNumber,
+	}
+
+	return asn1.Marshal(req)
+}
+
+func parseResponse(resp *http.Response) (*core.RenewalInfo, error) {
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var res core.RenewalInfo
+	err = json.Unmarshal(body, &res)
+	if err != nil {
+		return nil, err
+	}
+
+	return &res, nil
+}
+
+func checkARI(baseURL string, certPath string) (*core.RenewalInfo, error) {
+	cert, err := core.LoadCert(certPath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := createRequest(cert)
+	if err != nil {
+		return nil, err
+	}
+
+	url := fmt.Sprintf("%s/%s", baseURL, base64.RawURLEncoding.EncodeToString(req))
+	resp, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+
+	ri, err := parseResponse(resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return ri, nil
+}
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `
+checkari [-url https://acme.api/ari/endpoint] FILE [FILE]...
+
+Tool for querying ARI. Provide a list of filenames for certificates in PEM
+format, and this tool will query for and output the suggested renewal window
+for each certificate.
+
+`)
+		flag.PrintDefaults()
+	}
+	url := flag.String("url", "https://acme-v02.api.letsencrypt.org/get/draft-ietf-acme-ari-00/renewalInfo/", "ACME server's RenewalInfo URL")
+	flag.Parse()
+	if len(flag.Args()) == 0 {
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	for _, cert := range flag.Args() {
+		fmt.Printf("%s:\n", cert)
+		window, err := checkARI(*url, cert)
+		if err != nil {
+			fmt.Printf("\t%s\n", err)
+		} else {
+			fmt.Printf("\tRenew after : %s\n", window.SuggestedWindow.Start)
+			fmt.Printf("\tRenew before: %s\n", window.SuggestedWindow.End)
+		}
+	}
+}
diff --git a/test/ocsp/ocsp_forever/main.go b/test/ocsp/ocsp_forever/main.go
index 54fdb94c0..25d3a5873 100644
--- a/test/ocsp/ocsp_forever/main.go
+++ b/test/ocsp/ocsp_forever/main.go
@@ -86,6 +86,9 @@ func main() {
 	}
 	http.Handle("/metrics", promhttp.Handler())
 	go func() {
+		// The gosec linter complains that timeouts cannot be set here. That's fine,
+		// because this is test-only code.
+		////nolint:gosec
 		err := http.ListenAndServe(*listenAddress, nil)
 		if err != nil && err != http.ErrServerClosed {
 			log.Fatal(err)
diff --git a/test/redis-cli.sh b/test/redis-cli.sh
old mode 100644
new mode 100755
index d9872a5ce..b54f9c4c5
--- a/test/redis-cli.sh
+++ b/test/redis-cli.sh
@@ -2,12 +2,12 @@
 
 set -feuo pipefail
 
-ARGS="--tls \
-    -p 4218 \
+ARGS="-p 4218 \
+    --tls \
     --cert /test/redis-tls/redis/cert.pem \
     --key /test/redis-tls/redis/key.pem \
     --cacert /test/redis-tls/minica.pem \
     --user replication-user \
     --pass 435e9c4225f08813ef3af7c725f0d30d263b9cd3"
 
-exec docker compose exec bredis_clusterer redis-cli "${ARGS}" "${@}"
+exec docker compose exec bredis_1 redis-cli $ARGS "${@}"
diff --git a/test/redis-cluster.config b/test/redis-cluster.config
deleted file mode 100644
index 42c483b0e..000000000
--- a/test/redis-cluster.config
+++ /dev/null
@@ -1,40 +0,0 @@
-port 0
-tls-port 4218
-save 60 1
-maxmemory-policy noeviction
-loglevel warning
-# List of renamed commands comes from:
-# https://www.digitalocean.com/community/tutorials/how-to-secure-your-redis-installation-on-ubuntu-18-04
-rename-command BGREWRITEAOF ""
-rename-command BGSAVE ""
-rename-command CONFIG ""
-rename-command DEBUG ""
-rename-command DEL ""
-rename-command FLUSHALL ""
-rename-command FLUSHDB ""
-rename-command KEYS ""
-rename-command PEXPIRE ""
-rename-command RENAME ""
-rename-command SAVE ""
-rename-command SHUTDOWN ""
-rename-command SPOP ""
-rename-command SREM ""
-user default off
-user ocsp-updater      on +@all ~* >e4e9ce7845cb6adbbc44fb1d9deb05e6b4dc1386
-user ocsp-responder    on +@all ~* >0e5a4c8b5faaf3194c8ad83c3dd9a0dd8a75982b
-user boulder-sa        on +@all ~* >de75ae663596735b90e461e5924f71a4c5f622ab
-user boulder-ra        on +@all ~* >b3b2fcbbf46fe39fd522c395a51f84d93a98ff2f
-user replication-user  on +@all ~* >435e9c4225f08813ef3af7c725f0d30d263b9cd3
-user unittest-rw       on +@all ~* >824968fa490f4ecec1e52d5e34916bdb60d45f8d
-masteruser replication-user
-masterauth 435e9c4225f08813ef3af7c725f0d30d263b9cd3
-tls-protocols "TLSv1.3"
-tls-cert-file /test/redis-tls/redis/cert.pem
-tls-key-file /test/redis-tls/redis/key.pem
-tls-ca-cert-file /test/redis-tls/minica.pem
-tls-cluster yes
-tls-replication yes
-cluster-enabled yes
-cluster-config-file cluster-config-1234.conf
-cluster-node-timeout 5000
-cluster-require-full-coverage no
diff --git a/test/redis-create.sh b/test/redis-create.sh
deleted file mode 100755
index 83c5152b1..000000000
--- a/test/redis-create.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-# TODO(#6517) remove this file.
-
-set -feuo pipefail
-
-ARGS="--tls \
-    --cert /test/redis-tls/redis/cert.pem \
-    --key /test/redis-tls/redis/key.pem \
-    --cacert /test/redis-tls/minica.pem \
-    --user replication-user \
-    --pass 435e9c4225f08813ef3af7c725f0d30d263b9cd3"
-
-if [[ "${BOULDER_CONFIG_DIR}" == "test/config" ]]
-then
-    if ! redis-cli \
-        --cluster check \
-        10.33.33.2:4218 \
-        $ARGS ; then
-    echo "Cluster needs creation!"
-    redis-cli \
-        --cluster-yes \
-        --cluster create \
-        10.33.33.2:4218 10.33.33.3:4218 10.33.33.4:4218 \
-        10.33.33.5:4218 10.33.33.6:4218 10.33.33.7:4218 \
-        --cluster-replicas 1 \
-        $ARGS
-    fi
-fi
-
-# Hack: run redis-server so we have something listening on a port.
-# The Boulder container will wait for this port on this container to be
-# available before starting up.
-echo "Starting a server so everything knows we're done."
-exec redis-server /test/redis.config
diff --git a/test/s3-test-srv/main.go b/test/s3-test-srv/main.go
index 6ec277f4c..70408417f 100644
--- a/test/s3-test-srv/main.go
+++ b/test/s3-test-srv/main.go
@@ -93,6 +93,9 @@ func main() {
 	http.HandleFunc("/clear", srv.handleClear)
 	http.HandleFunc("/query", srv.handleQuery)
 
+	// The gosec linter complains that timeouts cannot be set here. That's fine,
+	// because this is test-only code.
+	////nolint:gosec
 	go log.Fatal(http.ListenAndServe(*listenAddr, nil))
 	cmd.CatchSignals(nil, nil)
 }
diff --git a/test/secrets/nonce_prefix_key b/test/secrets/nonce_prefix_key
new file mode 100644
index 000000000..d65802423
--- /dev/null
+++ b/test/secrets/nonce_prefix_key
@@ -0,0 +1 @@
+3b8c758dd85e113ea340ce0b3a99f389d40a308548af94d1730a7692c1874f1f
diff --git a/test/startservers.py b/test/startservers.py
index 8e864859c..bb35cb3bd 100644
--- a/test/startservers.py
+++ b/test/startservers.py
@@ -103,11 +103,11 @@ SERVICES = (
         ('boulder-ra-1', 'boulder-ra-2', 'mail-test-srv')),
     Service('nonce-service-taro',
         8111, 'nonce1.service.consul:9101',
-        ('./bin/boulder', 'nonce-service', '--config', os.path.join(config_dir, 'nonce.json'), '--addr', 'nonce1.service.consul:9101', '--debug-addr', ':8111', '--prefix', 'taro'),
+        ('./bin/boulder', 'nonce-service', '--config', os.path.join(config_dir, 'nonce-a.json'), '--addr', '10.77.77.77:9101', '--debug-addr', ':8111',),
         None),
     Service('nonce-service-zinc',
         8112, 'nonce2.service.consul:9101',
-        ('./bin/boulder', 'nonce-service', '--config', os.path.join(config_dir, 'nonce.json'), '--addr', 'nonce2.service.consul:9101', '--debug-addr', ':8112', '--prefix', 'zinc'),
+        ('./bin/boulder', 'nonce-service', '--config', os.path.join(config_dir, 'nonce-b.json'), '--addr', '10.88.88.88:9101', '--debug-addr', ':8112',),
         None),
     Service('boulder-wfe2',
         4001, None,
diff --git a/va/http_test.go b/va/http_test.go
index 258b2fd05..6b6307421 100644
--- a/va/http_test.go
+++ b/va/http_test.go
@@ -348,9 +348,9 @@ func TestHTTPValidationDNSIdMismatchError(t *testing.T) {
 			`hostname=\[id\.mismatch\] ` +
 			`respHostname=\[id\.mismatch\.\] ` +
 			`queryType=\[A\] ` +
-			`err\=\[dns: id mismatch\] ` +
 			`msg=\[([A-Za-z0-9+=/\=]+)\] ` +
-			`resp=\[([A-Za-z0-9+=/\=]+)\]`,
+			`resp=\[([A-Za-z0-9+=/\=]+)\] ` +
+			`err\=\[dns: id mismatch\]`,
 	)
 
 	matches := expectedRegex.FindAllStringSubmatch(matchingLines[0], -1)
diff --git a/wfe2/verify_test.go b/wfe2/verify_test.go
index 883425046..6de397c8e 100644
--- a/wfe2/verify_test.go
+++ b/wfe2/verify_test.go
@@ -1295,14 +1295,12 @@ func TestValidJWSForKey(t *testing.T) {
 	for _, tc := range testCases {
 		t.Run(tc.Name, func(t *testing.T) {
 			wfe.stats.joseErrorCount.Reset()
-			inputLogEvent := newRequestEvent()
 			request := makePostRequestWithPath("test", tc.Body)
-			outPayload, prob := wfe.validJWSForKey(context.Background(), tc.JWS, tc.JWK, request, inputLogEvent)
+			outPayload, prob := wfe.validJWSForKey(context.Background(), tc.JWS, tc.JWK, request)
 
 			if tc.ExpectedProblem == nil && prob != nil {
 				t.Fatalf("Expected nil problem, got %#v\n", prob)
 			} else if tc.ExpectedProblem == nil {
-				test.AssertEquals(t, inputLogEvent.Payload, payload)
 				test.AssertEquals(t, string(outPayload), payload)
 			} else {
 				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
@@ -1397,7 +1395,6 @@ func TestValidPOSTForAccount(t *testing.T) {
 			if tc.ExpectedProblem == nil && prob != nil {
 				t.Fatalf("Expected nil problem, got %#v\n", prob)
 			} else if tc.ExpectedProblem == nil {
-				test.AssertEquals(t, inputLogEvent.Payload, tc.ExpectedPayload)
 				test.AssertEquals(t, string(outPayload), tc.ExpectedPayload)
 				test.AssertMarshaledEquals(t, acct, tc.ExpectedAcct)
 				test.AssertMarshaledEquals(t, jws, tc.ExpectedJWS)
@@ -1437,7 +1434,6 @@ func TestValidPOSTAsGETForAccount(t *testing.T) {
 			ExpectedProblem: probs.Malformed("POST-as-GET requests must have an empty payload"),
 			ExpectedLogEvent: web.RequestEvent{
 				Contacts: []string{"mailto:person@mail.com"},
-				Payload:  "{}",
 			},
 		},
 		{
@@ -1544,15 +1540,13 @@ func TestValidSelfAuthenticatedPOST(t *testing.T) {
 	for _, tc := range testCases {
 		t.Run(tc.Name, func(t *testing.T) {
 			wfe.stats.joseErrorCount.Reset()
-			inputLogEvent := newRequestEvent()
-			outPayload, jwk, prob := wfe.validSelfAuthenticatedPOST(context.Background(), tc.Request, inputLogEvent)
+			outPayload, jwk, prob := wfe.validSelfAuthenticatedPOST(context.Background(), tc.Request)
 			if tc.ExpectedProblem == nil && prob != nil {
 				t.Fatalf("Expected nil problem, got %#v\n", prob)
 			} else if tc.ExpectedProblem == nil {
 				inThumb, _ := tc.ExpectedJWK.Thumbprint(crypto.SHA256)
 				outThumb, _ := jwk.Thumbprint(crypto.SHA256)
 				test.AssertDeepEquals(t, inThumb, outThumb)
-				test.AssertEquals(t, inputLogEvent.Payload, tc.ExpectedPayload)
 				test.AssertEquals(t, string(outPayload), tc.ExpectedPayload)
 			} else {
 				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
diff --git a/wfe2/wfe_test.go b/wfe2/wfe_test.go
index e78a4ce65..46ba164a7 100644
--- a/wfe2/wfe_test.go
+++ b/wfe2/wfe_test.go
@@ -350,10 +350,6 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) {
 		testKeyPolicy,
 		certChains,
 		issuerCertificates,
-		nonceGRPCService,
-		map[string]noncepb.NonceServiceClient{
-			noncePrefix: nonceGRPCService,
-		},
 		blog.NewMock(),
 		10*time.Second,
 		10*time.Second,
@@ -361,6 +357,10 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) {
 		7*24*time.Hour,
 		&MockRegistrationAuthority{},
 		mockSA,
+		nonceGRPCService,
+		map[string]nonce.Redeemer{noncePrefix: nonceGRPCService},
+		nonceGRPCService,
+		"",
 		mockSA)
 	test.AssertNotError(t, err, "Unable to create WFE")
 
@@ -914,7 +914,7 @@ func TestNonceEndpoint(t *testing.T) {
 			test.AssertEquals(t, responseWriter.Code, tc.expectedStatus)
 			// And the response should contain a valid nonce in the Replay-Nonce header
 			nonce := responseWriter.Header().Get("Replay-Nonce")
-			redeemResp, err := wfe.remoteNonceService.Redeem(context.Background(), &noncepb.NonceMessage{Nonce: nonce})
+			redeemResp, err := wfe.rnc.Redeem(context.Background(), &noncepb.NonceMessage{Nonce: nonce})
 			test.AssertNotError(t, err, "redeeming nonce")
 			test.AssertEquals(t, redeemResp.Valid, true)
 			// The server MUST include a Cache-Control header field with the "no-store"
@@ -3234,7 +3234,6 @@ func TestPrepAuthzForDisplay(t *testing.T) {
 				ProvidedKeyAuthorization: "	🔑",
 			},
 		},
-		Combinations: [][]int{{1, 2, 3}, {4}, {5, 6}},
 	}
 
 	// Prep the wildcard authz for display
@@ -3244,12 +3243,6 @@ func TestPrepAuthzForDisplay(t *testing.T) {
 	test.AssertEquals(t, strings.HasPrefix(authz.Identifier.Value, "*."), false)
 	// The authz should be marked as corresponding to a wildcard name
 	test.AssertEquals(t, authz.Wildcard, true)
-	// The authz should not have any combinations
-	// NOTE(@cpu): We don't use test.AssertNotNil here because its use of
-	// interface{} types makes a comparison of [][]int{nil} and nil fail.
-	if authz.Combinations != nil {
-		t.Errorf("Authz had a non-nil combinations")
-	}
 
 	// We expect the authz challenge has its URL set and the URI emptied.
 	authz.ID = "12345"
@@ -3627,12 +3620,14 @@ func TestARI(t *testing.T) {
 	resp = httptest.NewRecorder()
 	wfe.RenewalInfo(context.Background(), event, resp, req)
 	test.AssertEquals(t, resp.Code, http.StatusBadRequest)
+	test.AssertContains(t, resp.Body.String(), "Request used hash algorithm other than SHA-256")
 
 	// Ensure that a query with a non-CertID path fails.
 	req, event = makeGet(base64.RawURLEncoding.EncodeToString(ocspReqBytes), renewalInfoPath)
 	resp = httptest.NewRecorder()
 	wfe.RenewalInfo(context.Background(), event, resp, req)
 	test.AssertEquals(t, resp.Code, http.StatusBadRequest)
+	test.AssertContains(t, resp.Body.String(), "Path was not a DER-encoded CertID sequence")
 
 	// Ensure that a query with a non-Base64URL path (including one in the old
 	// request path style, which included slashes) fails.
@@ -3647,6 +3642,14 @@ func TestARI(t *testing.T) {
 	resp = httptest.NewRecorder()
 	wfe.RenewalInfo(context.Background(), event, resp, req)
 	test.AssertEquals(t, resp.Code, http.StatusBadRequest)
+	test.AssertContains(t, resp.Body.String(), "Path was not base64url-encoded")
+
+	// Ensure that a query with no path slug at all bails out early.
+	req, event = makeGet("", renewalInfoPath)
+	resp = httptest.NewRecorder()
+	wfe.RenewalInfo(context.Background(), event, resp, req)
+	test.AssertEquals(t, resp.Code, http.StatusNotFound)
+	test.AssertContains(t, resp.Body.String(), "Must specify a request path")
 }
 
 // TestIncidentARI tests that requests certs impacted by an ongoing revocation