Ottomata has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/345646 )
Change subject: Improvements to eventlogging_sync.sh script
......................................................................
Improvements to eventlogging_sync.sh script
- Default to replicating by id if the table has an auto increment id field,
otherwise timestamp will be used.
- Sane CLI options
- -D <cutoff-days> option to not even compare tables where there is
no recent data in master
- -n options for dry run - allows for testing of script without actually
modifying anything
- -d for master database name
- -s for slave database name
TODO: bring back purging? Not sure.
Bug: T124307
Change-Id: Ie96e872ebb763ce8398adcedc52a93ff9f16702a
---
M modules/role/files/mariadb/eventlogging_sync.sh
M modules/role/manifests/mariadb.pp
R modules/role/templates/mariadb/eventlogging_sync.init.erb
3 files changed, 157 insertions(+), 48 deletions(-)
Approvals:
Ottomata: Verified; Looks good to me, approved
diff --git a/modules/role/files/mariadb/eventlogging_sync.sh
b/modules/role/files/mariadb/eventlogging_sync.sh
index 339e026..6dda100 100755
--- a/modules/role/files/mariadb/eventlogging_sync.sh
+++ b/modules/role/files/mariadb/eventlogging_sync.sh
@@ -5,71 +5,168 @@
# does not currently take advantage of bulk insert batching which often leads
to
# replication lag. Adding DELETE into the mix makes it extra painful.
#
-# This script sychronizes a slave using bulk inserts with mysqldump where id >
N, and
-# purges old data with throttled bulk deletes where id < N order by id limit M.
-# Useful for creating a new slave, syncing a broken one, or replacing
replication
+# This script sychronizes a slave using bulk inserts with mysqldump where id
(or timestamp, if no
+# id field exists) > N, and purges old data with throttled bulk deletes where
id < N order by id
+# limit M. Useful for creating a new slave, syncing a broken one, or
replacing replication
# with a cron job.
-while true; do
-## execute endlessly in an infinite loop
+script_name=$(basename $0)
-db='log'
-ls="regexp '^[A-Z0-9].*[0-9]+$'"
-mhost='m4-master.eqiad.wmnet'
-shost="localhost"
+function usage {
+ echo "
+${script_name} [-n] [-b <batch-size>] [-D <cutoff-days>] [-d <database>] [-s
<slave-database>] <master-host> [<slave-host>]
-slave="mysql -h $shost --compress --skip-column-names --skip-ssl"
-master="mysql -h $mhost --compress --skip-column-names --skip-ssl"
-dump="mysqldump --skip-ssl -h $mhost --skip-opt --single-transaction --quick
--skip-triggers"
-dumpdata="$dump --no-create-info --insert-ignore --extended-insert --compress
--hex-blob"
-querytables="select table_name from information_schema.tables where
table_schema = '$db' and table_name"
+OPTIONS:
+ -h Print this usage message
+ -n Dry run. Only print the dump command that will be run, don't actually
dump from master into slave.
+ -b Batch replicate this many rows at a time. Default: 1000
+ -d Database name. Default: log
+ -s Slave database name. Defaults to -d if not given.
+ -D Don't replicate tables if they don't have events more recent than than
-D days ago.
+
+DESCRIPTION:
+ Checks all eventlogging tables on <master-host>, and looks for those tables
in <slave-host>
+ If <master-host> has any records with a larger auto increment \`id\` (or
\`timestamp\`) than
+ <slave-host>, then those records are mysqldumped into <slave-host>
<batch-size> records at a time.
+
+ <slave-host> defaults to localhost.
+"
+
+exit 0
+}
+
# select this many rows per table at a time
batch_size=1000
+database=log
+slave_host=localhost
+dry_run=0
+cutoff_days=0
-script=$(basename ${BASH_SOURCE})
-# Multi-execution is controlled by init.d
-#if [ $(ps ax | grep $script | grep -v grep | wc -l) -gt 2 ]; then
-# echo "duplicate process" 1>&2
-# exit 1
-#fi
+while getopts "hnvb:d:s:D:" opt; do
+ case "$opt" in
+ h)
+ usage
+ ;;
+ b) batch_size=$OPTARG
+ ;;
+ d)
+ database=$OPTARG
+ ;;
+ s)
+ slave_database=$OPTARG
+ ;;
+ D)
+ cutoff_days=$OPTARG
+ ;;
+ n)
+ dry_run=1
+ ;;
+ esac
+done
+
+# If slave_database was not given, default to using database.
+slave_database=${slave_database:-database}
+
+shift $((OPTIND-1))
+
+
+master_host="${1}"
+[ -z "${master_host}" ] && echo "ERROR: Must specify <master-host>" && usage
+shift
+
+# Last arg will be slave_host if given, otherwise use localhost.
+[ -n "${1}" ] && slave_host="${1}"
+
+
+
+table_regex="regexp '^[A-Z0-9].*[0-9]+$'"
+
+slave="mysql -h $slave_host --compress --skip-column-names --skip-ssl"
+master="mysql -h $master_host --compress --skip-column-names --skip-ssl"
+
+dump_opts="--skip-ssl --skip-opt --single-transaction --quick --skip-triggers"
+dump_schema="mysqldump -h $master_host $dump_opts --no-data"
+dump_data="mysqldump -h $master_host $dump_opts --no-create-info
--insert-ignore --extended-insert --compress --hex-blob"
+
+master_tables_query="select table_name from information_schema.tables where
table_schema = '$database'"
+slave_tables_query="select table_name from information_schema.tables where
table_schema = '$slave_database'"
+
+# Add and table_name = $table at the end of this query to use it
+has_id_column_query="select count(*) from information_schema.columns where
table_schema = '$database' and column_name = 'id'"
set -e
-for table in $($master $db -e "$querytables $ls"); do
+echo "Syncing EventLogging records in $master_host $database -> $slave_host
$slave_database..."
+# Execute endlessly in an infinite loop.
+while true; do
- echo -n "`date` $shost $table"
+ for table in $($master -e "$master_tables_query and table_name
$table_regex order by rand()"); do
- if [ $($slave $db -e "$querytables = '$table'" | wc -l) -eq 0 ]; then
- echo -n ", create"
- $dump --no-data $db $table | $slave $db
- fi
+ echo -n "$(date +"%Y-%m-%dT%H:%M:%S") $slave_host $slave_database
$table"
- #id=$($master $db -e "select min(id) from \`$table\`")
+ # If no new events for this table since cutoff_days ago, don't attempt
to replicate.
+ if [ $cutoff_days -ne 0 ]; then
+ cutoff_timestamp=$(date --date="$cutoff_days days ago"
+'%Y%m%d%H%M%S')
- #if [ ! $id = "NULL" ]; then
- #echo -n ", purge < $id"
- #$slave $db -e "delete from \`$table\` where id < $id order by id
limit 100000"
- #fi
+ max_master_timestamp=$($master $database -e "select max(timestamp)
from \`$table\` where timestamp >= '$cutoff_timestamp'")
+ if [ "${max_master_timestamp}" = "NULL" ]; then
+ echo " (no new data on master in last $cutoff_days days,
skipping)"
+ continue
+ fi
+ fi
- ts=$($slave $db -e "select ifnull(max(timestamp),0) from \`$table\`")
+ # If the table does not exist on the slave,
+ # then dump the schema from the master to create it.
+ if [ $($slave -e "$slave_tables_query and table_name = '$table'" | wc
-l) -eq 0 ]; then
+ echo -n ", create"
+ if [ $dry_run -eq 1 ]; then
+ echo -n " (dry-run) $dump_schema $database $table | $slave
$slave_database"
+ else
+ $dump_schema $database $table | $slave $slave_database
+ fi
+ fi
- echo -n " >= $ts"
- # mysqldump has overhead with information_schema queries, so do a quick
check for a noop
- if [ ! $($master $db -e "select ifnull(max(timestamp),0) from \`$table\`")
= $ts ]; then
- echo -n " (rows!)"
- $dumpdata --insert-ignore --where="timestamp >= '$ts' ORDER BY
timestamp LIMIT $batch_size" $db "$table" | $slave $db
- #$dumpdata --insert-ignore --where="timestamp >= '$ts'" $db "$table"
>tmp/$table.sql
- else
- echo -n " (nothing)"
- fi
+ # # Get the minimum autoincrement id from master table
+ # TODO: should we bring this back???
+ # id=$($master $database -e "select min(id) from \`$table\`")
+ #
+ # if [ ! $id = "NULL" ]; then
+ # echo -n ", purge < $id"
+ # $slave $db -e "delete from \`$table\` where id < $id order by id
limit 100000"
+ # fi
- echo " ok"
+ # replicate by timestamp or id. id is preferred.
+ column='id'
+ # If this table does not have an auto-increment id field, then use
timestamp instead.
+ if [ $($master -e "$has_id_column_query and table_name = '$table'")
-eq 0 ]; then
+ column='timestamp'
+ fi
-done
+ # Select records from the master where $column > max($column) on the
slave,
+ # and dump them into the slave.
+ max_master=$($master $database -e "select ifnull(max($column),0) from
\`$table\`")
+ max_slave=$($slave $slave_database -e "select ifnull(max($column),0)
from \`$table\`")
-#echo "Sleeping for 10 seconds before the next batch..."
-#sleep 10
-## infinite loop
+ # If no new data on the master, do nothing.
+ if [ $max_slave = $max_master ]; then
+ echo -n " (nothing)"
+ # Else dump $batch_size records from master into the slave.
+ else
+ echo -n " (rows!)"
+ if [ $dry_run -eq 1 ]; then
+ echo -n " (dry-run) $dump_data --where=\"$column >= $max_slave
ORDER BY $column LIMIT $batch_size\" $database \"$table\" | $slave
$slave_database"
+ else
+ $dump_data --where="id >= $max_slave ORDER BY id LIMIT
$batch_size" $database "$table" | $slave $slave_database
+ fi
+ fi
+
+ echo " ok"
+ done
+
+ echo "Sleeping for 5 seconds before the next batch..."
+ sleep 5
+
+# End infinite loop.
done
diff --git a/modules/role/manifests/mariadb.pp
b/modules/role/manifests/mariadb.pp
index f16fb9b..7e3870c 100644
--- a/modules/role/manifests/mariadb.pp
+++ b/modules/role/manifests/mariadb.pp
@@ -196,6 +196,18 @@
}
class role::mariadb::analytics::custom_repl_slave {
+ # Sync eventlogging tables from m4-master.eqiad.wmnet to localhost
+ # using a custom bash 'replication' script, that looks for new records on
'master',
+ # and inserts into 'slave'.
+ $master_host = 'm4-master.eqiad.wmnet'
+ $slave_host = 'localhost'
+ $database = 'log'
+
+ # Don't try to 'replicate' tables with no events more recent than this
many days ago.
+ $cutoff_days = 90
+
+ # Only 'replicate' this many rows at a time.
+ $batch_size = 1000
file { '/usr/local/bin/eventlogging_sync.sh':
ensure => present,
@@ -208,7 +220,7 @@
owner => 'root',
group => 'root',
mode => '0755',
- source => 'puppet:///modules/role/mariadb/eventlogging_sync.init',
+ content => template('role/mariadb/eventlogging_sync.init.erb'),
require => File['/usr/local/bin/eventlogging_sync.sh'],
notify => Service['eventlogging_sync'],
}
diff --git a/modules/role/files/mariadb/eventlogging_sync.init
b/modules/role/templates/mariadb/eventlogging_sync.init.erb
old mode 100644
new mode 100755
similarity index 92%
rename from modules/role/files/mariadb/eventlogging_sync.init
rename to modules/role/templates/mariadb/eventlogging_sync.init.erb
index 437a0a4..cbecba4
--- a/modules/role/files/mariadb/eventlogging_sync.init
+++ b/modules/role/templates/mariadb/eventlogging_sync.init.erb
@@ -10,7 +10,7 @@
### END INIT INFO
dir="/usr/local"
-cmd="/usr/local/bin/eventlogging_sync.sh"
+cmd="/usr/local/bin/eventlogging_sync.sh -D <%= @cutoff_days %> -b <%=
@batch_size %> -d <%= @database %> <%= @master_host %> <%= @slave_host %>"
user="root"
name=`basename $0`
--
To view, visit https://gerrit.wikimedia.org/r/345646
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ie96e872ebb763ce8398adcedc52a93ff9f16702a
Gerrit-PatchSet: 4
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Ottomata <[email protected]>
Gerrit-Reviewer: Giuseppe Lavagetto <[email protected]>
Gerrit-Reviewer: Jcrespo <[email protected]>
Gerrit-Reviewer: Marostegui <[email protected]>
Gerrit-Reviewer: Mforns <[email protected]>
Gerrit-Reviewer: Nuria <[email protected]>
Gerrit-Reviewer: Ottomata <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits