diff --git a/.github/workflows/buildandrun-docker.yml b/.github/workflows/buildandrun-docker.yml new file mode 100644 index 0000000..4c2d645 --- /dev/null +++ b/.github/workflows/buildandrun-docker.yml @@ -0,0 +1,16 @@ +name: MySQL 8.0 Build and Run /w docker-compose +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build and Run Spirit + run: docker compose up mysql buildandrun --abort-on-container-exit + working-directory: compose diff --git a/.github/workflows/mysql8-docker.yml b/.github/workflows/mysql8-docker.yml new file mode 100644 index 0000000..449851a --- /dev/null +++ b/.github/workflows/mysql8-docker.yml @@ -0,0 +1,16 @@ +name: MySQL 8.0.33 (with replicas) /w docker-compose +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test + run: docker compose -f compose.yml up mysql mysql_replica test --abort-on-container-exit + working-directory: compose/replication diff --git a/.github/workflows/mysql8.0.28-docker.yml b/.github/workflows/mysql8.0.28-docker.yml new file mode 100644 index 0000000..0c68eb1 --- /dev/null +++ b/.github/workflows/mysql8.0.28-docker.yml @@ -0,0 +1,16 @@ +name: MySQL 8.0.28 (Aurora version) /w docker-compose +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test + run: docker compose -f compose.yml -f 8.0.28.yml up mysql test --abort-on-container-exit + working-directory: compose diff --git a/.github/workflows/mysql84-docker.yml b/.github/workflows/mysql84-docker.yml new file mode 100644 index 0000000..c1c2046 --- /dev/null +++ b/.github/workflows/mysql84-docker.yml @@ -0,0 +1,16 @@ +name: MySQL 8.4 GA /w docker-compose +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test + run: docker compose -f compose.yml -f 8.4.yml up mysql mysql_replica test --abort-on-container-exit + working-directory: compose/replication diff --git a/.github/workflows/mysql8_rbr_minimal-docker.yml b/.github/workflows/mysql8_rbr_minimal-docker.yml new file mode 100644 index 0000000..b1d6aff --- /dev/null +++ b/.github/workflows/mysql8_rbr_minimal-docker.yml @@ -0,0 +1,16 @@ +name: MySQL 8.0.33 (RBR minimal) /w docker-compose +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test + run: docker compose -f compose.yml -f 8.0.33-rbr-minimal.yml up mysql test --abort-on-container-exit + working-directory: compose diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0e82e53 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM golang:1.21 + +WORKDIR /app +COPY ../go.mod go.sum ./ +RUN go mod download +COPY .. . \ No newline at end of file diff --git a/compose/8.0.28.yml b/compose/8.0.28.yml new file mode 100644 index 0000000..ab945e9 --- /dev/null +++ b/compose/8.0.28.yml @@ -0,0 +1,5 @@ +services: + mysql: + image: mysql:8.0.28 + platform: linux/amd64 + ports: [ '8028:3306' ] \ No newline at end of file diff --git a/compose/8.0.33-rbr-minimal.yml b/compose/8.0.33-rbr-minimal.yml new file mode 100644 index 0000000..adc1e0a --- /dev/null +++ b/compose/8.0.33-rbr-minimal.yml @@ -0,0 +1,5 @@ +services: + mysql: + image: mysql:8.0.33 + ports: [ '8033:3306' ] + command: --binlog-row-image=MINIMAL \ No newline at end of file diff --git a/compose/bootstrap.sql b/compose/bootstrap.sql new file mode 100644 index 0000000..3b3d194 --- /dev/null +++ b/compose/bootstrap.sql @@ -0,0 +1,31 @@ +use mysql; + +create role if not exists R_DO_IT_ALL; +grant all on *.* to R_DO_IT_ALL; +create user if not exists msandbox@'%' identified with caching_sha2_password by 'msandbox'; + +grant R_DO_IT_ALL to msandbox@'%' ; +set default role R_DO_IT_ALL to msandbox@'%'; + + +create role if not exists R_REPLICATION; +grant REPLICATION SLAVE, REPLICATION CLIENT on *.* to R_REPLICATION; +create role if not exists R_THROTTLER; +grant SELECT on performance_schema.replication_applier_status_by_worker to R_THROTTLER; +grant SELECT on performance_schema.replication_connection_status to R_THROTTLER; +create user if not exists rsandbox@'%' identified with caching_sha2_password by 'rsandbox'; +grant R_REPLICATION, R_THROTTLER to rsandbox@'%'; +set default role R_REPLICATION, R_THROTTLER to rsandbox@'%'; + +flush privileges; + +create database if not exists test; + +use test; + +create table t1 +( + id int not null primary key auto_increment, + b int not null, + c int not null +); diff --git a/compose/compose.yml b/compose/compose.yml new file mode 100644 index 0000000..3b19854 --- /dev/null +++ b/compose/compose.yml @@ -0,0 +1,46 @@ +services: + mysql: + image: mysql:8.0.33 + ports: [ '8033:3306' ] + # to supress mbind: Operation not permitted in CI + # https://stackoverflow.com/a/55706057 + cap_add: + - SYS_NICE + environment: + MYSQL_ROOT_PASSWORD: msandbox + healthcheck: + test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD + start_period: 1s + interval: 1s + timeout: 2s + retries: 60 + volumes: + - mysql-standalone-data-dir:/var/lib/mysql + - ./bootstrap.sql:/docker-entrypoint-initdb.d/bootstrap.sql + + test: + build: + context: ../ + command: go test -race -v ./... + depends_on: + mysql: + condition: service_healthy + environment: + MYSQL_DSN: msandbox:msandbox@tcp(mysql)/test + + buildandrun: + build: + context: ../ + command: scripts/buildandrun.sh + depends_on: + mysql: + condition: service_healthy + environment: + HOST: mysql:3306 + USERNAME: msandbox + PASSWORD: msandbox + DATABASE: test + TABLE: t1 + +volumes: + mysql-standalone-data-dir: \ No newline at end of file diff --git a/compose/replication/8.0.28.yml b/compose/replication/8.0.28.yml new file mode 100644 index 0000000..609bd75 --- /dev/null +++ b/compose/replication/8.0.28.yml @@ -0,0 +1,9 @@ +services: + mysql: + image: mysql:8.0.28 + platform: linux/amd64 + ports: [ '8028:3306' ] + mysql_replica: + image: mysql:8.0.28 + platform: linux/amd64 + ports: [ '8128:3306' ] \ No newline at end of file diff --git a/compose/replication/8.0.33-rbr-minimal.yml b/compose/replication/8.0.33-rbr-minimal.yml new file mode 100644 index 0000000..6adbdd2 --- /dev/null +++ b/compose/replication/8.0.33-rbr-minimal.yml @@ -0,0 +1,8 @@ +services: + mysql: + image: mysql:8.0.33 + ports: [ '8033:3306' ] + command: --binlog-row-image=MINIMAL --server-id=1 --log-bin=mysql-bin --gtid-mode=on --enforce-gtid-consistency=on + mysql_replica: + image: mysql:8.0.33 + ports: [ '8133:3306' ] \ No newline at end of file diff --git a/compose/replication/8.4.yml b/compose/replication/8.4.yml new file mode 100644 index 0000000..0189ec6 --- /dev/null +++ b/compose/replication/8.4.yml @@ -0,0 +1,7 @@ +services: + mysql: + image: mysql:8.4 + ports: [ '8040:3306' ] + mysql_replica: + image: mysql:8.4 + ports: [ '8141:3306' ] \ No newline at end of file diff --git a/compose/replication/bootstrap_replica.sql b/compose/replication/bootstrap_replica.sql new file mode 100644 index 0000000..5f9a6df --- /dev/null +++ b/compose/replication/bootstrap_replica.sql @@ -0,0 +1 @@ +change replication source to source_host='mysql',source_user='rsandbox',source_password='rsandbox',source_auto_position=1,get_source_public_key=1; \ No newline at end of file diff --git a/compose/replication/compose.yml b/compose/replication/compose.yml new file mode 100644 index 0000000..3534409 --- /dev/null +++ b/compose/replication/compose.yml @@ -0,0 +1,77 @@ +services: + mysql: + image: mysql:8.0.33 + ports: [ '8033:3306' ] + # to supress mbind: Operation not permitted in CI + # https://stackoverflow.com/a/55706057 + cap_add: + - SYS_NICE + environment: + MYSQL_ROOT_PASSWORD: msandbox + healthcheck: + test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD + start_period: 1s + interval: 1s + timeout: 2s + retries: 60 + volumes: + - mysql-data-dir:/var/lib/mysql + - ../bootstrap.sql:/docker-entrypoint-initdb.d/bootstrap.sql + command: --server-id=1 --log-bin=mysql-bin --gtid-mode=on --enforce-gtid-consistency=on + + mysql_replica: + image: mysql:8.0.33 + ports: [ '8133:3306' ] + # to supress mbind: Operation not permitted in CI + # https://stackoverflow.com/a/55706057 + cap_add: + - SYS_NICE + environment: + MYSQL_ROOT_PASSWORD: msandbox + healthcheck: + test: ["CMD", "./replication_health_check.sh"] + start_period: 1s + interval: 1s + timeout: 2s + retries: 60 + volumes: + - mysql-replica-data-dir:/var/lib/mysql + - ./bootstrap_replica.sql:/docker-entrypoint-initdb.d/bootstrap_replica.sql + - ./replication_health_check.sh:/replication_health_check.sh + command: --server-id=2 --relay-log=mysql-relay-bin --gtid-mode=on --enforce-gtid-consistency=on + depends_on: + mysql: + condition: service_healthy + test: + build: + context: ../../. + command: go test -race -v ./... + depends_on: + mysql: + condition: service_healthy + mysql_replica: + condition: service_healthy + environment: + MYSQL_DSN: msandbox:msandbox@tcp(mysql)/test + REPLICA_DSN: rsandbox:rsandbox@tcp(mysql_replica)/ + + buildandrun: + build: + context: ../../. + command: scripts/buildandrun.sh + depends_on: + mysql: + condition: service_healthy + mysql_replica: + condition: service_healthy + environment: + HOST: mysql:3306 + USERNAME: msandbox + PASSWORD: msandbox + DATABASE: test + TABLE: t1 + REPLICA_DSN: rsandbox:rsandbox@tcp(mysql_replica)/ + +volumes: + mysql-data-dir: + mysql-replica-data-dir: \ No newline at end of file diff --git a/compose/replication/replication_health_check.sh b/compose/replication/replication_health_check.sh new file mode 100755 index 0000000..cc31ff2 --- /dev/null +++ b/compose/replication/replication_health_check.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euxo pipefail + +replica_status=$(mysql -hmysql_replica -uroot -p"$MYSQL_ROOT_PASSWORD" -e "show replica status\G") + +if ! echo "$replica_status" | grep -q 'Replica_IO_Running: Yes'; then + echo "replica IO not running..." + exit 1 +fi + +if ! echo "$replica_status" | grep -q 'Replica_SQL_Running: Yes'; then + echo "replica SQL not running..." + exit 1 +fi + +if ! echo "$replica_status" | grep -q 'Seconds_Behind_Source: 0'; then + echo "unexpected replication lag..." + exit 1 +fi diff --git a/scripts/buildandrun.sh b/scripts/buildandrun.sh new file mode 100755 index 0000000..9f9dba1 --- /dev/null +++ b/scripts/buildandrun.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +set -e + +go build ./cmd/spirit + +if [ -n "$REPLICA_DSN" ]; then + ./spirit --replica-dsn "$REPLICA_DSN" --host $HOST --username $USERNAME --password $PASSWORD --database=$DATABASE --table=$TABLE +else + ./spirit --replica-dsn "$REPLICA_DSN" --host $HOST --username $USERNAME --password $PASSWORD --database=$DATABASE --table=$TABLE +fi