This is an automated email from the ASF dual-hosted git repository.
mitchell852 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 1183254 `db/admin migrate`: Migrate first migration explicitly if DB
version's migration is squashed (#6096)
1183254 is described below
commit 1183254381164bcd6534db7c266ca8cd65bf4b6d
Author: Zach Hoffman <[email protected]>
AuthorDate: Mon Aug 16 11:23:24 2021 -0600
`db/admin migrate`: Migrate first migration explicitly if DB version's
migration is squashed (#6096)
* Only fetch the migration version once
* If the migration version is the version of the last squashed migration,
temporarily set the migration version to 0
* Remove duplicate word
* Move Apache License 2.0 to the top
* migration version -> migration timestamp
* Migrate the first expected migration instead of temporarily forcing the
version to 0
* Add a script to squash migrations that existed before the most recent ATC
release
* Make squashing script use Migrate-style migrations
---
docs/source/development/traffic_ops.rst | 2 +-
traffic_ops/app/db/SQUASH.md | 51 ++++++++++++++++++++++++++++
traffic_ops/app/db/admin.go | 39 +++++++++++----------
traffic_ops/app/db/squash_migrations.sh | 60 +++++++++++++++++++++++++++++++++
traffic_ops/build/traffic_ops.spec | 2 ++
5 files changed, 136 insertions(+), 18 deletions(-)
diff --git a/docs/source/development/traffic_ops.rst
b/docs/source/development/traffic_ops.rst
index a276f8c..51174f8 100644
--- a/docs/source/development/traffic_ops.rst
+++ b/docs/source/development/traffic_ops.rst
@@ -259,7 +259,7 @@ If you encounter an error running a migration, you will see
a message like
[root@trafficops app]# db/admin -env production migrate
Error running migrate up: migration failed: syntax error at or near
"This_is_a_syntax_error" (column 1) in line 18: /*
-That means that the migration version in the ``version`` column of the
``schema_migrations`` table has been updated to the version of the migration
that failed, but the ``dirty`` column is also set, and if you try to run
another migration (either up or down), you will see
+That means that the migration timestamp in the ``version`` column of the
``schema_migrations`` table has been updated to the version of the migration
that failed, but the ``dirty`` column is also set, and if you try to run
another migration (either up or down), you will see
.. code-block:: bash
:caption: db/admin error migrating when the database version is dirty
diff --git a/traffic_ops/app/db/SQUASH.md b/traffic_ops/app/db/SQUASH.md
new file mode 100644
index 0000000..b1801f1
--- /dev/null
+++ b/traffic_ops/app/db/SQUASH.md
@@ -0,0 +1,51 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Squashing database migrations
+
+For convenience,
[`squash_migrations.sh`](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/app/db/squash_migrations.sh)
script squashes the migrations, but whoever PRs the result is responsible for
verifying that the migrations are squashed and `LastSquashedMigrationVersion`
in
[`db/admin.go`](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/app/db/admin.go)
is updated correctly, regardless of the result of having run the script.
+
+--------
+
+Each major release of Apache Traffic Control combines database migrations from
previous ATC releases into
[`create_tables.sql`](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/app/db/create_tables.sql).
+
+For example, suppose the latest version of Apache Traffic Control is 147.5.8
and contains these migrations:
+* `1_my-migration.up.sql`
+* `1_my-migration.down.sql`
+* `3_another-migration.up.sql`
+* `3_another-migration.down.sql`
+
+And suppose the ATC
[`master`](https://github.com/apache/trafficcontrol/commits/master) branch
contains these migrations:
+* `1_my-migration.up.sql`
+* `1_my-migration.down.sql`
+* `3_another-migration.up.sql`
+* `3_another-migration.down.sql`
+* `4_migration-name.up.sql`
+* `4_migration-name.down.sql`
+* `9_add-column-to-table.up.sql`
+* `9_add-column-to-table.down.sql`
+
+1. In order to prepare database migrations for the next major release, in this
case, ATC 148.0.0, migrations `1` and `3` should be collapsed into
`create_tables.sql` and migrations `4` and `9` should remain in
[`traffic_ops/app/db/migrations/`](https://github.com/apache/trafficcontrol/tree/master/traffic_ops/app/db/migrations/).
+
+2. * Note that `3` is the migration timestamp of the last up/down migration
set. Find the definition for `LastSquashedMigrationTimestamp` in
[`traffic_ops/app/db/admin.go`](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/app/db/admin.go)
and change it to `3`.
+ * After migrations from ATC 147.5.8 have been collapsed, the first
migration version will be `4`. Find the definition for
`FirstMigrationTimestamp` in
[`traffic_ops/app/db/admin.go`](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/app/db/admin.go)
and change it to `4`.
+
+Past PRs that have collapsed the DB migrations:
+- https://github.com/apache/trafficcontrol/pull/6065
+- https://github.com/apache/trafficcontrol/pull/3524
diff --git a/traffic_ops/app/db/admin.go b/traffic_ops/app/db/admin.go
index df53b38..ce2a883 100644
--- a/traffic_ops/app/db/admin.go
+++ b/traffic_ops/app/db/admin.go
@@ -94,8 +94,8 @@ const (
CmdUp = "up"
CmdDown = "down"
CmdRedo = "redo"
- // Deprecated: Migrate only tracks migration version and dirty status,
not a status for each migration.
- // Use CmdDBVersion to check the migration version and dirty status.
+ // Deprecated: Migrate only tracks migration timestamp and dirty
status, not a status for each migration.
+ // Use CmdDBVersion to check the migration timestamp and dirty status.
CmdStatus = "status"
CmdDBVersion = "dbversion"
CmdSeed = "seed"
@@ -116,12 +116,17 @@ const (
TrafficVaultMigrationsSource = "file:" + TrafficVaultDir + "migrations"
TrafficVaultDir = dbDir + "trafficvault/"
TrafficVaultSchemaPath = TrafficVaultDir + "create_tables.sql"
+
+ LastSquashedMigrationTimestamp uint = 2021012200000000 //
2021012200000000_max_request_header_bytes_default_zero.sql
+ FirstMigrationTimestamp uint = 2021012700000000 //
2021012700000000_update_interfaces_multiple_routers.up.sql
)
var (
// globals that are passed in via CLI flags and used in commands
- Environment string
- TrafficVault bool
+ Environment string
+ TrafficVault bool
+ DBVersion uint
+ DBVersionDirty bool
// globals that are parsed out of DBConfigFile and used in commands
DBDriver string
@@ -314,7 +319,8 @@ func upgrade() {
}
func maybeMigrateFromGoose() bool {
- _, _, versionErr := Migrate.Version()
+ var versionErr error
+ DBVersion, DBVersionDirty, versionErr = Migrate.Version()
if versionErr == nil {
return false
}
@@ -329,15 +335,18 @@ func maybeMigrateFromGoose() bool {
func runMigrations() {
migratedFromGoose := initMigrate()
- upErr := Migrate.Up()
- if upErr == migrate.ErrNoChange {
+ if !TrafficVault && DBVersion == LastSquashedMigrationTimestamp &&
!DBVersionDirty {
+ if migrateErr := Migrate.Migrate(FirstMigrationTimestamp);
migrateErr != nil {
+ die(fmt.Sprintf("Error migrating from DB version %d to
%d: %s", LastSquashedMigrationTimestamp, FirstMigrationTimestamp,
migrateErr.Error()))
+ }
+ }
+ if upErr := Migrate.Up(); upErr == migrate.ErrNoChange {
if !migratedFromGoose {
println(upErr.Error())
}
} else if upErr != nil {
die("Error running migrate up: " + upErr.Error())
}
-
}
func runUp() {
@@ -361,19 +370,15 @@ func redo() {
}
}
-// Deprecated: Migrate does not track migration status of past migrations. Use
dbversion() to check the migration version and dirty status.
+// Deprecated: Migrate does not track migration status of past migrations. Use
dbversion() to check the migration timestamp and dirty status.
func status() {
dbVersion()
}
func dbVersion() {
initMigrate()
- version, dirty, err := Migrate.Version()
- if err != nil {
- die("Error running migrate version: " + err.Error())
- }
- fmt.Printf("dbversion %d", version)
- if dirty {
+ fmt.Printf("dbversion %d", DBVersion)
+ if DBVersionDirty {
fmt.Printf(" (dirty)")
}
println()
@@ -496,7 +501,7 @@ create_migration NAME
- Creates a pair of timestamped up/down migrations titled NAME.
create_user - Execute 'create_user' the user for the current environment
(traffic_ops).
-dbversion - Prints the current migration version
+dbversion - Prints the current migration timestamp
drop_user - Execute 'drop_user' the user for the current environment
(traffic_ops).
patch - Execute sql from db/patches.sql for loading post-migration data
@@ -507,7 +512,7 @@ reset - Execute db 'dropdb', 'createdb', load_schema,
migrate on the
seed - Execute sql from db/seeds.sql for loading static data (NOTE: not
supported with --trafficvault option).
show_users - Execute sql to show all of the user for the current environment.
-status - Prints the current migration version (Deprecated, status is now
an
+status - Prints the current migration timestamp (Deprecated, status is
now an
alias for dbversion and will be removed in a future Traffic
Control release).
upgrade - Execute migrate, seed, and patches on the database for the
current
diff --git a/traffic_ops/app/db/squash_migrations.sh
b/traffic_ops/app/db/squash_migrations.sh
new file mode 100755
index 0000000..2fbc274
--- /dev/null
+++ b/traffic_ops/app/db/squash_migrations.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+trap 'echo "Error on line ${LINENO} of ${0}" >/dev/stderr; exit 1' ERR
+set -o errexit -o nounset -o pipefail
+cd "$(dirname "$0")"
+
+apache_remote="$(git remote -v | grep 'apache/trafficcontrol.*(fetch)$' | cut
-f1 | head -n1)"
+git fetch --tags --force "$apache_remote"
+last_release="$(git tag --list --sort=v:refname | grep -E
'^RELEASE-[0-9]+[.][0-9]+[.][0-9]+$' | tail -n1)"
+migrations_to_squash="$(git ls-tree -r "$last_release" -- migrations | grep -o
'migrations/[0-9].*')"
+
+cp create_tables.sql to_squash.sql
+echo "$migrations_to_squash" | grep '\.up\.sql$' | xargs cat >>to_squash.sql
+last_squashed_migration="$(<<<"$migrations_to_squash" tail -n1)"
+last_squashed_migration_timestamp="$(<<<"$last_squashed_migration" sed -E
's|migrations/([0-9]+).*|\1|')"
+first_migration="$(ls migrations/*.sql | grep -A1
"/${last_squashed_migration_timestamp}_" | tail -n1)"
+first_migration_timestamp="$(<<<"$first_migration" sed -E
's|migrations/([0-9]+).*|\1|')"
+sed -i '/^--/,$d' create_tables.sql # keeps the Apache License 2.0 header
+sed -Ei "s|(LastSquashedMigrationTimestamp\s+uint\s+=
).*|\1${last_squashed_migration_timestamp} // ${last_squashed_migration}|"
admin.go
+sed -Ei "s|(FirstMigrationTimestamp\s+uint\s+=
).*|\1${first_migration_timestamp} // ${first_migration}|" admin.go
+
+dump_db_with_migrations() {
+ trap 'echo "Error on line ${LINENO} of dump_db_with_migrations"
>/dev/stderr; exit 1' ERR
+ set -o errexit -o nounset
+ {
+ docker-entrypoint.sh postgres &
+ sleep 10
+ psql -f to_squash.sql
+ } >/dev/stderr
+ pg_dump
+}
+docker run --rm -iw/db \
+ -v "$(pwd):/db" \
+ -e PGUSER=traffic_ops \
+ -e PGPASSWORD=twelve \
+ -e POSTGRES_USER=traffic_ops \
+ -e POSTGRES_PASSWORD=twelve \
+ postgres:13-alpine bash -c "$(type dump_db_with_migrations | tail -n+2);
dump_db_with_migrations" >>create_tables.sql
+rm to_squash.sql
+
+git add create_tables.sql
+git commit -m "Redump create_tables.sql with migrations through timestamp
${last_squashed_migration_timestamp}"
+
+echo "$migrations_to_squash" | xargs git rm
+git commit -m "Remove migrations that existed at ${last_release}"
+
+git add -p admin.go
+git commit -m 'Update LastSquashedMigrationTimestamp and
FirstMigrationTimestamp'
+
+echo 'Migrations squashed successfully!'
diff --git a/traffic_ops/build/traffic_ops.spec
b/traffic_ops/build/traffic_ops.spec
index 2f75a25..fc427ba 100644
--- a/traffic_ops/build/traffic_ops.spec
+++ b/traffic_ops/build/traffic_ops.spec
@@ -236,6 +236,8 @@ fi
%config(noreplace) %attr(750,%{TRAFFIC_OPS_USER},%{TRAFFIC_OPS_GROUP})
/opt/traffic_ops/app/db/dbconf.yml
%config(noreplace)/var/www/files/osversions.json
%attr(755, %{TRAFFIC_OPS_USER},%{TRAFFIC_OPS_GROUP}) %{PACKAGEDIR}/app/db/admin
+%exclude %{PACKAGEDIR}/app/db/SQUASH.md
+%exclude %{PACKAGEDIR}/app/db/squash_migrations.sh
%attr(755, %{TRAFFIC_OPS_USER},%{TRAFFIC_OPS_GROUP})
%{PACKAGEDIR}/install/bin/convert_profile/convert_profile
%attr(755, %{TRAFFIC_OPS_USER},%{TRAFFIC_OPS_GROUP})
%{PACKAGEDIR}/app/db/reencrypt/reencrypt
%attr(755, %{TRAFFIC_OPS_USER},%{TRAFFIC_OPS_GROUP})
%{PACKAGEDIR}/app/db/traffic_vault_migrate/traffic_vault_migrate