diff --git a/CHANGELOG.md b/CHANGELOG.md index 90aa221fe0..d9bbed47d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ `merkle` libraries in this repository. This is not a breaking change, but we recommend clients also migrate over to this library at the earliest convenient time; the long term plan is to remove `merkle` from this repo. +* `countFromInformationSchema` function to add support for MySQL 8. ## v1.4.0 diff --git a/quota/mysqlqm/mysql_quota.go b/quota/mysqlqm/mysql_quota.go index 1e288b5009..45e4dcc4ca 100644 --- a/quota/mysqlqm/mysql_quota.go +++ b/quota/mysqlqm/mysql_quota.go @@ -19,6 +19,7 @@ import ( "context" "database/sql" "errors" + "fmt" "github.com/google/trillian/quota" ) @@ -97,6 +98,10 @@ func (m *QuotaManager) countUnsequenced(ctx context.Context) (int, error) { } func countFromInformationSchema(ctx context.Context, db *sql.DB) (int, error) { + // turn off statistics caching for MySQL 8 + if err := turnOffInformationSchemaCache(ctx, db); err != nil { + return 0, err + } // information_schema.tables doesn't have an explicit PK, so let's play it safe and ensure // the cursor returns a single row. rows, err := db.QueryContext(ctx, countFromInformationSchemaQuery, "Unsequenced", "BASE TABLE") @@ -124,3 +129,31 @@ func countFromTable(ctx context.Context, db *sql.DB) (int, error) { } return count, nil } + +// turnOffInformationSchemaCache turn off statistics caching for MySQL 8 +// To always retrieve the latest statistics directly from the storage engine and bypass cached values, set information_schema_stats_expiry to 0. +// See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_information_schema_stats_expiry +// MySQL versions prior to 8 will fail safely. +func turnOffInformationSchemaCache(ctx context.Context, db *sql.DB) error { + opt := "information_schema_stats_expiry" + res := db.QueryRowContext(ctx, "SHOW VARIABLES LIKE '"+opt+"'") + var none string + var expiry int + + if err := res.Scan(&none, &expiry); err != nil { + // fail safely for all versions of MySQL prior to 8 + if errors.Is(err, sql.ErrNoRows) { + return nil + } + + return fmt.Errorf("failed to get variable %q: %v", opt, err) + } + + if expiry != 0 { + if _, err := db.ExecContext(ctx, "SET SESSION "+opt+"=0"); err != nil { + return fmt.Errorf("failed to set variable %q: %v", opt, err) + } + } + + return nil +}