Skip to content

Commit 4c20f5a

Browse files
committed
sa: truncate all timestamps to seconds
As described in #7075, go-sql-driver/mysql v1.5.0 truncates timestamps to microseconds, while v1.6.0 and above does not. That means upon upgrading to v1.6.0, timestamps are written to the database with a resolution of nanoseconds, and SELECT statements also use a resolution of nanoseconds. We believe this is the cause of performance problems we observed when upgrading to v1.6.0 and above. To fix that, apply rounding in the application code. Rather than just rounding to microseconds, round to seconds since that is the resolution we care about. Using seconds rather than microseconds may also allow some of our indexes to grow more slowly over time.
1 parent f24a979 commit 4c20f5a

File tree

4 files changed

+70
-55
lines changed

4 files changed

+70
-55
lines changed

cmd/bad-key-revoker/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (bkr *badKeyRevoker) findUnrevoked(ctx context.Context, unchecked unchecked
141141
"SELECT id, certSerial FROM keyHashToSerial WHERE keyHash = ? AND id > ? AND certNotAfter > ? ORDER BY id LIMIT ?",
142142
unchecked.KeyHash,
143143
initialID,
144-
bkr.clk.Now(),
144+
bkr.clk.Now().Truncate(time.Second),
145145
bkr.serialBatchSize,
146146
)
147147
if err != nil {

sa/model.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ func registrationPbToModel(reg *corepb.Registration) (*regModel, error) {
304304

305305
var createdAt time.Time
306306
if !core.IsAnyNilOrZero(reg.CreatedAt) {
307-
createdAt = reg.CreatedAt.AsTime()
307+
createdAt = reg.CreatedAt.AsTime().Truncate(time.Second)
308308
}
309309

310310
return &regModel{
@@ -411,8 +411,8 @@ func orderToModelv1(order *corepb.Order) (*orderModelv1, error) {
411411
om := &orderModelv1{
412412
ID: order.Id,
413413
RegistrationID: order.RegistrationID,
414-
Expires: order.Expires.AsTime(),
415-
Created: order.Created.AsTime(),
414+
Expires: order.Expires.AsTime().Truncate(time.Second),
415+
Created: order.Created.AsTime().Truncate(time.Second),
416416
BeganProcessing: order.BeganProcessing,
417417
CertificateSerial: order.CertificateSerial,
418418
}
@@ -458,8 +458,8 @@ func orderToModelv2(order *corepb.Order) (*orderModelv2, error) {
458458
om := &orderModelv2{
459459
ID: order.Id,
460460
RegistrationID: order.RegistrationID,
461-
Expires: order.Expires.AsTime(),
462-
Created: order.Created.AsTime(),
461+
Expires: order.Expires.AsTime().Truncate(time.Second),
462+
Created: order.Created.AsTime().Truncate(time.Second),
463463
BeganProcessing: order.BeganProcessing,
464464
CertificateSerial: order.CertificateSerial,
465465
CertificateProfileName: order.CertificateProfileName,
@@ -695,7 +695,7 @@ func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) {
695695
IdentifierValue: authz.Identifier,
696696
RegistrationID: authz.RegistrationID,
697697
Status: statusToUint[core.AcmeStatus(authz.Status)],
698-
Expires: authz.Expires.AsTime(),
698+
Expires: authz.Expires.AsTime().Truncate(time.Second),
699699
}
700700
if authz.Id != "" {
701701
// The v1 internal authorization objects use a string for the ID, the v2
@@ -734,7 +734,7 @@ func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) {
734734
// If validated Unix timestamp is zero then keep the core.Challenge Validated object nil.
735735
var validated *time.Time
736736
if !core.IsAnyNilOrZero(chall.Validated) {
737-
val := chall.Validated.AsTime()
737+
val := chall.Validated.AsTime().Truncate(time.Second)
738738
validated = &val
739739
}
740740
am.AttemptedAt = validated

sa/sa.go

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (ssa *SQLStorageAuthority) NewRegistration(ctx context.Context, req *corepb
105105
return nil, err
106106
}
107107

108-
reg.CreatedAt = ssa.clk.Now()
108+
reg.CreatedAt = ssa.clk.Now().Truncate(time.Second)
109109

110110
err = ssa.dbMap.Insert(ctx, reg)
111111
if err != nil {
@@ -166,8 +166,8 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri
166166
err := ssa.dbMap.Insert(ctx, &recordedSerialModel{
167167
Serial: req.Serial,
168168
RegistrationID: req.RegID,
169-
Created: req.Created.AsTime(),
170-
Expires: req.Expires.AsTime(),
169+
Created: req.Created.AsTime().Truncate(time.Second),
170+
Expires: req.Expires.AsTime().Truncate(time.Second),
171171
})
172172
if err != nil {
173173
return nil, err
@@ -220,7 +220,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
220220
Serial: serialHex,
221221
RegistrationID: req.RegID,
222222
DER: req.Der,
223-
Issued: req.Issued.AsTime(),
223+
Issued: req.Issued.AsTime().Truncate(time.Second),
224224
Expires: parsed.NotAfter,
225225
}
226226

@@ -249,13 +249,15 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
249249
cs := &core.CertificateStatus{
250250
Serial: serialHex,
251251
Status: status,
252-
OCSPLastUpdated: ssa.clk.Now(),
252+
OCSPLastUpdated: ssa.clk.Now().Truncate(time.Second),
253253
RevokedDate: time.Time{},
254254
RevokedReason: 0,
255255
LastExpirationNagSent: time.Time{},
256-
NotAfter: parsed.NotAfter,
257-
IsExpired: false,
258-
IssuerNameID: req.IssuerNameID,
256+
// No need to truncate because it's already truncated to encode
257+
// per https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5.1
258+
NotAfter: parsed.NotAfter,
259+
IsExpired: false,
260+
IssuerNameID: req.IssuerNameID,
259261
}
260262
err = ssa.dbMap.Insert(ctx, cs)
261263
if err != nil {
@@ -314,7 +316,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
314316
Serial: serial,
315317
Digest: digest,
316318
DER: req.Der,
317-
Issued: req.Issued.AsTime(),
319+
Issued: req.Issued.AsTime().Truncate(time.Second),
318320
Expires: parsedCertificate.NotAfter,
319321
}
320322

@@ -500,11 +502,11 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
500502
// Second, insert the new order.
501503
var orderID int64
502504
var err error
503-
created := ssa.clk.Now()
505+
created := ssa.clk.Now().Truncate(time.Second)
504506
if features.Get().MultipleCertificateProfiles {
505507
omv2 := orderModelv2{
506508
RegistrationID: req.NewOrder.RegistrationID,
507-
Expires: req.NewOrder.Expires.AsTime(),
509+
Expires: req.NewOrder.Expires.AsTime().Truncate(time.Second),
508510
Created: created,
509511
CertificateProfileName: req.NewOrder.CertificateProfileName,
510512
}
@@ -513,7 +515,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
513515
} else {
514516
omv1 := orderModelv1{
515517
RegistrationID: req.NewOrder.RegistrationID,
516-
Expires: req.NewOrder.Expires.AsTime(),
518+
Expires: req.NewOrder.Expires.AsTime().Truncate(time.Second),
517519
Created: created,
518520
}
519521
err = tx.Insert(ctx, &omv1)
@@ -563,7 +565,13 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
563565
}
564566

565567
// Fifth, insert the FQDNSet entry for the order.
566-
err = addOrderFQDNSet(ctx, tx, req.NewOrder.Names, orderID, req.NewOrder.RegistrationID, req.NewOrder.Expires.AsTime())
568+
err = addOrderFQDNSet(ctx,
569+
tx,
570+
req.NewOrder.Names,
571+
orderID,
572+
req.NewOrder.RegistrationID,
573+
req.NewOrder.Expires.AsTime().Truncate(time.Second),
574+
)
567575
if err != nil {
568576
return nil, err
569577
}
@@ -590,7 +598,12 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
590598
if req.NewOrder.ReplacesSerial != "" {
591599
// Update the replacementOrders table to indicate that this order
592600
// replaces the provided certificate serial.
593-
err := addReplacementOrder(ctx, tx, req.NewOrder.ReplacesSerial, orderID, req.NewOrder.Expires.AsTime())
601+
err := addReplacementOrder(ctx,
602+
tx,
603+
req.NewOrder.ReplacesSerial,
604+
orderID,
605+
req.NewOrder.Expires.AsTime().Truncate(time.Second),
606+
)
594607
if err != nil {
595608
return nil, err
596609
}
@@ -803,7 +816,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
803816
// database attemptedAt field Null instead of 1970-01-01 00:00:00.
804817
var attemptedTime *time.Time
805818
if !core.IsAnyNilOrZero(req.AttemptedAt) {
806-
val := req.AttemptedAt.AsTime()
819+
val := req.AttemptedAt.AsTime().Truncate(time.Second)
807820
attemptedTime = &val
808821
}
809822
params := map[string]interface{}{
@@ -813,7 +826,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
813826
"validationRecord": vrJSON,
814827
"id": req.Id,
815828
"pending": statusUint(core.StatusPending),
816-
"expires": req.Expires.AsTime(),
829+
"expires": req.Expires.AsTime().Truncate(time.Second),
817830
// if req.ValidationError is nil veJSON should also be nil
818831
// which should result in a NULL field
819832
"validationError": veJSON,
@@ -881,7 +894,7 @@ func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb
881894
}
882895

883896
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
884-
revokedDate := req.Date.AsTime()
897+
revokedDate := req.Date.AsTime().Truncate(time.Second)
885898

886899
res, err := tx.ExecContext(ctx,
887900
`UPDATE certificateStatus SET
@@ -938,8 +951,8 @@ func (ssa *SQLStorageAuthority) UpdateRevokedCertificate(ctx context.Context, re
938951
}
939952

940953
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
941-
thisUpdate := req.Date.AsTime()
942-
revokedDate := req.Backdate.AsTime()
954+
thisUpdate := req.Date.AsTime().Truncate(time.Second)
955+
revokedDate := req.Backdate.AsTime().Truncate(time.Second)
943956

944957
res, err := tx.ExecContext(ctx,
945958
`UPDATE certificateStatus SET
@@ -1021,7 +1034,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add
10211034
cols, qs := blockedKeysColumns, "?, ?, ?, ?"
10221035
vals := []interface{}{
10231036
req.KeyHash,
1024-
req.Added.AsTime(),
1037+
req.Added.AsTime().Truncate(time.Second),
10251038
sourceInt,
10261039
req.Comment,
10271040
}
@@ -1143,7 +1156,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
11431156
VALUES (?, ?, ?)`,
11441157
req.IssuerNameID,
11451158
shardIdx,
1146-
req.Until.AsTime(),
1159+
req.Until.AsTime().Truncate(time.Second),
11471160
)
11481161
if err != nil {
11491162
return -1, fmt.Errorf("inserting selected shard: %w", err)
@@ -1155,7 +1168,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
11551168
WHERE issuerID = ?
11561169
AND idx = ?
11571170
LIMIT 1`,
1158-
req.Until.AsTime(),
1171+
req.Until.AsTime().Truncate(time.Second),
11591172
req.IssuerNameID,
11601173
shardIdx,
11611174
)
@@ -1211,7 +1224,7 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *
12111224
VALUES (?, ?, ?)`,
12121225
req.IssuerNameID,
12131226
req.MinShardIdx,
1214-
req.Until.AsTime(),
1227+
req.Until.AsTime().Truncate(time.Second),
12151228
)
12161229
if err != nil {
12171230
return nil, fmt.Errorf("inserting selected shard: %w", err)
@@ -1223,7 +1236,7 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *
12231236
WHERE issuerID = ?
12241237
AND idx = ?
12251238
LIMIT 1`,
1226-
req.Until.AsTime(),
1239+
req.Until.AsTime().Truncate(time.Second),
12271240
req.IssuerNameID,
12281241
req.MinShardIdx,
12291242
)
@@ -1261,24 +1274,25 @@ func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.Up
12611274
// Only set the nextUpdate if it's actually present in the request message.
12621275
var nextUpdate *time.Time
12631276
if req.NextUpdate != nil {
1264-
nut := req.NextUpdate.AsTime()
1277+
nut := req.NextUpdate.AsTime().Truncate(time.Second)
12651278
nextUpdate = &nut
12661279
}
12671280

12681281
_, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
1282+
thisUpdate := req.ThisUpdate.AsTime().Truncate(time.Second)
12691283
res, err := tx.ExecContext(ctx,
12701284
`UPDATE crlShards
12711285
SET thisUpdate = ?, nextUpdate = ?, leasedUntil = ?
12721286
WHERE issuerID = ?
12731287
AND idx = ?
12741288
AND (thisUpdate is NULL OR thisUpdate < ?)
12751289
LIMIT 1`,
1276-
req.ThisUpdate.AsTime(),
1290+
thisUpdate,
12771291
nextUpdate,
1278-
req.ThisUpdate.AsTime(),
1292+
thisUpdate,
12791293
req.IssuerNameID,
12801294
req.ShardIdx,
1281-
req.ThisUpdate.AsTime(),
1295+
thisUpdate,
12821296
)
12831297
if err != nil {
12841298
return nil, err

0 commit comments

Comments
 (0)