解決したい課題
- GitHub-Hosted Runner上のJob内で生成したcsvファイルを、Self-Hosted Runnerのジョブに渡して社内ネットワーク内のMySQLサーバーに接続したい
- csvデータ中身を読み込んでINSERTのSQLクエリを実行する処理を簡潔にしたい
解決する方法
MySQLには高速でテキストファイルを読み込んでテーブルに突っ込む LOAD DATA STATEMENT
があるので、ワークフロー内でこれを使う。
YAML(GitHub Actionsのワークフローファイル)
CSVの生成は GitHub-Hosted Runner
上で、csvファイルのINSERTは Self-Hosted Runner
上でやりたいのでジョブを分けています。
refresh-mysql-db.yml
name: reflesh-mysql-data
on:
workflow_dispatch:
jobs:
create-and-upload-csv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create CSV file
run: |
echo "id,name,age" > example.csv
echo "1,Tomitarou,67" >> example.csv
- name: Upload CSV as artifact
uses: actions/upload-artifact@v4
with:
name: example-csv
path: example.csv
refresh-mysql-data:
needs: create-and-upload-csv
runs-on: [self-hosted, linux, x64]
container:
# 今回は AWS ECR Public Gallery にある Docker の公式 MySQL イメージを使用
# Pull Through Cache 機能を介してキャッシュされるようにしている
image: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/docker/library/mysql:latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4 # 前のジョブで作ったcsvをダウンロード
with:
name: example-csv
path: downloaded
- name: Refresh MySQL Data
env:
# あらかじめリポジトリの Secrets and variables に設定しておく
MYSQL_HOST: ${{ vars.MYSQL_HOST }}
MYSQL_USER: ${{ secrets.MYSQL_USER }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
MYSQL_DB_NAME: ${{ vars.MYSQL_DB_NAME }}
run: mysql --host=${MYSQL_HOST} --user=${MYSQL_USER} --password=${MYSQL_PASSWORD} ${MYSQL_DB_NAME} --local-infile=1 -e "$(cat path/to/refresh_data.sql)"
SQLファイル
- ファイルPATH:
path/to/refresh_data.sql
- サンプルとして、テーブルに対して
DELETE-INSERT
のクエリを投げるようにしている - 対象のテーブル増やしたければ、csbとテーブルの指定を変えてSQLファイル内に追加するだけ
refresh_data.sql
START TRANSACTION;
DELETE FROM HOGE_TABLE;
LOAD DATA LOCAL INFILE "downloaded/example.csv" INTO TABLE HOGE_TABLE FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' IGNORE 1 LINES;
COMMIT;
ちょっとだけ補足
-
YAML:
- MySQLクライアント起動時に指定する
--local-infile=1
オプション- MySQLサーバーにローカルファイル(クライアント側で生成されたファイル)を読み込むことを許可する設定
- この設定値が 0 だと利用できないので注意(逆にセキュリティやデータの整合性を維持するために設定値を 0 にしておいた方がよい場合もあるので要確認)
- MySQLクライアント起動時に指定する
-
SQL(LOAD DATA Statement):
-
LOCAL
:- クライアントマシンにあるファイルを読み込むよう指定
-
INFILE 'downloaded/example.csv'
:- MySQLにロードするデータファイルへのパスを指定
-
INTO TABLE HOGE_TABLE
:- データをロードする対象のテーブルを指定
-
FIELDS TERMINATED BY ','
:- 各フィールド(列)をどの文字で区切るかを明示的に指定
- 今回はCSVなのでカンマ(
,
)を区切り文字として使う
-
OPTIONALLY ENCLOSED BY '"'
:- フィールドの値が必要に応じて引用符(
"
)で囲まれていることを指定 - フィールド値の中にカンマが含まれている場合の対策)
- フィールドの値が必要に応じて引用符(
-
IGNORE 1 LINES
:- CSVファイルの最初のヘッダー行を無視するために指定
-
注意点
- MySQLのトランザクションについて
-
AUTOCOMMIT
オプション:MySQL はデフォルトでON
になっている -
LOAD DATA Statement
:NDB strage engine
使用時のみ暗黙的commitになる(トランザクション制御が不可能)
-
The statements listed in this section (and any synonyms for them) implicitly end any transaction active in the current session, as if you had done a COMMIT before executing the statement.(訳:以下に書かれているstatementは暗黙的に現在のトランザクションを終了しちゃうよ)
- ...
- Data loading statements. LOAD DATA. LOAD DATA causes an implicit commit only for tables using the NDB storage engine.
- ...
なので、トランザクション制御できるかどうか、ストレージエンジンを1度見たほうがよさげ。
strage engine の調査クエリ
SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'データベース名';
結果のサンプル
┌──────────────┬────────────┐
│ TABLE_NAME │ ENGINE │
├──────────────┼────────────┤
│ HOGE_TABLE │ InnoDB │ ← InnoDBはトランザクション制御できそう
│ FUGA_TABLE │ InnoDB │
│ PIYO_TABLE │ InnoDB │
└──────────────┴────────────┘
参考