Skip to content

Commit 3d80d85

Browse files
authored
SA: gRPC methods for leasing CRL shards (#6940)
Add two new methods, LeaseCRLShard and UpdateCRLShard, to the SA gRPC interface. These methods work in concert both to prevent multiple instances of crl-updater from stepping on each others toes, and to lay the groundwork for a less bursty version of crl-updater in the future. Introduce a new database table, crlShards, which tracks the thisUpdate and nextUpdate timestamps of each CRL shard for each issuer. It also has a column "leasedUntil", which is also a timestamp. Grant the SA user read-write access to this table. LeaseCRLShard updates the leasedUntil column of the identified shard to the given time. It returns an error if the identified shard's leasedUntil timestamp is already in the future. This provides a mechanism for crl-updater instances to "lick the cookie", so to speak, marking CRL shards as "taken" so that multiple crl-updater instances don't attempt to work on the same shard at the same time. Using a timestamp has the added benefit that leases are guaranteed to expire, ensuring that we don't accidentally fail to work on a shard forever. LeaseCRLShard has a second mode of operation, when a range of potential shards is given in the request, rather than a single shard. In this mode, it returns the shard (within the given range) whose thisUpdate timestamp is oldest. (Shards with no thisUpdate timestamp, including because the requested range includes shard indices the database doesn't yet know about, count as older than any shard with any thisUpdate timestamp.) This allows crl-updater instances which don't care which shard they're working on to do the most urgent work first. UpdateCRLShard updates the thisUpdate and nextUpdate timestamps of the identified shard. This closes the loop with the second mode of LeaseCRLShard above: by updating the thisUpdate timestamp, the method marks the shard as no longer urgently needing to be worked on. IN-9220 tracks creating this table in staging and production Part of #6897
1 parent f6a005b commit 3d80d85

File tree

10 files changed

+1442
-490
lines changed

10 files changed

+1442
-490
lines changed

mocks/mocks.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ func (sa *StorageAuthorityReadOnly) GetCertificateStatus(_ context.Context, req
245245
Status: string(core.OCSPStatusRevoked),
246246
}, nil
247247
} else {
248-
return nil, errors.New("No cert status")
248+
return nil, errors.New("no cert status")
249249
}
250250
}
251251

@@ -355,7 +355,6 @@ func (sa *StorageAuthority) DeactivateRegistration(_ context.Context, _ *sapb.Re
355355

356356
// NewOrderAndAuthzs is a mock
357357
func (sa *StorageAuthority) NewOrderAndAuthzs(_ context.Context, req *sapb.NewOrderAndAuthzsRequest, _ ...grpc.CallOption) (*corepb.Order, error) {
358-
rand.Seed(time.Now().UnixNano())
359358
response := &corepb.Order{
360359
// Fields from the input new order request.
361360
RegistrationID: req.NewOrder.RegistrationID,
@@ -556,7 +555,7 @@ func (sa *StorageAuthorityReadOnly) GetAuthorization2(ctx context.Context, id *s
556555
authz.ID = fmt.Sprintf("%d", authzIdExpired)
557556
return bgrpc.AuthzToPB(authz)
558557
case authzIdErrorResult:
559-
return nil, fmt.Errorf("Unspecified database error")
558+
return nil, fmt.Errorf("unspecified database error")
560559
case authzIdDiffAccount:
561560
exp := sa.clk.Now().AddDate(100, 0, 0)
562561
authz.RegistrationID = 2
@@ -593,6 +592,16 @@ func (sa *StorageAuthorityReadOnly) IncidentsForSerial(ctx context.Context, req
593592
return &sapb.Incidents{}, nil
594593
}
595594

595+
// LeaseCRLShard is a mock.
596+
func (sa *StorageAuthority) LeaseCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest, _ ...grpc.CallOption) (*sapb.LeaseCRLShardResponse, error) {
597+
return nil, errors.New("unimplemented")
598+
}
599+
600+
// UpdateCRLShard is a mock.
601+
func (sa *StorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.UpdateCRLShardRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
602+
return nil, errors.New("unimplemented")
603+
}
604+
596605
// Publisher is a mock
597606
type PublisherClient struct {
598607
// empty

sa/database.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ func initTables(dbMap *gorp.DbMap) {
281281
dbMap.AddTableWithName(keyHashModel{}, "keyHashToSerial").SetKeys(true, "ID")
282282
dbMap.AddTableWithName(incidentModel{}, "incidents").SetKeys(true, "ID")
283283
dbMap.AddTable(incidentSerialModel{})
284+
dbMap.AddTableWithName(crlShardModel{}, "crlShards").SetKeys(true, "ID")
284285

285286
// Read-only maps used for selecting subsets of columns.
286287
dbMap.AddTableWithName(CertStatusMetadata{}, "certificateStatus")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- +migrate Up
2+
-- SQL in section 'Up' is executed when this migration is applied
3+
4+
CREATE TABLE `crlShards` (
5+
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
6+
`issuerID` bigint(20) NOT NULL,
7+
`idx` int UNSIGNED NOT NULL,
8+
`thisUpdate` datetime,
9+
`nextUpdate` datetime,
10+
`leasedUntil` datetime NOT NULL,
11+
PRIMARY KEY (`id`),
12+
UNIQUE KEY `shardID` (`issuerID`, `idx`)
13+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
14+
15+
-- +migrate Down
16+
-- SQL section 'Down' is executed when this migration is rolled back
17+
18+
DROP TABLE `crlShards`;

sa/db-users/boulder_sa.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ GRANT SELECT,INSERT ON keyHashToSerial TO 'sa'@'localhost';
3232
GRANT SELECT,INSERT ON blockedKeys TO 'sa'@'localhost';
3333
GRANT SELECT,INSERT,UPDATE ON newOrdersRL TO 'sa'@'localhost';
3434
GRANT SELECT ON incidents TO 'sa'@'localhost';
35+
GRANT SELECT,INSERT,UPDATE ON crlShards TO 'sa'@'localhost';
3536

3637
GRANT SELECT ON certificates TO 'sa_ro'@'localhost';
3738
GRANT SELECT ON certificateStatus TO 'sa_ro'@'localhost';
@@ -50,6 +51,7 @@ GRANT SELECT ON keyHashToSerial TO 'sa_ro'@'localhost';
5051
GRANT SELECT ON blockedKeys TO 'sa_ro'@'localhost';
5152
GRANT SELECT ON newOrdersRL TO 'sa_ro'@'localhost';
5253
GRANT SELECT ON incidents TO 'sa_ro'@'localhost';
54+
GRANT SELECT ON crlShards TO 'sa_ro'@'localhost';
5355

5456
-- OCSP Responder
5557
GRANT SELECT ON certificateStatus TO 'ocsp_resp'@'localhost';

sa/model.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,3 +1168,14 @@ func namesForOrder(s db.Selector, orderID int64) ([]string, error) {
11681168
}
11691169
return reversedNames, nil
11701170
}
1171+
1172+
// crlShardModel represents one row in the crlShards table. The ThisUpdate and
1173+
// NextUpdate fields are pointers because they are NULL-able columns.
1174+
type crlShardModel struct {
1175+
ID int64 `db:"id"`
1176+
IssuerID int64 `db:"issuerID"`
1177+
Idx int `db:"idx"`
1178+
ThisUpdate *time.Time `db:"thisUpdate"`
1179+
NextUpdate *time.Time `db:"nextUpdate"`
1180+
LeasedUntil time.Time `db:"leasedUntil"`
1181+
}

0 commit comments

Comments
 (0)