- 投稿日:2020-01-18T23:47:08+09:00
Capistranoでの自動デプロイでエラーが起きた際の対処方法
この記事について
この記事では、自身がRailsで開発しているアプリをCapistranoでデプロイをしようとした時にエラーが発生し、解決するまでに試した作業を書いています。
同様のエラーが起きた際に自分が読み返すために書いていますが、他の方の参考にもなれば嬉しいです。
前回の記事と同じような状況ながら違う原因でエラーが出ていたので、別の記事として書くことにしました。エラーが起こった時の状況
Capistranoで自動デプロイを実行したがエラーのため完了でしませんでした。
エラーの内容を確認してみると、以下のように書かれていました。00:15 deploy:migrating 01 $HOME/.rbenv/bin/rbenv exec bundle exec rake db:migrate 01 rake aborted! 01 StandardError: An error has occurred, all later migrations canceled: 01 01 Mysql2::Error: Table 'users' already exists: CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` var…まだまだエラー表示は続きますが、要は「migrationでuserテーブルを作成しようとしたが、userテーブルが既に存在するので実行できません」ってことですね。
チーム開発をしている状況で、migrationファイルが変わってしまって上手くマイグレーションできなかったものと思われます。今後も開発中にチームメンバーの更新によって変更が発生する可能性も無くはないでしょうか。解決までの作業
今回のエラーはmigrationファイルと現状のデータベースで齟齬が発生してしまったため起こったものと考えられます。
そこでデータベースを一度削除し、1からマイグレーションを行うことで解決できると考えました。サーバー環境のMySQLに接続し、既存のデータベースを削除する
ターミナルからawsにアクセスし、データベースを操作するためにMySQLに接続します。
mysql -u root -pパスワードを入力すればMySQLへの接続が完了します。接続できたら既存のデータベースを削除します。まずは削除するデータベースを確認します。
show databases; # 実行結果 +--------------------+ | Database | +--------------------+ | information_schema | | sample_production | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)データベースの名前が確認できたら、そのデータベースを削除します。
mysql> drop database`sample_production`; # 実行結果 Query OK, 9 rows affected (0.07 sec)これで既存のデータベースは削除されました。
データベースを再作成する
データベースがないままだと自動デプロイができませんので、削除した後は再びデータベースを作成します。
MySQLからの操作でも作成できそうですが、今回はawsからの操作で作成します。mysql> quit [ec2-user@ip-111-111-111-111 sample]$ rails db:create RAILS_ENV=productionマイグレーションを実行する(自動デプロイを実行)
bundle exec cap production deploy以上です。今回はこの作業で自動デプロイができるように復旧できました。
- 投稿日:2020-01-18T23:47:08+09:00
Capistranoでの自動デプロイでエラーが起きた際の対処方法(migration error)その2
この記事について
この記事では、自身がRailsで開発しているアプリをCapistranoでデプロイをしようとした時にエラーが発生し、解決するまでに試した作業を書いています。
同様のエラーが起きた際に自分が読み返すために書いていますが、他の方の参考にもなれば嬉しいです。
前回の記事と同じような状況ながら違う原因でエラーが出ていたので、別の記事として書くことにしました。エラーが起こった時の状況
Capistranoで自動デプロイを実行したがエラーのため完了でしませんでした。
エラーの内容を確認してみると、以下のように書かれていました。00:15 deploy:migrating 01 $HOME/.rbenv/bin/rbenv exec bundle exec rake db:migrate 01 rake aborted! 01 StandardError: An error has occurred, all later migrations canceled: 01 01 Mysql2::Error: Table 'users' already exists: CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` var…まだまだエラー表示は続きますが、要は「migrationでuserテーブルを作成しようとしたが、userテーブルが既に存在するので実行できません」ってことですね。
チーム開発をしている状況で、migrationファイルが変わってしまって上手くマイグレーションできなかったものと思われます。今後も開発中にチームメンバーの更新によって変更が発生する可能性も無くはないでしょうか。解決までの作業
今回のエラーはmigrationファイルと現状のデータベースで齟齬が発生してしまったため起こったものと考えられます。
そこでデータベースを一度削除し、1からマイグレーションを行うことで解決できると考えました。サーバー環境のMySQLに接続し、既存のデータベースを削除する
ターミナルからawsにアクセスし、データベースを操作するためにMySQLに接続します。
mysql -u root -pパスワードを入力すればMySQLへの接続が完了します。接続できたら既存のデータベースを削除します。まずは削除するデータベースを確認します。
show databases; # 実行結果 +--------------------+ | Database | +--------------------+ | information_schema | | sample_production | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)データベースの名前が確認できたら、そのデータベースを削除します。
mysql> drop database`sample_production`; # 実行結果 Query OK, 9 rows affected (0.07 sec)これで既存のデータベースは削除されました。
データベースを再作成する
データベースがないままだと自動デプロイができませんので、削除した後は再びデータベースを作成します。
MySQLからの操作でも作成できそうですが、今回はawsからの操作で作成します。mysql> quit [ec2-user@ip-111-111-111-111 sample]$ rails db:create RAILS_ENV=productionマイグレーションを実行する(自動デプロイを実行)
bundle exec cap production deploy以上です。今回はこの作業で自動デプロイができるように復旧できました。
- 投稿日:2020-01-18T22:05:42+09:00
MariaDBのクライアント用にHeidiSQLを入れてみた
経緯
自宅PCにDBを直接インストールしたくないので、xamppのMySQL(MariaDB)を使っている。
以前、仕事でMySQL Workbenchを使っていたので、インストールしてMySQLにログインしようとしたらエラーになった。wb.log21:10:15 [ERR][ WBContext]: Unsupported server version: mariadb.org binary distribution 10.4.8-MariaDBWorkbenchのログを見てみると使っているMariaDBのバージョンがサポート対象外らしい。
使ったことのある A5:SQL Mk-2 でも良かったのだがせっかくなので別のものにしようと思い、HeidiSQL
を入れることにした。検証環境
- Windows10 Home
- XAMPP 7.3.11
- MariaDB 10.4.8
- MySQL Workbench 8.0.19
- HeidiSQL 10.3.0.5771
インストーラの取得
以下が公式ページ。
HeidiSQL - MariaDB, MySQL, MSSQL and PostgreSQL made easyヘッダの「Downloads」から「Installer」をクリック。
インストーラがダウンロードされる。
インストール
ダウンロードしたインストーラを実行する。
接続
上記の手順だとHeidiSQLが起動されるので下記画面になる。
今回はxamppのMariaDBに接続するのでホスト名は「127.0.0.1」のままでOK。
デフォルトのままだと、ユーザ名は「root」、パスワードなし(だった気がする)なので、他の項目も変えず「開く」。
- 投稿日:2020-01-18T20:25:25+09:00
データベースのリセット方法
- 投稿日:2020-01-18T17:55:17+09:00
MySQLのバイナリログが原因でディスクが枯渇した場合の対処方法
はじめに
本記事はMySQLを使用していて、バイナリログが原因でディスクが枯渇した場合の対処方法について記載しています。
MySQLで使用しているデータ領域が枯渇し、ディスク使用率が100%になりMySQLが起動できない事象が発生しました。
原因を調査したところ、データ領域として使用している
/data/mysql/
配下のディレクトリに、1.1Gのバイナリログが大量に出力されていることを確認。バイナリログを
puge
して削除したいがMySQLも起動できないため、mysqlにもログインできません。
このようなケースの暫定対応と恒久対応について記載します。暫定対応
まずは、rmコマンドで一番古いバイナリログを削除し、
df
コマンドを実行して空き容量が確保されたことを確認します。
# rm /data/mysql/binlog.000169
/dev/sdb1 50G 49G 1.1G 98% /data
次にmysqlを起動。正常に起動できたことを確認し、mysqlにログインする。
# systemctl start mysql
# systemctl status mysql
# mysql -u root -p
以下のコマンドを実行し、バイナリログの状況を確認する。
- 最新のバイナリログ
mysql> SHOW MASTER STATUS\g; +---------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +---------------+----------+--------------+------------------+-------------------+ | binlog.000215 | 155 | | | | +---------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
- バイナログの一覧
mysql> SHOW MASTER LOGS; +---------------+------------+-----------+ | Log_name | File_size | Encrypted | +---------------+------------+-----------+ | binlog.000170 | 1073742751 | No | | binlog.000171 | 1073751257 | No | | binlog.000172 | 1073757631 | No | | binlog.000173 | 1073755121 | No | | binlog.000174 | 1073752838 | No | | binlog.000175 | 1073743951 | No | | binlog.000176 | 1073748347 | No | | binlog.000177 | 1073751179 | No | | binlog.000178 | 1073763386 | No | | binlog.000179 | 1073765359 | No | | binlog.000180 | 1073745938 | No | | binlog.000181 | 1073763697 | No | | binlog.000182 | 1073744490 | No | | binlog.000183 | 1073761859 | No | | binlog.000184 | 1073775954 | No | | binlog.000185 | 1073757908 | No | | binlog.000186 | 1073773373 | No | | binlog.000187 | 1073755666 | No | | binlog.000188 | 1073743518 | No | | binlog.000189 | 1073782889 | No | | binlog.000190 | 1073756995 | No | | binlog.000191 | 1073770640 | No | | binlog.000192 | 1073743582 | No | | binlog.000193 | 1073757032 | No | | binlog.000194 | 1073754014 | No | | binlog.000195 | 1073757718 | No | | binlog.000196 | 1073746065 | No | | binlog.000197 | 1073745350 | No | | binlog.000198 | 1073751875 | No | | binlog.000199 | 1073745702 | No | | binlog.000200 | 1073754484 | No | | binlog.000201 | 1073762857 | No | | binlog.000202 | 1073761196 | No | | binlog.000203 | 1073755084 | No | | binlog.000204 | 1073836164 | No | | binlog.000205 | 1073745617 | No | | binlog.000206 | 1073753921 | No | | binlog.000207 | 1073771413 | No | | binlog.000208 | 1073764906 | No | | binlog.000209 | 1073767754 | No | | binlog.000210 | 1073781899 | No | | binlog.000211 | 1073744615 | No | | binlog.000212 | 1073744221 | No | | binlog.000213 | 1073770872 | No | | binlog.000214 | 323286661 | No | | binlog.000215 | 398814 | No | | binlog.000216 | 896660 | No | +---------------+------------+-----------+ 47 rows in set (0.03 sec)暫定対応として蓄積された不要なバイナリログを削除する。
toは、指定したbinlog以前のファイルを削除する。
- バイナリログ削除
mysql> PURGE MASTER LOGS TO 'binlog.000212'; Query OK, 0 rows affected, 1 warning (0.00 sec)再度、
df
コマンドを実行してディスク使用率が低下したことを確認。/dev/sdb1 50G 4.0G 46G 8% /data
恒久対応
事象の直接原因はMySQLの仕様により、デフォルトの設定でバイナリログの出力が有効なため、バイナリログが蓄積され続けていたこと。
以前のMySQLバージョンでは、バイナリロギングはデフォルトで無効になっており、--log-binオプションを指定した場合は有効になり ました。MySQL 8.0以降では、--log-binオプションを指定するかどうかにかかわらず、デフォルトでバイナリロギングが有効になっています。
恒久対応としてログローテーションの設定を行い、バイナリログが蓄積されないように設定変更を行う。
設定変更後は、MySQLを再起動する。
# vi /etc/mysql/mysql.conf.d/mysqld.cnf
# binlog rotation expire_logs_days = 7上記の場合は7日でログローテーションが行われる。
おわりに
根本原因は設計時の考慮不足です。
- 投稿日:2020-01-18T16:28:57+09:00
【2020年3月5日まで】RDS証明書を更新する for java
プロローグ
新しい SSL/TLS 証明書を使用して MySQL DB インスタンスに接続するようにアプリケーションを更新
2020年3月5日までにRDSのcaを更新してくださいという話。
手順通りやれば問題ないのだが、いくつかハマりポイントがあったので記事にした。前提
・java
・tomcat
・mysql
・証明書はJKS形式
・クライアントの証明書を先に更新し、mysqlの証明書は後から更新
(同時の場合は、ダウンロードする証明書が異なる)手順
1.証明書の取得
先にクライアントを更新するので、2015,2019のバンドル版をダウンロード
場所は現在と同じでよいかと。$ cd /etc/pki/java $ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem --2020-01-18 15:22:35-- https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem s3.amazonaws.com (s3.amazonaws.com) をDNSに問いあわせています... 52.216.9.165 s3.amazonaws.com (s3.amazonaws.com)|52.216.9.165|:443 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 65484 (64K) [binary/octet-stream] `rds-combined-ca-bundle.pem' に保存中 100%[==========================================================================================================================================>] 65,484 193KB/s 時間 0.3s 2020-01-18 15:22:36 (193 KB/s) - `rds-combined-ca-bundle.pem' へ保存完了 [65484/65484]クライアントとサーバを同時に更新する場合は、SSL/TLS を使用した DB インスタンスへの接続の暗号化 から、2019バージョンにする。東京リージョンの場合は、rds-ca-2019-ap-northeast-1.pem
2.証明書の変換
pemをjksに変換する
pem->der、der->jksの2ステップを踏む必要がある
jksに変換するとき、現在の証明書と異なるファイル名にすることパスワードは現在と同じで構わない。証明書アクセス用なので、DBのパスワードとは無関係。
$ openssl x509 -outform der -in ./rds-combined-ca-bundle.pem -out ./rds-combined-ca-bundle.der $ ll -rw-rw-r-- 1 root root 1016 1月 18 15:22 rds-combined-ca-bundle.der -rw-rw-r-- 1 root root 65484 9月 20 03:27 rds-combined-ca-bundle.pem $ keytool -import -file ./rds-combined-ca-bundle.der -destkeystore ./rds-combined-ca-bundle.jks -deststoretype jks キーストアのパスワードを入力してください: キーストアのパスワードが短すぎます - 6文字以上にしてください キーストアのパスワードを入力してください: 新規パスワードを再入力してください: 所有者: CN=Amazon RDS Root CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US 発行者: CN=Amazon RDS Root CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US シリアル番号: 42 有効期間の開始日: Thu Feb 05 18:11:31 JST 2015 終了日: Thu Mar 05 18:11:31 JST 2020 証明書のフィンガプリント: MD5: CF:C1:A2:0D:A1:C1:B1:3C:12:0B:C1:5A:E5:33:73:EF SHA1: E8:11:88:56:E7:A7:CE:3E:5E:DC:9A:31:25:1B:93:AC:DC:43:CE:B0 SHA256: AF:EB:B8:40:BE:0C:1A:F6:5E:63:E0:CB:D7:9C:CE:35:65:4B:F6:0F:4E:07:62:54:BB:A9:35:E9:63:6D:53:F7 署名アルゴリズム名: SHA1withRSA サブジェクト公開鍵アルゴリズム: 2048ビットRSA鍵 バージョン: 3 拡張: #1: ObjectId: 2.5.29.35 Criticality=false AuthorityKeyIdentifier [ KeyIdentifier [ 0000: 4E 02 EE AC 3E F6 15 C8 4C ED 2C F5 05 C1 8F 9C N...>...L.,..... 0010: 84 08 49 83 ..I. ] ] #2: ObjectId: 2.5.29.19 Criticality=true BasicConstraints:[ CA:true PathLen:2147483647 ] #3: ObjectId: 2.5.29.15 Criticality=true KeyUsage [ Key_CertSign Crl_Sign ] #4: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 4E 02 EE AC 3E F6 15 C8 4C ED 2C F5 05 C1 8F 9C N...>...L.,..... 0010: 84 08 49 83 ..I. ] ] この証明書を信頼しますか。 [いいえ]: はい 証明書がキーストアに追加されました $ ll -rw-rw-r-- 1 root roor 1016 1月 18 15:22 rds-combined-ca-bundle.der -rw-rw-r-- 1 root root 1078 1月 18 15:23 rds-combined-ca-bundle.jks -rw-rw-r-- 1 root root 65484 9月 20 03:27 rds-combined-ca-bundle.pem3.context.xmlの更新
更新前にtomcatは停止すること。
urlの
trustCertificateKeyStoreUrl=file:///etc/pki/java/rds-combined-ca-bundle.jks
を作成したjksファイル名にする。context.xml<Resource name="jdbc/honyaDB" auth="Container" type="javax.sql.DataSource" username="honya" password="honyapasswd" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://honya.ap-northeast-1.rds.amazonaws.com:3306/honyaDB?useUnicode=true&characterEncoding=UTF-8&useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:///etc/pki/java/rds-combined-ca-bundle.jks&trustCertificateKeyStoreType=JKS&trustCertificateKeyStorePassword=password" />エピローグ
context.xml を保存した瞬間に反映されしまうので焦った
(検証環境なので、そうでもないけど)証明書を同じファイル名にすればcontext.xmlの変更は要らないが、同じファイル名で新旧を管理するとトラブルの元なので、ファイル名は変更することにした。
2019版証明書(rds-ca-2019-ap-northeast-1.pem)で試したら、しっかり動かなかった
サーバ側はawsコンソールでポチポチするだけなので、「SSL/TLS 証明書の更新」を参考にしてください。
参考
新しい SSL/TLS 証明書を使用して MariaDB DB インスタンスに接続するようにアプリケーションを更新
- 投稿日:2020-01-18T14:29:38+09:00
C# EntityFrameworkCore リレーションデータの取得、登録、削除
始めに
EntityFrameworkCoreを勉強がてらに触ってみて、「リレーションの構築」これは使えそうだなと思ったテクニックをメモしてみました。
作成したソースはメモ置き場としてgithubに公開しています。開発環境
・.Net Core 3.1
・MySql.Data.EntityFrameworkCore(8.0.18)
・Visual Studio Community 2019
・Windows 10下準備
データベースへの接続環境とテーブルを構築します。
DB接続
MySQL用にDBの接続設定を行います。
MySQLの接続先は各々の環境で設定してください。DbContextクラスを継承したクラスを作成し、「OnConfiguring」をオーバーライドします。
public class SampleEntities : DbContext { // 一部省略 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.UseMySQL("MySQL接続文字列"); } }データベース初期化
DBの接続先を変更したら下記コードでデータベースの作成を行います。
// 接続文字列記載のデータベースを削除 this.sampleEntities.Database.EnsureDeleted(); // 接続文字列記載のデータベースを作成 this.sampleEntities.Database.EnsureCreated();※注意
「EnsureDeleted」は接続文字列記載のデータベースを全て削除します。今回は技術検証のため使用しています。
使い方等についてはApi の作成と削除-EF Core | Microsoft Docsを参照ください。リレーションの構築
データベースの初期化を行うと次の4つのテーブルを作成します。
ER図作って
PKは全て「ID」としオートインクリメントで構成します。
・M_AREA (MArea.cs)
・M_SHOP (MShop.cs) ・・・ M_AREAが1に対して複数存在
・T_DAILY_SALES (TDailySales.cs) ・・・ M_SHOPが1に対して複数存在
・T_MONTHLY_SALES (TMonthlySales.cs) ・・・ M_SHOPが1に対して複数存在関連データの構築はDbContextを継承したクラスの「OnModelCreating」の中で行います。
public class SampleEntities : DbContext { public DbSet<MArea> Areas { get; set; } public DbSet<MShop> Shops { get; set; } public DbSet<TDailySales> DailySales { get; set; } public DbSet<TMonthlySales> MonthlySales { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.UseMySQL("MySQL接続文字列"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // エリアと店舗情報で1:nの関係を作成 modelBuilder.Entity<MShop>() .HasOne(s => s.Area) .WithMany(a => a.Shops); // 店舗情報と売上高(日別)で1:nの関係を作成 modelBuilder.Entity<TDailySales>() .HasOne(d => d.Shop) .WithMany(s => s.SalesDailies); // 店舗情報と売上高(月別)で1:nの関係を作成 modelBuilder.Entity<TMonthlySales>() .HasOne(m => m.Shop) .WithMany(s => s.SalesMonthlies); } }リレーション構築に使用するエンティティクラスです。
[Table("M_AREA")] public class MArea { [Key] [Column("ID")] public int Id { get; set; } /* 長くなるため一部プロパティは省略 */ /// <summary> /// 店舗情報 /// </summary> public List<MShop> Shops { get; set; } } [Table("M_SHOP")] public class MShop { [Key] [Column("ID")] public int Id { get; set; } // 長くなるため一部プロパティは省略 /// <summary> /// エリアID /// </summary> [Column("AREA_ID")] public int AreaId { get; set; } /// <summary> /// エリア /// </summary> public MArea Area { get; set; } /// <summary> /// 売上高(日別) /// </summary> public List<TDailySales> SalesDailies { get; set; } /// <summary> /// 売上高(月別) /// </summary> public List<TMonthlySales> SalesMonthlies { get; set; } } [Table("T_SALES_DAILY")] public class TDailySales { [Key] [Column("ID")] public int Id { get; set; } // 長くなるため一部プロパティは省略 /// <summary> /// 店舗ID /// </summary> [Column("SHOP_ID")] public int ShopId { get; set; } /// <summary> /// 店舗情報 /// </summary> public MShop Shop { get; set; } } [Table("T_SALES_MONTHLY")] public class TMonthlySales { [Key] [Column("ID")] public int Id { get; set; } // 長くなるため一部プロパティは省略 /// <summary> /// 店舗ID /// </summary> [Column("SHOP_ID")] public int ShopId { get; set; } /// <summary> /// 店舗情報 /// </summary> public MShop Shop { get; set; } }リレーション構築に必要なプロパティ名
リレーションを構築する上でプロパティの名前が重要になってきます。
プロパティ名がパターンに則っていない場合、リレーションは構築されません。
そのパターンは4つあり、1つ前のセクションで説明した「MShop」クラスで構築した場合の具体例と共にメモします。パターン1:<navigation property name><principal key property name>
// このプロパティが「navigation property」 public MArea Area { get; set; } // 「Area」が「navigation property name」、「Id」が「principal key property name」 [Column("AREA_ID")] public int AreaId { get; set; }パターン2:<navigation property name>Id
// このプロパティが「navigation property」 public MArea Area { get; set; } // 「Area」が「navigation property name」になり、その後ろに「Id」をつける。 [Column("AREA_ID")] public int AreaId { get; set; }パターン3:<principal entity name><principal key property name>
public MArea Area { get; set; } // 「MArea」が「principal entity name」、「Id」が「principal key property name」 [Column("AREA_ID")] public int MAreaId { get; set; }パターン4:<principal entity name>Id
public MArea Area { get; set; } // 「MArea」が「principal entity name」になり、その後ろに「Id」をつける。 [Column("AREA_ID")] public int MAreaId { get; set; }上記パターンの詳細な説明についてはリレーションシップ-EF Core | Microsoft Docsを参照してください。
ちなみに1対多のほかにで1対1、多対多でのリレーションの組み方などが書いてあります。リレーションデータ取得、登録、削除
リレーションデータ一括取得
リレーションデータの取得は次のように行います。
using (SampleEntities sampleEntities = new SampleEntities()) { // エリア、店舗、売上情報取得 MArea area = sampleEntities.Areas .Include(a => a.Shops) .ThenInclude(shop => shop.SalesDailies) .Include(a => a.Shops) .ThenInclude(shop => shop.SalesMonthlies) .FirstOrDefault(); }リレーションデータの構築を行っていない場合の取得方法は「Join」や「Linqのクエリ式」があります。
ただ今回はリレーションデータに関するメモのため省きます。。リレーションデータ一括登録
リレーションを構築するとオートインクリメントをキーに使用するデータの登録が簡単になります。
「LAST_INSERT_ID()」をINSERT文に組み込むなどの工夫が必要でしたが(私はこんな感じにやってました)、リレーションを構築すると、主キーを自動で紐づけて登録を行ってくれます。// エリア作成 MArea area1 = new MArea { AreaName = "北海道" }; // エリアに店舗情報追加 MShop shopHokkaido = new MShop { Area = area1, Address = "北海道xxxxxxxxxx", ShopName = "北海道店舗" }; // 店舗に売上情報追加:北海道 shopHokkaido.SalesDailies = new List<TDailySales>(); shopHokkaido.SalesDailies.Add(new TDailySales { Shop = shopHokkaido, /*紐づけに必要な情報以外は省略*/ }); shopHokkaido.SalesDailies.Add(new TDailySales { Shop = shopHokkaido, /*紐づけに必要な情報以外は省略*/ }); shopHokkaido.SalesDailies.Add(new TDailySales { Shop = shopHokkaido, /*紐づけに必要な情報以外は省略*/ }); shopHokkaido.SalesMonthlies = new List<TMonthlySales>(); shopHokkaido.SalesMonthlies.Add(new TMonthlySales { Shop = shopHokkaido, /*紐づけに必要な情報以外は省略*/ }); using(SampleEntities sampleEntities = new SampleEntities()) { // 登録内容の追加 sampleEntities.Areas.Add(area1); sampleEntities.Shops.Add(shopHokkaido); // 登録内容の保存 sampleEntities.SaveChanges(); }ちなみに登録前の何も設定していないIDの値を確認すると「-2147482647」から連番になっていることができます。
登録内容の保存後は採番された値が代入されていることを確認できます。リレーションデータ一括削除
リレーションデータの物理削除を行います。
リレーションの親要素を削除することで、子要素も自動で削除されます。using(SampleEntities sampleEntities = new SampleEntities()) { // エリア情報取得 // 子要素を取得しなくても、リレーションデータの削除されることを確認。 MArea area = sampleEntities.Areas.FirstOrDefault(); //.Include(a => a.Shops) // .ThenInclude(shop => shop.SalesDailies) //.Include(a => a.Shops) // .ThenInclude(shop => shop.SalesMonthlies) // .FirstOrDefault(); // DbSet経由の削除 sampleEntities.Remove(area); // 削除内容の保存 sampleEntities.SaveChanges() }参考にしたページ
「EF Core | Microsoft Docs」の下記ページを主に参考にしました。
・リレーションシップ
・関連データの読み込み
・関連データの保存
・連鎖削除最後に
「EF Core| Microsoft Docs」にはまだまだ有用なテクニック書かれているため、本格的に業務で使うとなれば理解する必要があると感じました。
データベースの移行に関するテクニックは特に。。
- 投稿日:2020-01-18T13:25:03+09:00
MySQLの暗黙的な型変換の検証
経緯
業務中、SELECT文で意図しないレコードがSELECTされた。
今更ではありますが私としてはMySQLでも暗黙的な型変換あったんだ~ってなりました。
型に沿った形で検索しないと暗黙的な型変換により意図しないレコードが検索される恐れがあるため、
プログラム側で適切なバリデーションを行い、MySQLへ引き渡して検索を行う必要がありますね。
…っていうのはわかってるんだけどもともとの作りでバリデーション厳密にしていないところとかってあるじゃないですか。
なのでそういうときにどういうレコードが検出される可能性があるのか考慮まとめておきました。検証準備
検証用のテーブルを記載します。
定義
mysql> show create table users; +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | users | CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)レコード
mysql> SELECT * FROM users; +----+--------+ | id | name | +----+--------+ | 0 | ゼロ | | 1 | 太郎 | | 2 | 次郎 | | 3 | 三郎 | +----+--------+ 4 rows in set (0.00 sec)※AUTO_INCREMENTなので通常id=1からですが検証のためid=0も挿入しております。
検証
WHERE id = 2
mysql> SELECT * FROM users WHERE id = 2; +----+--------+ | id | name | +----+--------+ | 2 | 次郎 | +----+--------+ 1 row in set (0.00 sec)特に問題なし。
WHERE id = '2'
mysql> SELECT * FROM users WHERE id = '2'; +----+--------+ | id | name | +----+--------+ | 2 | 次郎 | +----+--------+ 1 row in set (0.00 sec)特に問題なし。
WHERE id = a
mysql> SELECT * FROM users WHERE id = a; ERROR 1054 (42S22): Unknown column 'a' in 'where clause'mysql> SHOW WARNINGS; +-------+------+--------------------------------------+ | Level | Code | Message | +-------+------+--------------------------------------+ | Error | 1054 | Unknown column 'a' in 'where clause' | +-------+------+--------------------------------------+ 1 row in set (0.00 sec)エラーになってくれるので特に問題なし。
WHERE id = 'a'
mysql> SELECT * FROM users WHERE id = 'a'; +----+--------+ | id | name | +----+--------+ | 0 | ゼロ | +----+--------+ 1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGS; +---------+------+---------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | +---------+------+---------------------------------------+ 1 row in set (0.00 sec)ゼロさんが引っかかってしまいます。
誤ったDOUBLE値って扱いで切り捨てられ、0として検索したようですね。WHERE id = a2
mysql> SELECT * FROM users WHERE id = a2; ERROR 1054 (42S22): Unknown column 'a2' in 'where clause'mysql> SHOW WARNINGS; +-------+------+---------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------+ | Error | 1054 | Unknown column 'a2' in 'where clause' | +-------+------+---------------------------------------+ 1 row in set (0.00 sec)エラーになってくれるので特に問題なし。
WHERE id = 'a2'
mysql> SELECT * FROM users WHERE id = 'a2'; +----+--------+ | id | name | +----+--------+ | 0 | ゼロ | +----+--------+ 1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGS; +---------+------+----------------------------------------+ | Level | Code | Message | +---------+------+----------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: 'a2' | +---------+------+----------------------------------------+ 1 row in set (0.00 sec)ゼロさんが引っかかってしまいます。
誤ったDOUBLE値って扱いで切り捨てられ、0として検索したようですね。WHERE id = 2a
mysql> SELECT * FROM users WHERE id = 2a; ERROR 1054 (42S22): Unknown column '2a' in 'where clause'mysql> SHOW WARNINGS; +-------+------+---------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------+ | Error | 1054 | Unknown column '2a' in 'where clause' | +-------+------+---------------------------------------+ 1 row in set (0.00 sec)エラーになってくれるので特に問題なし。
WHERE id = '2a'
mysql> SELECT * FROM users WHERE id = '2a'; +----+--------+ | id | name | +----+--------+ | 2 | 次郎 | +----+--------+ 1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGS; +---------+------+----------------------------------------+ | Level | Code | Message | +---------+------+----------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: '2a' | +---------+------+----------------------------------------+ 1 row in set (0.00 sec)次郎さんが引っかかってしまいます。
誤ったDOUBLE値って扱いで切り捨てられ、2として検索したようですね。WHERE id = 2.1
mysql> SELECT * FROM users WHERE id = 2.1; Empty set (0.00 sec)特に問題なし。
WHERE id = '2.1'
mysql> SELECT * FROM users WHERE id = '2.1'; Empty set (0.00 sec)特に問題なし。
WHERE id = '2a1'
mysql> SELECT * FROM users WHERE id = '2a1'; +----+--------+ | id | name | +----+--------+ | 2 | 次郎 | +----+--------+ 1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGS; +---------+------+-----------------------------------------+ | Level | Code | Message | +---------+------+-----------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: '2a1' | +---------+------+-----------------------------------------+ 1 row in set (0.00 sec)次郎さんが引っかかってしまいます。
誤ったDOUBLE値って扱いで切り捨てられ、2として検索したようですね。
- 投稿日:2020-01-18T13:14:38+09:00
docker-compose を CI で実行するとき, MySQL の起動完了まで確実に待つ
背景
- jwilder/dockerize: Utility to simplify running applications in docker containers
- ufoscout/docker-compose-wait: A simple script to wait for other docker images to be started while using docker-compose
この辺は全部試したが, PHP から接続しようとすると
Connection Refused
が多発する。TCP で接続可能になってから実際に利用可能になるまで若干のラグがあるため,もっと確実な方法を探していた。対処法
とりあえずこれを書いておけsh -c 'docker-compose logs -f <MySQLコンテナ名> | { sed "/mysqld: ready for connections/ q" && kill $$ ;}' || :YAMLのリストに書く場合はエスケープが必要なのでパイプから改行する- | sh -c 'docker-compose logs -f <MySQLコンテナ名> | { sed "/mysqld: ready for connections/ q" && kill $$ ;}' || :ログを見れば確実!ログに
ready for connections
と出ればアプリケーションレベルでも必ず有効な状態であることが保証される。
- 投稿日:2020-01-18T00:37:52+09:00
ruby 2.7.0 + Rails 5.2.4 + mysql 5.7.28 でDocker開発環境を構築する備忘録
流れ
- Dockerfileを作成
- docker-compose.ymlを作成
- Gemfile,Gemfile.lockを作成
- イメージ・コンテナの作成
- Rails初期設定
- Githubにリモートリポジトリ作成
ディレクトリ構成
application_root/ - Dockerfile - docker-compose.yml - Gemfile - Gemfile.lock - その他Railsアプリケーションファイル群Dockerfileを作成
application_root下に下記のファイルを作成
# イメージのベースラインにRuby2.7.0を指定 FROM ruby:2.7.0 # Railsに必要なパッケージをインストール RUN apt-get update -qq && apt-get install -y build-essential nodejs RUN gem install bundler # ルートディレクトリを作成 RUN mkdir /app # 作業ディレクトリを指定 WORKDIR /app # ローカルのGemfileとGemfile.lockをコピー COPY ./Gemfile /app/Gemfile COPY ./Gemfile.lock /app/Gemfile.lock # Gemのインストール実行 RUN bundle install # カレントディレクトリの内容をコピー COPY . .docker-compose.ymlを作成
同様に下記のファイルを作成。
DBはmysqlで、ローカルにデータストアを設けて永続化している。# docker-compose.ymlフォーマットのバージョン指定 version: '3' services: # Railsコンテナ定義 web: # Dockerfileを使用してイメージをビルド build: . # ポート3000が来たらrailsサーバーが応答 command: bundle exec rails s -p 3000 -b '0.0.0.0' # ローカルのsrcをコンテナにマウント volumes: - .:/app ports: - 3000:3000 # dbコンテナが先に起動するよう設定 depends_on: - mysql tty: true stdin_open: true # MySQLコンテナ定義 mysql: # mysqlを使用してコンテナ作成 image: mysql:5.7.28 volumes: # データストアからマウント - ./tmp/mysql:/var/lib/mysql # コンテナ内の環境変数を定義 environment: # mysqlのルートユーザーのパスワード設定 MYSQL_ROOT_PASSWORD: password ports: - "3306:3306" restart: alwaysGemfile,Gemfile.lockの作成
以下のコマンドでGemfileを作成。
bundle init下記のように編集。
# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem "rails", "~> 5.2.4"Gemfile.lockは空で作成しておく。
touch Gemfile.lockイメージ・コンテナの作成
Railsプロジェクトを作成する。
docker-compose run web rails new . --force --database=mysqlDockerイメージを作成する。(で合っているよね?)
docker-compose build作成されたイメージを確認できる。
コンテナはまだない。# イメージのリスト docker image list # 動作しているコンテナ一覧 docker psここまでがうまくいかなかった時は削除してやり直す。
docker rm -f [container_id] docker rmi -f [image_id]いよいよコンテナを立ち上げる。
-dをつけるとデーモン実行できるが、ログを見たいのでそのまま実行。docker-compose upRails初期設定
config/database.ymlのpassword, hostを編集する。
default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # docker-compose.ymlに記載したパスワード host: mysql # docker-compose.ymlに記載したホスト名コンテナ内に入って、DBを作成する。
docker exec -it [container_name] bash rake db:create下記URLで動作が確認できる。
http://localhost:3000Github設定(ついでに)
Githubアカウントは作ってある前提で・・・
まずはSSHキーを作成。もうある場合は不要。
内容をGithubの設定から、SSH keysにアップロードする。ssh-keygen -t rsa cat .ssh/id_rsa.pubあとはリモートリポジトリのSSHアドレスを設定してPush。
# ユーザー名の設定 git config --global user.name [ユーザー名] # リモートリポジトリの設定 git remote add origin git@github.com:xxxxxxxx/xxxxxxxxx.git # アプリケーションの初期状態をPush git add -A git commit -m "create application" git push origin master