Skip to content

Commit ebaeed0

Browse files
beautifulentropynatalia.astashenko
authored andcommitted
sa: Stop injecting max_statement_time and long_query_time into DSNs (letsencrypt#8490)
1 parent 6cbad3a commit ebaeed0

File tree

11 files changed

+49
-115
lines changed

11 files changed

+49
-115
lines changed

sa/database.go

Lines changed: 4 additions & 26 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,25 +209,10 @@ 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")
212+
// Omit max_statement_time and max_execution_time from the DSN. Query
213+
// timeouts are managed exclusively by ProxySQL and/or Vitess.
214+
delete(conf.Params, "max_statement_time")
215+
delete(conf.Params, "max_execution_time")
238216

239217
// Finally, perform validation over all variables set by the DSN and via Boulder.
240218
for k, v := range conf.Params {

sa/database_test.go

Lines changed: 10 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ func TestInvalidDSN(t *testing.T) {
2121
_, err := DBMapForTest("invalid")
2222
test.AssertError(t, err, "DB connect string missing the slash separating the database name")
2323

24-
DSN := "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&stringVarThatDoesntExist=%27whoopsidaisies"
24+
DSN := "policy:password@tcp(foo-database:1337)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&stringVarThatDoesntExist=%27whoopsidaisies"
2525
_, err = DBMapForTest(DSN)
2626
test.AssertError(t, err, "Variable does not exist in curated system var list, but didn't return an error and should have")
2727

28-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=2"
28+
DSN = "policy:password@tcp(foo-database:1337)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=2"
2929
_, err = DBMapForTest(DSN)
3030
test.AssertError(t, err, "Variable is unable to be set in the SESSION scope, but was declared")
3131

32-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&optimizer_switch=incorrect-quoted-string"
32+
DSN = "policy:password@tcp(foo-database:1337)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&optimizer_switch=incorrect-quoted-string"
3333
_, err = DBMapForTest(DSN)
3434
test.AssertError(t, err, "Variable declared with incorrect quoting")
3535

36-
DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=%272%27"
36+
DSN = "policy:password@tcp(foo-database:1337)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=%272%27"
3737
_, err = DBMapForTest(DSN)
3838
test.AssertError(t, err, "Integer enum declared, but should not have been quoted")
3939
}
@@ -75,9 +75,8 @@ func TestDbSettings(t *testing.T) {
7575
oldSetConnMaxIdleTime(db, connMaxIdleTime)
7676
}
7777
dsnFile := path.Join(t.TempDir(), "dbconnect")
78-
err := os.WriteFile(dsnFile,
79-
[]byte("sa@tcp(boulder-proxysql:6033)/boulder_sa_integration"),
80-
os.ModeAppend)
78+
79+
err := os.WriteFile(dsnFile, []byte(vars.DBConnSA), os.ModeAppend)
8180
test.AssertNotError(t, err, "writing dbconnect file")
8281

8382
config := cmd.DBConfig{
@@ -108,7 +107,7 @@ func TestDbSettings(t *testing.T) {
108107
// TODO: Change this to test `newDbMapFromMySQLConfig` instead?
109108
func TestNewDbMap(t *testing.T) {
110109
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"
110+
const expected = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?clientFoundRows=true&parseTime=true&readTimeout=800ms&writeTimeout=800ms&sql_mode=%27STRICT_ALL_TABLES%27"
112111
oldSQLOpen := sqlOpen
113112
defer func() {
114113
sqlOpen = oldSQLOpen
@@ -146,27 +145,6 @@ func TestStrictness(t *testing.T) {
146145
}
147146
}
148147

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-
170148
// TestAutoIncrementSchema tests that all of the tables in the boulder_*
171149
// databases that have auto_increment columns use BIGINT for the data type. Our
172150
// data is too big for INT.
@@ -190,40 +168,7 @@ func TestAdjustMySQLConfig(t *testing.T) {
190168
conf := &mysql.Config{}
191169
err := adjustMySQLConfig(conf)
192170
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-
})
171+
test.Assert(t, conf.ParseTime, "ParseTime should be enabled")
172+
test.Assert(t, conf.ClientFoundRows, "ClientFoundRows should be enabled")
173+
test.AssertDeepEquals(t, conf.Params, map[string]string{"sql_mode": "'STRICT_ALL_TABLES'"})
229174
}

test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fi
1212
# Defaults
1313
#
1414
export RACE="false"
15+
export DB_ADDR="boulder-proxysql:6033"
1516
STAGE="starting"
1617
STATUS="FAILURE"
1718
RUN=()

test/db.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import (
66
"fmt"
77
"io"
88
"testing"
9-
)
109

11-
var (
12-
_ CleanUpDB = &sql.DB{}
10+
"github.com/letsencrypt/boulder/test/vars"
1311
)
1412

13+
var _ CleanUpDB = &sql.DB{}
14+
1515
// CleanUpDB is an interface with only what is needed to delete all
1616
// rows in all tables in a database plus close the database
1717
// connection. It is satisfied by *sql.DB.
@@ -27,7 +27,7 @@ type CleanUpDB interface {
2727
// table as this is used by sql-migrate (https://github.com/rubenv/sql-migrate)
2828
// to track migrations. If it encounters an error it fails the tests.
2929
func ResetBoulderTestDatabase(t testing.TB) func() {
30-
return resetTestDatabase(t, context.Background(), "boulder")
30+
return resetTestDatabase(t, context.Background(), vars.DBConnSAFullPerms)
3131
}
3232

3333
// ResetIncidentsTestDatabase returns a cleanup function which deletes all rows
@@ -36,11 +36,11 @@ func ResetBoulderTestDatabase(t testing.TB) func() {
3636
// (https://github.com/rubenv/sql-migrate) to track migrations. If it encounters
3737
// an error it fails the tests.
3838
func ResetIncidentsTestDatabase(t testing.TB) func() {
39-
return resetTestDatabase(t, context.Background(), "incidents")
39+
return resetTestDatabase(t, context.Background(), vars.DBConnIncidentsFullPerms)
4040
}
4141

42-
func resetTestDatabase(t testing.TB, ctx context.Context, dbPrefix string) func() {
43-
db, err := sql.Open("mysql", fmt.Sprintf("test_setup@tcp(boulder-proxysql:6033)/%s_sa_test", dbPrefix))
42+
func resetTestDatabase(t testing.TB, ctx context.Context, dsn string) func() {
43+
db, err := sql.Open("mysql", dsn)
4444
if err != nil {
4545
t.Fatalf("Couldn't create db: %s", err)
4646
}

test/secrets/backfiller_dburl

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

test/secrets/expiration_mailer_dburl

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

test/secrets/mailer_dburl

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

test/secrets/ocsp_responder_dburl

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

test/secrets/ocsp_responder_redis_password

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

test/secrets/rocsp_tool_password

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

0 commit comments

Comments
 (0)