Skip to content

Commit e3b5db5

Browse files
database: Add vitess + mysql 8.0 to our development environment
1 parent 88ed5e7 commit e3b5db5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+564
-213
lines changed

.github/workflows/boulder-ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,19 @@ jobs:
5454
- "./t.sh --unit --enable-race-detection"
5555
- "./tn.sh --unit --enable-race-detection"
5656
- "./t.sh --start-py"
57+
# Same cases but backed by Vitess + MySQL 8 instead of ProxySQL + MariaDB
58+
- "./t.sh --use-vitess --integration"
59+
- "./tn.sh --use-vitess --integration"
60+
- "./t.sh --use-vitess --unit --enable-race-detection"
61+
- "./tn.sh --use-vitess --unit --enable-race-detection"
62+
- "./t.sh --use-vitess --start-py"
5763

5864
env:
5965
# This sets the docker image tag for the boulder-tools repository to
6066
# use in tests. It will be set appropriately for each tag in the list
6167
# defined in the matrix.
6268
BOULDER_TOOLS_TAG: ${{ matrix.BOULDER_TOOLS_TAG }}
69+
BOULDER_VTCOMBOSERVER_TAG: vitessv22.0.0_2025-11-03
6370

6471
# Sequence of tasks that will be executed as part of the job.
6572
steps:

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,16 @@ test/proxysql/*.log*
4343

4444
# Coverage files
4545
test/coverage
46+
47+
# Database config symlinks
48+
sa/db/dbconfig.yml
49+
test/secrets/backfiller_dburl
50+
test/secrets/badkeyrevoker_dburl
51+
test/secrets/cert_checker_dburl
52+
test/secrets/expiration_mailer_dburl
53+
test/secrets/incidents_dburl
54+
test/secrets/mailer_dburl
55+
test/secrets/ocsp_responder_dburl
56+
test/secrets/revoker_dburl
57+
test/secrets/sa_dburl
58+
test/secrets/sa_ro_dburl

docker-compose.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ services:
5252
depends_on:
5353
- bmysql
5454
- bproxysql
55+
- bvitess
5556
- bredis_1
5657
- bredis_2
5758
- bconsul
@@ -79,7 +80,7 @@ services:
7980
networks:
8081
bouldernet:
8182
aliases:
82-
- boulder-mysql
83+
- boulder-mariadb
8384
environment:
8485
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
8586
# Send slow queries to a table so we can check for them in the
@@ -144,6 +145,21 @@ services:
144145
networks:
145146
- bouldernet
146147

148+
bvitess:
149+
# The `letsencrypt/boulder-vtcomboserver:latest` tag is automatically built
150+
# in local dev environments. In CI a specific BOULDER_VTCOMBOSERVER_TAG is
151+
# passed, and it is pulled with `docker compose pull`.
152+
image: letsencrypt/boulder-vtcomboserver:${BOULDER_VTCOMBOSERVER_TAG:-latest}
153+
environment:
154+
# By specifying KEYSPACES vttestserver will create the corresponding
155+
# databases on startup.
156+
KEYSPACES: boulder_sa_test,boulder_sa_integration,incidents_sa_test,incidents_sa_integration
157+
NUM_SHARDS: 1,1,1,1
158+
networks:
159+
bouldernet:
160+
aliases:
161+
- boulder-vitess
162+
147163
networks:
148164
# This network represents the data-center internal network. It is used for
149165
# boulder services and their infrastructure, such as consul, mariadb, and

sa/database.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,6 @@ func adjustMySQLConfig(conf *mysql.Config) error {
201201
}
202202
}
203203

204-
// If a given parameter has the value "0", delete it from conf.Params.
205-
omitZero := func(name string) {
206-
if conf.Params[name] == "0" {
207-
delete(conf.Params, name)
208-
}
209-
}
210-
211204
// Ensures that MySQL/MariaDB warnings are treated as errors. This
212205
// avoids a number of nasty edge conditions we could wander into.
213206
// Common things this discovers includes places where data being sent
@@ -216,26 +209,6 @@ func adjustMySQLConfig(conf *mysql.Config) error {
216209
// <https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html#sql-mode-strict>.
217210
setDefault("sql_mode", "'STRICT_ALL_TABLES'")
218211

219-
// If a read timeout is set, we set max_statement_time to 95% of that, and
220-
// long_query_time to 80% of that. That way we get logs of queries that are
221-
// close to timing out but not yet doing so, and our queries get stopped by
222-
// max_statement_time before timing out the read. This generates clearer
223-
// errors, and avoids unnecessary reconnects.
224-
// To override these values, set them in the DSN, e.g.
225-
// `?max_statement_time=2`. A zero value in the DSN means these won't be
226-
// sent on new connections.
227-
if conf.ReadTimeout != 0 {
228-
// In MariaDB, max_statement_time and long_query_time are both seconds,
229-
// but can have up to microsecond granularity.
230-
// Note: in MySQL (which we don't use), max_statement_time is millis.
231-
readTimeout := conf.ReadTimeout.Seconds()
232-
setDefault("max_statement_time", fmt.Sprintf("%.6f", readTimeout*0.95))
233-
setDefault("long_query_time", fmt.Sprintf("%.6f", readTimeout*0.80))
234-
}
235-
236-
omitZero("max_statement_time")
237-
omitZero("long_query_time")
238-
239212
// Finally, perform validation over all variables set by the DSN and via Boulder.
240213
for k, v := range conf.Params {
241214
err := checkMariaDBSystemVariables(k, v)

sa/database_test.go

Lines changed: 10 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,38 @@ import (
44
"context"
55
"database/sql"
66
"errors"
7+
"fmt"
78
"os"
89
"path"
910
"strings"
1011
"testing"
1112
"time"
1213

13-
"github.com/go-sql-driver/mysql"
1414
"github.com/letsencrypt/boulder/cmd"
1515
"github.com/letsencrypt/boulder/config"
1616
"github.com/letsencrypt/boulder/test"
1717
"github.com/letsencrypt/boulder/test/vars"
1818
)
1919

20+
var dbHost = os.Getenv("MYSQL_ADDR")
21+
2022
func TestInvalidDSN(t *testing.T) {
2123
_, err := DBMapForTest("invalid")
2224
test.AssertError(t, err, "DB connect string missing the slash separating the database name")
2325

24-
DSN := "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&stringVarThatDoesntExist=%27whoopsidaisies"
26+
DSN := fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&stringVarThatDoesntExist=%%27whoopsidaisies", dbHost)
2527
_, err = DBMapForTest(DSN)
2628
test.AssertError(t, err, "Variable does not exist in curated system var list, but didn't return an error and should have")
2729

28-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=2"
30+
DSN = fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=2", dbHost)
2931
_, err = DBMapForTest(DSN)
3032
test.AssertError(t, err, "Variable is unable to be set in the SESSION scope, but was declared")
3133

32-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&optimizer_switch=incorrect-quoted-string"
34+
DSN = fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&optimizer_switch=incorrect-quoted-string", dbHost)
3335
_, err = DBMapForTest(DSN)
3436
test.AssertError(t, err, "Variable declared with incorrect quoting")
3537

36-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=%272%27"
38+
DSN = fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=%%272%%27", dbHost)
3739
_, err = DBMapForTest(DSN)
3840
test.AssertError(t, err, "Integer enum declared, but should not have been quoted")
3941
}
@@ -76,7 +78,7 @@ func TestDbSettings(t *testing.T) {
7678
}
7779
dsnFile := path.Join(t.TempDir(), "dbconnect")
7880
err := os.WriteFile(dsnFile,
79-
[]byte("sa@tcp(boulder-proxysql:6033)/boulder_sa_integration"),
81+
[]byte(fmt.Sprintf("sa@tcp(%s)/boulder_sa_integration", dbHost)),
8082
os.ModeAppend)
8183
test.AssertNotError(t, err, "writing dbconnect file")
8284

@@ -107,8 +109,8 @@ func TestDbSettings(t *testing.T) {
107109

108110
// TODO: Change this to test `newDbMapFromMySQLConfig` instead?
109111
func TestNewDbMap(t *testing.T) {
110-
const mysqlConnectURL = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms"
111-
const expected = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?clientFoundRows=true&parseTime=true&readTimeout=800ms&writeTimeout=800ms&long_query_time=0.640000&max_statement_time=0.760000&sql_mode=%27STRICT_ALL_TABLES%27"
112+
mysqlConnectURL := fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms", dbHost)
113+
expected := fmt.Sprintf("policy:password@tcp(%s)/boulder_policy_integration?clientFoundRows=true&parseTime=true&readTimeout=800ms&writeTimeout=800ms&sql_mode=%%27STRICT_ALL_TABLES%%27", dbHost)
112114
oldSQLOpen := sqlOpen
113115
defer func() {
114116
sqlOpen = oldSQLOpen
@@ -146,27 +148,6 @@ func TestStrictness(t *testing.T) {
146148
}
147149
}
148150

149-
func TestTimeouts(t *testing.T) {
150-
dbMap, err := DBMapForTest(vars.DBConnSA + "?max_statement_time=1")
151-
if err != nil {
152-
t.Fatal("Error setting up DB:", err)
153-
}
154-
// SLEEP is defined to return 1 if it was interrupted, but we want to actually
155-
// get an error to simulate what would happen with a slow query. So we wrap
156-
// the SLEEP in a subselect.
157-
_, err = dbMap.ExecContext(ctx, `SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`)
158-
if err == nil {
159-
t.Fatal("Expected error when running slow query, got none.")
160-
}
161-
162-
// We expect to get:
163-
// Error 1969: Query execution was interrupted (max_statement_time exceeded)
164-
// https://mariadb.com/kb/en/mariadb/mariadb-error-codes/
165-
if !strings.Contains(err.Error(), "Error 1969") {
166-
t.Fatalf("Got wrong type of error: %s", err)
167-
}
168-
}
169-
170151
// TestAutoIncrementSchema tests that all of the tables in the boulder_*
171152
// databases that have auto_increment columns use BIGINT for the data type. Our
172153
// data is too big for INT.
@@ -185,45 +166,3 @@ func TestAutoIncrementSchema(t *testing.T) {
185166
test.AssertNotError(t, err, "unexpected err querying columns")
186167
test.AssertEquals(t, count, int64(0))
187168
}
188-
189-
func TestAdjustMySQLConfig(t *testing.T) {
190-
conf := &mysql.Config{}
191-
err := adjustMySQLConfig(conf)
192-
test.AssertNotError(t, err, "unexpected err setting server variables")
193-
test.AssertDeepEquals(t, conf.Params, map[string]string{
194-
"sql_mode": "'STRICT_ALL_TABLES'",
195-
})
196-
197-
conf = &mysql.Config{ReadTimeout: 100 * time.Second}
198-
err = adjustMySQLConfig(conf)
199-
test.AssertNotError(t, err, "unexpected err setting server variables")
200-
test.AssertDeepEquals(t, conf.Params, map[string]string{
201-
"sql_mode": "'STRICT_ALL_TABLES'",
202-
"max_statement_time": "95.000000",
203-
"long_query_time": "80.000000",
204-
})
205-
206-
conf = &mysql.Config{
207-
ReadTimeout: 100 * time.Second,
208-
Params: map[string]string{
209-
"max_statement_time": "0",
210-
},
211-
}
212-
err = adjustMySQLConfig(conf)
213-
test.AssertNotError(t, err, "unexpected err setting server variables")
214-
test.AssertDeepEquals(t, conf.Params, map[string]string{
215-
"sql_mode": "'STRICT_ALL_TABLES'",
216-
"long_query_time": "80.000000",
217-
})
218-
219-
conf = &mysql.Config{
220-
Params: map[string]string{
221-
"max_statement_time": "0",
222-
},
223-
}
224-
err = adjustMySQLConfig(conf)
225-
test.AssertNotError(t, err, "unexpected err setting server variables")
226-
test.AssertDeepEquals(t, conf.Params, map[string]string{
227-
"sql_mode": "'STRICT_ALL_TABLES'",
228-
})
229-
}

sa/db-next/boulder_sa/20230419000003_OrderToAuthzID.sql

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ CREATE TABLE `orderToAuthz2` (
99
PRIMARY KEY (`id`),
1010
KEY `orderID_idx` (`orderID`),
1111
KEY `authzID_idx` (`authzID`)
12-
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4
13-
PARTITION BY RANGE (`id`)
14-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
12+
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4;
1513

1614
-- +migrate Down
1715
-- SQL section 'Down' is executed when this migration is rolled back
@@ -22,6 +20,4 @@ CREATE TABLE `orderToAuthz2` (
2220
`authzID` bigint(20) NOT NULL,
2321
PRIMARY KEY (`orderID`,`authzID`),
2422
KEY `authzID` (`authzID`)
25-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
26-
PARTITION BY RANGE COLUMNS(orderID, authzID)
27-
(PARTITION p_start VALUES LESS THAN (MAXVALUE, MAXVALUE));
23+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

sa/db/boulder_sa/20230419000000_CombinedSchema.sql

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ CREATE TABLE `authz2` (
1818
KEY `regID_expires_idx` (`registrationID`,`status`,`expires`),
1919
KEY `regID_identifier_status_expires_idx` (`registrationID`,`identifierType`,`identifierValue`,`status`,`expires`),
2020
KEY `expires_idx` (`expires`)
21-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
22-
PARTITION BY RANGE(id)
23-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
21+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2422

2523
CREATE TABLE `blockedKeys` (
2624
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -53,9 +51,7 @@ CREATE TABLE `certificateStatus` (
5351
KEY `serial` (`serial`),
5452
KEY `isExpired_ocspLastUpdated_idx` (`isExpired`,`ocspLastUpdated`),
5553
KEY `notAfter_idx` (`notAfter`)
56-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
57-
PARTITION BY RANGE(id)
58-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
54+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5955

6056
CREATE TABLE `certificates` (
6157
`id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -69,9 +65,7 @@ CREATE TABLE `certificates` (
6965
KEY `serial` (`serial`),
7066
KEY `regId_certificates_idx` (`registrationID`) COMMENT 'Common lookup',
7167
KEY `issued_idx` (`issued`)
72-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
73-
PARTITION BY RANGE(id)
74-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
68+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
7569

7670
CREATE TABLE `certificatesPerName` (
7771
`id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -93,9 +87,7 @@ CREATE TABLE `fqdnSets` (
9387
PRIMARY KEY (`id`),
9488
KEY `serial` (`serial`),
9589
KEY `setHash_issued_idx` (`setHash`,`issued`)
96-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
97-
PARTITION BY RANGE(id)
98-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
90+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
9991

10092
CREATE TABLE `incidents` (
10193
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -104,7 +96,7 @@ CREATE TABLE `incidents` (
10496
`renewBy` datetime NOT NULL,
10597
`enabled` boolean DEFAULT false,
10698
PRIMARY KEY (`id`)
107-
) CHARSET=utf8mb4;
99+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
108100

109101
CREATE TABLE `issuedNames` (
110102
`id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -114,9 +106,7 @@ CREATE TABLE `issuedNames` (
114106
`renewal` tinyint(1) NOT NULL DEFAULT 0,
115107
PRIMARY KEY (`id`),
116108
KEY `reversedName_notBefore_Idx` (`reversedName`,`notBefore`)
117-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
118-
PARTITION BY RANGE(id)
119-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
109+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
120110

121111
CREATE TABLE `keyHashToSerial` (
122112
`id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -147,18 +137,14 @@ CREATE TABLE `orderFqdnSets` (
147137
KEY `setHash_expires_idx` (`setHash`,`expires`),
148138
KEY `orderID_idx` (`orderID`),
149139
KEY `orderFqdnSets_registrationID_registrations` (`registrationID`)
150-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
151-
PARTITION BY RANGE(id)
152-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
140+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
153141

154142
CREATE TABLE `orderToAuthz2` (
155143
`orderID` bigint(20) NOT NULL,
156144
`authzID` bigint(20) NOT NULL,
157145
PRIMARY KEY (`orderID`,`authzID`),
158146
KEY `authzID` (`authzID`)
159-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
160-
PARTITION BY RANGE COLUMNS(orderID, authzID)
161-
(PARTITION p_start VALUES LESS THAN (MAXVALUE, MAXVALUE));
147+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
162148

163149
CREATE TABLE `orders` (
164150
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -171,9 +157,7 @@ CREATE TABLE `orders` (
171157
PRIMARY KEY (`id`),
172158
KEY `reg_status_expires` (`registrationID`,`expires`),
173159
KEY `regID_created_idx` (`registrationID`,`created`)
174-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
175-
PARTITION BY RANGE(id)
176-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
160+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
177161

178162
-- Note: This table's name is a historical artifact and it is now
179163
-- used to store linting certificates, not precertificates.
@@ -189,9 +173,7 @@ CREATE TABLE `precertificates` (
189173
KEY `serial` (`serial`),
190174
KEY `regId_precertificates_idx` (`registrationID`),
191175
KEY `issued_precertificates_idx` (`issued`)
192-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
193-
PARTITION BY RANGE(id)
194-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
176+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
195177

196178
CREATE TABLE `registrations` (
197179
`id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -215,9 +197,7 @@ CREATE TABLE `requestedNames` (
215197
PRIMARY KEY (`id`),
216198
KEY `orderID_idx` (`orderID`),
217199
KEY `reversedName_idx` (`reversedName`)
218-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
219-
PARTITION BY RANGE(id)
220-
(PARTITION p_start VALUES LESS THAN (MAXVALUE));
200+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
221201

222202
-- Tables below have foreign key constraints, so are created after all other tables.
223203

@@ -245,6 +225,7 @@ DROP TABLE `certificateStatus`;
245225
DROP TABLE `certificatesPerName`;
246226
DROP TABLE `certificates`;
247227
DROP TABLE `fqdnSets`;
228+
DROP TABLE `incidents`;
248229
DROP TABLE `issuedNames`;
249230
DROP TABLE `keyHashToSerial`;
250231
DROP TABLE `newOrdersRL`;

0 commit comments

Comments
 (0)