Skip to content

Commit 21e5473

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 de8401e commit 21e5473

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{
@@ -405,8 +405,8 @@ func orderToModelv1(order *corepb.Order) (*orderModelv1, error) {
405405
om := &orderModelv1{
406406
ID: order.Id,
407407
RegistrationID: order.RegistrationID,
408-
Expires: order.Expires.AsTime(),
409-
Created: order.Created.AsTime(),
408+
Expires: order.Expires.AsTime().Truncate(time.Second),
409+
Created: order.Created.AsTime().Truncate(time.Second),
410410
BeganProcessing: order.BeganProcessing,
411411
CertificateSerial: order.CertificateSerial,
412412
}
@@ -452,8 +452,8 @@ func orderToModelv2(order *corepb.Order) (*orderModelv2, error) {
452452
om := &orderModelv2{
453453
ID: order.Id,
454454
RegistrationID: order.RegistrationID,
455-
Expires: order.Expires.AsTime(),
456-
Created: order.Created.AsTime(),
455+
Expires: order.Expires.AsTime().Truncate(time.Second),
456+
Created: order.Created.AsTime().Truncate(time.Second),
457457
BeganProcessing: order.BeganProcessing,
458458
CertificateSerial: order.CertificateSerial,
459459
CertificateProfileName: order.CertificateProfileName,
@@ -689,7 +689,7 @@ func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) {
689689
IdentifierValue: authz.Identifier,
690690
RegistrationID: authz.RegistrationID,
691691
Status: statusToUint[core.AcmeStatus(authz.Status)],
692-
Expires: authz.Expires.AsTime(),
692+
Expires: authz.Expires.AsTime().Truncate(time.Second),
693693
}
694694
if authz.Id != "" {
695695
// The v1 internal authorization objects use a string for the ID, the v2
@@ -728,7 +728,7 @@ func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) {
728728
// If validated Unix timestamp is zero then keep the core.Challenge Validated object nil.
729729
var validated *time.Time
730730
if !core.IsAnyNilOrZero(chall.Validated) {
731-
val := chall.Validated.AsTime()
731+
val := chall.Validated.AsTime().Truncate(time.Second)
732732
validated = &val
733733
}
734734
am.AttemptedAt = validated

sa/sa.go

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

111-
reg.CreatedAt = ssa.clk.Now()
111+
reg.CreatedAt = ssa.clk.Now().Truncate(time.Second)
112112

113113
err = ssa.dbMap.Insert(ctx, reg)
114114
if err != nil {
@@ -169,8 +169,8 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri
169169
err := ssa.dbMap.Insert(ctx, &recordedSerialModel{
170170
Serial: req.Serial,
171171
RegistrationID: req.RegID,
172-
Created: req.Created.AsTime(),
173-
Expires: req.Expires.AsTime(),
172+
Created: req.Created.AsTime().Truncate(time.Second),
173+
Expires: req.Expires.AsTime().Truncate(time.Second),
174174
})
175175
if err != nil {
176176
return nil, err
@@ -223,7 +223,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
223223
Serial: serialHex,
224224
RegistrationID: req.RegID,
225225
DER: req.Der,
226-
Issued: req.Issued.AsTime(),
226+
Issued: req.Issued.AsTime().Truncate(time.Second),
227227
Expires: parsed.NotAfter,
228228
}
229229

@@ -252,13 +252,15 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
252252
cs := &core.CertificateStatus{
253253
Serial: serialHex,
254254
Status: status,
255-
OCSPLastUpdated: ssa.clk.Now(),
255+
OCSPLastUpdated: ssa.clk.Now().Truncate(time.Second),
256256
RevokedDate: time.Time{},
257257
RevokedReason: 0,
258258
LastExpirationNagSent: time.Time{},
259-
NotAfter: parsed.NotAfter,
260-
IsExpired: false,
261-
IssuerNameID: req.IssuerNameID,
259+
// No need to truncate because it's already truncated to encode
260+
// per https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5.1
261+
NotAfter: parsed.NotAfter,
262+
IsExpired: false,
263+
IssuerNameID: req.IssuerNameID,
262264
}
263265
err = ssa.dbMap.Insert(ctx, cs)
264266
if err != nil {
@@ -317,7 +319,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
317319
Serial: serial,
318320
Digest: digest,
319321
DER: req.Der,
320-
Issued: req.Issued.AsTime(),
322+
Issued: req.Issued.AsTime().Truncate(time.Second),
321323
Expires: parsedCertificate.NotAfter,
322324
}
323325

@@ -503,11 +505,11 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
503505
// Second, insert the new order.
504506
var orderID int64
505507
var err error
506-
created := ssa.clk.Now()
508+
created := ssa.clk.Now().Truncate(time.Second)
507509
if features.Get().MultipleCertificateProfiles {
508510
omv2 := orderModelv2{
509511
RegistrationID: req.NewOrder.RegistrationID,
510-
Expires: req.NewOrder.Expires.AsTime(),
512+
Expires: req.NewOrder.Expires.AsTime().Truncate(time.Second),
511513
Created: created,
512514
CertificateProfileName: req.NewOrder.CertificateProfileName,
513515
}
@@ -516,7 +518,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
516518
} else {
517519
omv1 := orderModelv1{
518520
RegistrationID: req.NewOrder.RegistrationID,
519-
Expires: req.NewOrder.Expires.AsTime(),
521+
Expires: req.NewOrder.Expires.AsTime().Truncate(time.Second),
520522
Created: created,
521523
}
522524
err = tx.Insert(ctx, &omv1)
@@ -549,7 +551,13 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
549551
}
550552

551553
// Fourth, insert the FQDNSet entry for the order.
552-
err = addOrderFQDNSet(ctx, tx, req.NewOrder.Names, orderID, req.NewOrder.RegistrationID, req.NewOrder.Expires.AsTime())
554+
err = addOrderFQDNSet(ctx,
555+
tx,
556+
req.NewOrder.Names,
557+
orderID,
558+
req.NewOrder.RegistrationID,
559+
req.NewOrder.Expires.AsTime().Truncate(time.Second),
560+
)
553561
if err != nil {
554562
return nil, err
555563
}
@@ -576,7 +584,12 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
576584
if req.NewOrder.ReplacesSerial != "" {
577585
// Update the replacementOrders table to indicate that this order
578586
// replaces the provided certificate serial.
579-
err := addReplacementOrder(ctx, tx, req.NewOrder.ReplacesSerial, orderID, req.NewOrder.Expires.AsTime())
587+
err := addReplacementOrder(ctx,
588+
tx,
589+
req.NewOrder.ReplacesSerial,
590+
orderID,
591+
req.NewOrder.Expires.AsTime().Truncate(time.Second),
592+
)
580593
if err != nil {
581594
return nil, err
582595
}
@@ -789,7 +802,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
789802
// database attemptedAt field Null instead of 1970-01-01 00:00:00.
790803
var attemptedTime *time.Time
791804
if !core.IsAnyNilOrZero(req.AttemptedAt) {
792-
val := req.AttemptedAt.AsTime()
805+
val := req.AttemptedAt.AsTime().Truncate(time.Second)
793806
attemptedTime = &val
794807
}
795808
params := map[string]interface{}{
@@ -799,7 +812,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
799812
"validationRecord": vrJSON,
800813
"id": req.Id,
801814
"pending": statusUint(core.StatusPending),
802-
"expires": req.Expires.AsTime(),
815+
"expires": req.Expires.AsTime().Truncate(time.Second),
803816
// if req.ValidationError is nil veJSON should also be nil
804817
// which should result in a NULL field
805818
"validationError": veJSON,
@@ -867,7 +880,7 @@ func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb
867880
}
868881

869882
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
870-
revokedDate := req.Date.AsTime()
883+
revokedDate := req.Date.AsTime().Truncate(time.Second)
871884

872885
res, err := tx.ExecContext(ctx,
873886
`UPDATE certificateStatus SET
@@ -924,8 +937,8 @@ func (ssa *SQLStorageAuthority) UpdateRevokedCertificate(ctx context.Context, re
924937
}
925938

926939
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
927-
thisUpdate := req.Date.AsTime()
928-
revokedDate := req.Backdate.AsTime()
940+
thisUpdate := req.Date.AsTime().Truncate(time.Second)
941+
revokedDate := req.Backdate.AsTime().Truncate(time.Second)
929942

930943
res, err := tx.ExecContext(ctx,
931944
`UPDATE certificateStatus SET
@@ -1007,7 +1020,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add
10071020
cols, qs := blockedKeysColumns, "?, ?, ?, ?"
10081021
vals := []interface{}{
10091022
req.KeyHash,
1010-
req.Added.AsTime(),
1023+
req.Added.AsTime().Truncate(time.Second),
10111024
sourceInt,
10121025
req.Comment,
10131026
}
@@ -1129,7 +1142,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
11291142
VALUES (?, ?, ?)`,
11301143
req.IssuerNameID,
11311144
shardIdx,
1132-
req.Until.AsTime(),
1145+
req.Until.AsTime().Truncate(time.Second),
11331146
)
11341147
if err != nil {
11351148
return -1, fmt.Errorf("inserting selected shard: %w", err)
@@ -1141,7 +1154,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
11411154
WHERE issuerID = ?
11421155
AND idx = ?
11431156
LIMIT 1`,
1144-
req.Until.AsTime(),
1157+
req.Until.AsTime().Truncate(time.Second),
11451158
req.IssuerNameID,
11461159
shardIdx,
11471160
)
@@ -1197,7 +1210,7 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *
11971210
VALUES (?, ?, ?)`,
11981211
req.IssuerNameID,
11991212
req.MinShardIdx,
1200-
req.Until.AsTime(),
1213+
req.Until.AsTime().Truncate(time.Second),
12011214
)
12021215
if err != nil {
12031216
return nil, fmt.Errorf("inserting selected shard: %w", err)
@@ -1209,7 +1222,7 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *
12091222
WHERE issuerID = ?
12101223
AND idx = ?
12111224
LIMIT 1`,
1212-
req.Until.AsTime(),
1225+
req.Until.AsTime().Truncate(time.Second),
12131226
req.IssuerNameID,
12141227
req.MinShardIdx,
12151228
)
@@ -1247,24 +1260,25 @@ func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.Up
12471260
// Only set the nextUpdate if it's actually present in the request message.
12481261
var nextUpdate *time.Time
12491262
if req.NextUpdate != nil {
1250-
nut := req.NextUpdate.AsTime()
1263+
nut := req.NextUpdate.AsTime().Truncate(time.Second)
12511264
nextUpdate = &nut
12521265
}
12531266

12541267
_, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
1268+
thisUpdate := req.ThisUpdate.AsTime().Truncate(time.Second)
12551269
res, err := tx.ExecContext(ctx,
12561270
`UPDATE crlShards
12571271
SET thisUpdate = ?, nextUpdate = ?, leasedUntil = ?
12581272
WHERE issuerID = ?
12591273
AND idx = ?
12601274
AND (thisUpdate is NULL OR thisUpdate < ?)
12611275
LIMIT 1`,
1262-
req.ThisUpdate.AsTime(),
1276+
thisUpdate,
12631277
nextUpdate,
1264-
req.ThisUpdate.AsTime(),
1278+
thisUpdate,
12651279
req.IssuerNameID,
12661280
req.ShardIdx,
1267-
req.ThisUpdate.AsTime(),
1281+
thisUpdate,
12681282
)
12691283
if err != nil {
12701284
return nil, err

0 commit comments

Comments
 (0)