Skip to content

Commit 2197eea

Browse files
database: Add vitess + mysql 8.4 to our development environment (#8468)
The original plan for getting the Vitess infrastructure running was to use [vttestserver](https://vitess.io/docs/22.0/reference/programs/vttestserver) as a starting point to reach a minimum viable setup. However, vttestserver didn’t work out because some of its defaults conflicted with how we clean up rows and the level of resources (threads) we need. Fortunately, vttestserver is just a wrapper around [vtcombo](https://vitess.io/docs/21.0/reference/programs/vtcombo) that generates a [vttest protobuf](https://github.com/vitessio/vitess/blob/v22.0.1/proto/vttest.proto) describing the configuration for an in-memory topology server started by vtcombo, encoded in JSON. By modifying vttestserver’s [run.sh](https://github.com/vitessio/vitess/blob/v22.0.1/docker/vttestserver/run.sh), we're able to interact with vtcombo directly, passing the JSON configuration along with other vttestserver defaults reverse-engineered from run.sh and [vtprocess.go](https://github.com/vitessio/vitess/blob/v22.0.1/go/vt/vttest/vtprocess.go). Vitess doesn’t provide a `vtcombo` image, we must build our own. Build and upload a [boulder-vtcomboserver image](https://hub.docker.com/repository/docker/letsencrypt/boulder-vtcomboserver) on top of Docker's official MySQL 8.4 image, which provides native arm64 support. The accompanying tag-and-upload shell script defaults to amd64 for CI. As an aside, Vitess’s official Dockerfiles are only published for amd64, and modifying them to build for arm64 would prove difficult because Oracle doesn’t publish MySQL arm64 binaries in its [Debian apt repository](https://repo.mysql.com/apt/debian/pool/mysql-8.0/m/mysql-community). With boulder-vtcomboserver up and running I was able to find/validate the following issues and provide workarounds: - **Problem:** db-migrate, the tool we use to apply database migrations, must be configured to talk to MariaDB and to MySQL through Vitess (vtgate + vttablet). **Solution:** Create two new dbconfig YAML files (mariadb and vitess) and use `test/entrypoint.sh` to set the appropriate file for `sql-migrate` (`test/create_db.sh`) to use. Also, symlink each of these two new files from db to db-next just like the old dbconfig.yml file. - **Problem:** Vitess does not allow database `CREATE` statements and any DDL containing them will be rejected by vtgate. **Solution:** These databases are already created by vtcombo since they’re defined as KEYSPACES. Skip database creation in `test/create_db.sh`. - **Problem:** Vitess does not allow user creation or grants (`CREATE USER`, `GRANT`), and any DDL containing these commands will be blocked by vtgate. **Solution:** Skip user creation and grant steps in `test/create_db.sh`. Set `%` for `--vschema_ddl_authorized_users` as vttestserver does, and revisit this later for a more complete approach. - **Problem:** vttablet default for maximum number of rows returned from a (non-streaming) query (10,000) is too low for Boulder’s needs, causing queries to fail due to vttablet rejecting them. **Solution:** Increase `--queryserver-config-max-result-size` to 1,000,000 and `--queryserver-config-warn-result-size` to 1,000,000. - **Problem:** vttablet default for connection pool size (16) and maximum number of concurrent transactions (20) are too low for Boulder’s needs, causing queries to fail due to vttablet being overloaded. **Solution:** Increase `--queryserver-config-pool-size` to 64 and `--queryserver-config-transaction-cap` to 80. - **Problem:** Vitess does not allow `TRIGGER` statements and any DDL containing them will be rejected by vtgate. Without TRIGGER statements TestIssuanceCertStorageFailed, an integration test, will fail. **Soluton:** Run these TRIGGER statements in an entrypoint scripttest/vtcomboserver/install_trigger.sh, bypassing vtgate entirely. Depends on #8479 Depends on #8489 Depends on #8490 Depends on #8494 Fixes #7736
1 parent 8b5aa62 commit 2197eea

29 files changed

+489
-52
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: vitessv23.0.0_2025-12-02
6370

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

.gitignore

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

4444
# Coverage files
4545
test/coverage
46+
47+
# DSN symlinks
48+
test/secrets/badkeyrevoker_dburl
49+
test/secrets/cert_checker_dburl
50+
test/secrets/incidents_dburl
51+
test/secrets/revoker_dburl
52+
test/secrets/sa_dburl
53+
test/secrets/sa_ro_dburl

docker-compose.yml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ services:
5050
- 4001:4001 # ACMEv2
5151
- 4003:4003 # SFE
5252
depends_on:
53-
- bmysql
53+
- bmariadb
5454
- bproxysql
55+
- bvitess
5556
- bredis_1
5657
- bredis_2
5758
- bconsul
@@ -74,12 +75,12 @@ services:
7475
# with a "docker compose up bsetup".
7576
- setup
7677

77-
bmysql:
78+
bmariadb:
7879
image: mariadb:10.11.13
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
@@ -101,7 +102,7 @@ services:
101102
volumes:
102103
- ./test/:/test/:cached
103104
depends_on:
104-
- bmysql
105+
- bmariadb
105106
networks:
106107
bouldernet:
107108
aliases:
@@ -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/db-next/dbconfig.mysql8.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../db/dbconfig.mysql8.yml

sa/db-next/dbconfig.yml

Lines changed: 0 additions & 1 deletion
This file was deleted.

sa/db/dbconfig.mysql8.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# https://github.com/rubenv/sql-migrate#readme
2+
boulder_sa_test:
3+
dialect: mysql
4+
datasource: root@tcp(boulder-vitess:33577)/boulder_sa_test?parseTime=true
5+
dir: boulder_sa
6+
7+
boulder_sa_integration:
8+
dialect: mysql
9+
datasource: root@tcp(boulder-vitess:33577)/boulder_sa_integration?parseTime=true
10+
dir: boulder_sa
11+
12+
incidents_sa_test:
13+
dialect: mysql
14+
datasource: root@tcp(boulder-vitess:33577)/incidents_sa_test?parseTime=true
15+
dir: incidents_sa
16+
17+
incidents_sa_integration:
18+
dialect: mysql
19+
datasource: root@tcp(boulder-vitess:33577)/incidents_sa_integration?parseTime=true
20+
dir: incidents_sa

test.sh

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fi
1212
# Defaults
1313
#
1414
export RACE="false"
15-
export DB_ADDR="boulder-proxysql:6033"
15+
export USE_VITESS="false"
1616
STAGE="starting"
1717
STATUS="FAILURE"
1818
RUN=()
@@ -22,6 +22,14 @@ INTEGRATION_FLAGS=()
2222
FILTER=()
2323
COVERAGE="false"
2424
COVERAGE_DIR="test/coverage/$(date +%Y-%m-%d_%H-%M-%S)"
25+
DB_URL_FILES=(
26+
badkeyrevoker_dburl
27+
cert_checker_dburl
28+
incidents_dburl
29+
revoker_dburl
30+
sa_dburl
31+
sa_ro_dburl
32+
)
2533

2634
#
2735
# Cleanup Functions
@@ -79,6 +87,23 @@ function run_and_expect_silence() {
7987
rm "${result_file}"
8088
}
8189

90+
configure_database_endpoints() {
91+
dburl_target_dir="proxysql"
92+
export DB_ADDR="boulder-proxysql:6033"
93+
94+
if [[ "${USE_VITESS}" == "true" ]]
95+
then
96+
dburl_target_dir="vitess"
97+
export DB_ADDR="boulder-vitess:33577"
98+
fi
99+
100+
# Configure DBURL symlinks
101+
rm -f test/secrets/*_dburl || true
102+
for file in ${DB_URL_FILES:+${DB_URL_FILES[@]+"${DB_URL_FILES[@]}"}}
103+
do
104+
ln -sf "dburls/${dburl_target_dir}/${file}" "test/secrets/${file}"
105+
done
106+
}
82107
#
83108
# Testing Helpers
84109
#
@@ -122,11 +147,12 @@ With no options passed, runs standard battery of tests (lint, unit, and integrat
122147
Example:
123148
TestGenerateValidity/TestWFECORS
124149
-h, --help Shows this help message
150+
-b --use-vitess Run tests against Vitess + MySQL 8.0 database
125151
126152
EOM
127153
)"
128154

129-
while getopts luvwecisgnhd:p:f:-: OPT; do
155+
while getopts luvwecisgnhbd:p:f:-: OPT; do
130156
if [ "$OPT" = - ]; then # long option: reformulate OPT and OPTARG
131157
OPT="${OPTARG%%=*}" # extract long option name
132158
OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty)
@@ -146,13 +172,17 @@ while getopts luvwecisgnhd:p:f:-: OPT; do
146172
n | config-next ) BOULDER_CONFIG_DIR="test/config-next" ;;
147173
c | coverage ) COVERAGE="true" ;;
148174
d | coverage-dir ) check_arg; COVERAGE_DIR="${OPTARG}" ;;
175+
b | use-vitess ) USE_VITESS="true" ;;
149176
h | help ) print_usage_exit ;;
150177
??* ) exit_msg "Illegal option --$OPT" ;; # bad long option
151178
? ) exit 2 ;; # bad short option (error reported via getopts)
152179
esac
153180
done
154181
shift $((OPTIND-1)) # remove parsed options and args from $@ list
155182

183+
# Defaults to MariaDB unless USE_VITESS is true.
184+
configure_database_endpoints
185+
156186
# The list of segments to run. Order doesn't matter.
157187
if [ -z "${RUN[@]+x}" ]
158188
then
@@ -207,6 +237,7 @@ settings="$(cat -- <<-EOM
207237
FILTER: ${FILTER[@]}
208238
COVERAGE: $COVERAGE
209239
COVERAGE_DIR: $COVERAGE_DIR
240+
USE_VITESS: $USE_VITESS
210241
EOM
211242
)"
212243

test/create_db.sh

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,41 @@ function create_empty_db() {
4444
dbconn="-u root"
4545
if [[ $MYSQL_CONTAINER ]]
4646
then
47-
dbconn="-u root -h boulder-mysql --port 3306"
47+
dbconn="-u root -h ${DB_HOST} --port ${DB_PORT}"
4848
fi
4949

50-
# MariaDB sets the default binlog_format to STATEMENT,
51-
# which causes warnings that fail tests. Instead set it
52-
# to the format we use in production, MIXED.
53-
mysql ${dbconn} -e "SET GLOBAL binlog_format = 'MIXED';"
50+
if ! mysql ${dbconn} -e "select 1" >/dev/null 2>&1; then
51+
exit_err "unable to connect to ${DB_HOST}:${DB_PORT}"
52+
fi
5453

55-
# MariaDB sets the default @@max_connections value to 100. The SA alone is
56-
# configured to use up to 100 connections. We increase the max connections here
57-
# to give headroom for other components.
58-
mysql ${dbconn} -e "SET GLOBAL max_connections = 500;"
54+
if [[ ${SKIP_CREATE} -eq 0 ]]
55+
then
56+
# MariaDB sets the default binlog_format to STATEMENT,
57+
# which causes warnings that fail tests. Instead set it
58+
# to the format we use in production, MIXED.
59+
mysql ${dbconn} -e "SET GLOBAL binlog_format = 'MIXED';"
60+
61+
# MariaDB sets the default @@max_connections value to 100. The SA alone is
62+
# configured to use up to 100 connections. We increase the max connections here
63+
# to give headroom for other components.
64+
mysql ${dbconn} -e "SET GLOBAL max_connections = 500;"
65+
fi
5966

6067
for db in $DBS; do
6168
for env in $ENVS; do
6269
dbname="${db}_${env}"
6370
print_heading "${dbname}"
64-
if mysql ${dbconn} -e 'show databases;' | grep "${dbname}" > /dev/null; then
65-
echo "Already exists - skipping create"
71+
if [[ ${SKIP_CREATE} -eq 0 ]]
72+
then
73+
if mysql ${dbconn} -e 'show databases;' | grep -q "${dbname}"
74+
then
75+
echo "Already exists - skipping create"
76+
else
77+
echo "Doesn't exist - creating"
78+
create_empty_db "${dbname}" "${dbconn}"
79+
fi
6680
else
67-
echo "Doesn't exist - creating"
68-
create_empty_db "${dbname}" "${dbconn}"
81+
echo "Skipping database create for ${dbname}"
6982
fi
7083

7184
if [[ "${BOULDER_CONFIG_DIR}" == "test/config-next" ]]
@@ -78,27 +91,32 @@ for db in $DBS; do
7891
# sql-migrate will default to ./dbconfig.yml and treat all configured dirs
7992
# as relative.
8093
cd "${dbpath}"
81-
r=`sql-migrate up -env="${dbname}" | xargs -0 echo`
94+
r=`sql-migrate up -config="${DB_CONFIG_FILE}" -env="${dbname}" | xargs -0 echo`
8295
if [[ "${r}" == "Migration failed"* ]]
8396
then
8497
echo "Migration failed - dropping and recreating"
8598
create_empty_db "${dbname}" "${dbconn}"
86-
sql-migrate up -env="${dbname}" || exit_err "Migration failed after dropping and recreating"
99+
sql-migrate up -config="${DB_CONFIG_FILE}" -env="${dbname}" || exit_err "Migration failed after dropping and recreating"
87100
else
88101
echo "${r}"
89102
fi
90103

91104
USERS_SQL="../db-users/${db}.sql"
92-
if [[ ${MYSQL_CONTAINER} ]]
105+
if [[ ${SKIP_USERS} -eq 1 ]]
93106
then
94-
sed -e "s/'localhost'/'%'/g" < ${USERS_SQL} | \
95-
mysql ${dbconn} -D "${dbname}" -f || exit_err "Unable to add users from ${USERS_SQL}"
107+
echo "Skipping user grants for ${dbname}"
96108
else
97-
sed -e "s/'localhost'/'127.%'/g" < $USERS_SQL | \
98-
mysql ${dbconn} -D "${dbname}" -f < $USERS_SQL || exit_err "Unable to add users from ${USERS_SQL}"
109+
if [[ $MYSQL_CONTAINER ]]
110+
then
111+
sed -e "s/'localhost'/'%'/g" < "${USERS_SQL}" | \
112+
mysql ${dbconn} -D "${dbname}" -f || exit_err "Unable to add users from ${USERS_SQL}"
113+
else
114+
sed -e "s/'localhost'/'127.%'/g" < "${USERS_SQL}" | \
115+
mysql ${dbconn} -D "${dbname}" -f || exit_err "Unable to add users from ${USERS_SQL}"
116+
fi
117+
echo "Added users from ${USERS_SQL}"
99118
fi
100-
echo "Added users from ${USERS_SQL}"
101-
119+
102120
# return to the root directory
103121
cd "${root_dir}"
104122
done

test/entrypoint.sh

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,32 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
1010
rm -f /var/run/rsyslogd.pid
1111
rsyslogd
1212

13-
# make sure we can reach the mysqldb.
14-
./test/wait-for-it.sh boulder-mysql 3306
13+
# make sure we can reach mariadb and proxysql
14+
./test/wait-for-it.sh boulder-mariadb 3306
15+
./test/wait-for-it.sh boulder-proxysql 6033
1516

16-
# make sure we can reach the proxysql.
17-
./test/wait-for-it.sh bproxysql 6032
17+
# make sure we can reach vitess
18+
./test/wait-for-it.sh boulder-vitess 33577
1819

1920
# make sure we can reach pkilint
2021
./test/wait-for-it.sh bpkimetal 8080
2122

22-
# create the database
23-
MYSQL_CONTAINER=1 $DIR/create_db.sh
23+
# create the databases
24+
MYSQL_CONTAINER=1 \
25+
DB_HOST="boulder-mariadb" \
26+
DB_PORT=3306 \
27+
DB_CONFIG_FILE="${DIR}/../sa/db/dbconfig.mariadb.yml" \
28+
SKIP_CREATE=0 \
29+
SKIP_USERS=0 \
30+
"$DIR/create_db.sh"
31+
32+
MYSQL_CONTAINER=1 \
33+
DB_HOST="boulder-vitess" \
34+
DB_PORT=33577 \
35+
DB_CONFIG_FILE="${DIR}/../sa/db/dbconfig.mysql8.yml" \
36+
SKIP_CREATE=1 \
37+
SKIP_USERS=1 \
38+
"$DIR/create_db.sh"
2439

2540
if [[ $# -eq 0 ]]; then
2641
exec python3 ./start.py

0 commit comments

Comments
 (0)