コネヒト開発者ブログ

コネヒト開発者ブログ

ママリ iOSアプリの2023年3月現在の開発環境

2023年3月現在のママリのiOSアプリは開発環境について紹介します。

言語

100% Swiftで、以前はObjective-Cのコードもありましたが、全てSwiftに書き換えました。

iOSのサポート範囲

iOS14以上としています。4/10以降は14.5以上に変更予定です。(iOS14.0~14.4でSwiftUIの挙動の違いがあるためサポート範囲外としました)

使っている主なフレームワークやライブラリ

主に以下のフレームワークやライブラリを利用しています。

  • UIKit, SwiftUI
  • RxSwift, Combine
  • Realm
  • Firebase

以前はUIKitとRxSwiftを使っていましたが、新しい画面についてはSwiftUIとCombineを使用する方針で開発しています。ただし、SwiftUIで実装する際に特殊な方法が必要であれば、新たな負債を作らないためにUIKitで実装する方針です。

アプリ内でデータ永続化のためにRealmを利用していますが、ママリは基本的にWeb APIから情報を取得して表示することがメインであり、Realmは軽量な利用に留めています。

Firebaseは、分析、ABテスト、クラッシュ検知など多様な用途で使用しています。

アーキテクチャ

RxSwiftが主となっているため、MVVMになっています。 SwiftUI導入時にTCAも検討しましたが、特定のFrameworkに強い依存をしたくなかったのもあり、一旦見送っています。ただし、今後課題が出てきた場合は再考していく予定です。

Model部分を別モジュールとして切り分けています。これについて詳しくはこちらのブログをご覧ください。 tech.connehito.com

ツール

swift-formatやSwiftLintが開発中に自動で実行されるようにしています。

Renovateをつかってライブラリを定期的にアップデートする仕組みを導入しています。

iOSエンジニアの数は2名で、project fileでコンフリクトすることはあまりないためXcodeGenは導入していません。

デザイナーとのデザインのやりとりはFigmaをつかっています。

テスト

Quick/Nimbleを使ってユニットテストを書いています。

全てにテストを書いているわけではなく、APIの通信周りは必須、ViewModelやModelは推奨としています。

テスト時の依存性注入のためにDIのライブラリは導入していませんが、必要な箇所ではinitializerで注入しています。

CI/CD

CI/CDの実行環境としてはBitriseとGitHub Actionsを使っています。 Macでの実行が必要なケースはBitriseを使い、それ以外はGitHub Actionsを使うといった使い分けをしています。

テストの実行、AppDistributionでのアプリの配布、テスト端末の登録、App Storeへのアプリのアップロード、リリースノートの生成といったことを自動化しています。

今年度やった技術的改善

ママリの開発業務をやるだけでなく、技術的な改善にも一定の時間を使うようにしています。今年度は主に以下のようなことをやりました。

技術的な改善の今後

今注力している技術的な改善はAPIKitの置き換えとAPI通信周りをRxSwift/CombineからSwift Concurrencyへの置き換えです。

その後やりたいこととしては、Swift Package Managerの導入や画面の一部をSwiftUIへのリプレース、起動周りのコードの改善などがあります。

最後に

コネヒトではママリの機能開発をしながらも技術的な改善を進める時間も一定確保しこのような改善を進めています。

コネヒトでの開発に興味を持っていただいた方はカジュアルにお話しましょう〜
TwitterのDMなどでも大丈夫ですので@yanamura_までお気軽にどうぞ
日本中の家族をITの力で笑顔にしたい、iOSエンジニア募集! - コネヒト株式会社のモバイルエンジニアの採用 - Wantedly

コネヒトはPHPerKaigi 2023にシルバースポンサーとして協賛します!

こんにちは!高谷です。今回は弊社が協賛するPHPに関するイベントを紹介します。

PHPerKaigi 2023に協賛いたします

コネヒトではメインプロダクトである「ママリ」を始めとして開発のメイン言語としてPHPを活用しており、フレームワークとしてはCakePHPを採用しています(その他、技術スタックを知りたい場合はこちらをご覧ください)

その縁もあり、この度 PHPerKaigi 2023 にシルバースポンサーとして協賛させていただくこととなりました!

イベント概要

公式サイトからの引用になりますが、PHPerKaigiは

(ペチパーカイギ)は、PHPer、つまり、現在PHPを使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。 今年の開催はオフライン会場を軸としたオフライン・オンラインハイブリッド開催です。みなさんのご都合に合う参加方法をお選び頂けます。

となっており、いろんな方が楽しめる間口の広いPHPのイベントになっていそうです!今回ははじめてのハイブリッド開催なので、都合がつく方は両方で参加して楽しむこともできそうですね!

発表内容やタイムスケジュールを知りたい方はこちらをご覧ください!

タイムテーブル | PHPerKaigi 2023 #phperkaigi - fortee.jp

また、最新情報はTwitterで告知されるので PHPerKaigi 公式Twitterも要チェックです!

@phperkaigi

ブースも出します!

今年はコネヒトは企業ブースも出します! 企画としては

1. あなたは何問正解できる!? ママリドリル

2. システム開発・運用に関するアンケート

を予定しております! ママリドリルやアンケートにお答えいただくとノベルティのチロルチョコがもらえます! 可愛いチロルチョコになっているので、ぜひぜひご参加ください🙌

場所は会場内の研修室2で開催しております。

チケット購入

こちらからチケット購入できますのでよければぜひ!

www.eventbrite.com

最後に

ここまで読んでくださった皆様、ありがとうございました。 そして、 PHPerチャレンジ中の皆様、お目当てのPHPerトークンはこちらです!

#connehito-techvision

それでは!

コネヒトではPHPerを積極採用中です!ご興味がある方は気軽にご連絡ください!

www.wantedly.com

ママリ抽選会の裏側 〜CakePHPで排他処理を使って実装してみた〜

こんにちは。ママリアプリ開発チームでエンジニアをしております高橋です。

今日は3月1日よりリリースされているママリ抽選会の実装についてお話ししていきます。

ママリ抽選会とは

ママリ抽選会

ママリポイントを使用して参加できる抽選会のことです。

ママリポイントとはアプリ内で回答をしたり、ベストアンサーをもらったりすると付与されるポイントのことです。ただ現状でポイントを使用する用途があまりなく、ユーザーからのたくさんのお声とコネヒトとしてポイントを活用してユーザーに感謝の気持ちを伝えたいという背景から今回の「ママリ抽選会」プロジェクトがスタートしました。

テーブル設計

まずは簡単にテーブル設計を説明します。

以下は今回新規で作成した抽選会に関わるデータベースのER図です。

抽選会データベースER図

前提として開催期間に対して賞品それぞれに最大在庫があります。

在庫管理をするだけなら履歴テーブルは必要なく商品テーブルの最大在庫数を減らしていくという方法もあります。

ですが以下の懸念点を考えて最大在庫は固定でデータベースに保存するようにしました。

  • 本来の最大在庫がわからなくなる
  • 抽選会の参加履歴画面などが仕様が出てきた時に対応できなくなる

テーブル設計についてはそこまで複雑なことはないので本題の内部の実装についてお話しして行きます。

排他処理

排他処理とは簡単に説明すると「ダブルブッキングしないように制御すること」です。

前提として履歴を登録する前に次の抽選処理が始めると残在庫に誤差が生まれてしまうので順番に抽選する必要があります。 そこで排他処理を利用して正しく処理できるようにしました。

今回はテーブル全体ではなく行に対してロックをかけて実現しました。

SQLでは排他処理はこのように書きます。idが1のデータのみ行ロックされています。

select *
from table_name
where id = 1
for update; // 行ロック

CakePHPでは以下のように書くことができます。念の為キャッシュ削除をすると安心です。

$table->find()
    ->modifier('SQL_NO_CACHE')
    ->epilog('FOR UPDATE')
    ->first(); // 行ロック

実装の流れは以下のようになっています。

$user = $this->ユーザーテーブル->get($id);
try {
    $connection->begin(); // トランザクション開始
    $lottery = $this->抽選会テーブル->find('active')
                    ->contain(['賞品テーブル'])
                    ->modifier('SQL_NO_CACHE')
                    ->epilog('FOR UPDATE') // 行ロック
                     ->first();
    // 参加資格があるかバリデーションをする
    // 抽選をする
    $connection->commit(); // トランザクション終了
} catch (Exception $e) {
    $connection->rollback();
    throw new InternalErrorException($e->getMessage());
}

よくある排他処理は複数のユーザーが同じデータを更新できないように制御していくイメージですが、今回に関しては抽選処理自体を止めたいので1番最初の抽選会テーブルのデータを取得するところでを制御する必要があります。

トランザクションを開始した後にselect文にfor updateを指定すると行ロックが発生し、この間は順番待ち状態になります。

トランザクション終了と共に解放され順番待ちしていた処理が開始する仕組みです。

これでどんなにリクエストが集中しても安全に順番に処理できます。

最後に

私は入社後大きな新機能実装は初めてだったので、テーブル設計、API設計、API実装、テストコード、レビューを一貫してやることができてとてもいい学びになりました。

設計をしていく上で難しいのが変更のしやすさだったり、どれだけ先を見通すかはすごく悩むポイントでした。

またチーム開発をしていく上で実装に入る前にモデリングだったり、設計の認識を合わせるということが大切だなと思いました。

色々大変なことはありましたが、新機能をリリースできたことはとても嬉しく思います。自分の中ではかなり大きな経験だったので次に活かしていきたいです!

【秘話】ママリ抽選会のリリース🎉までにデザイナーがやってきたこと↓

note.com

コネヒトでは一緒に働く仲間を募集しています! そして興味持っていただけた方は気軽にご連絡ください! https://www.wantedly.com/companies/connehito/projects

カオナビさんと合同LT勉強会を開催しました!

こんにちは。2017年11月にAndroidエンジニアとしてjoinした@katsutomuです。

2023/2/16に株式会社カオナビの皆様と、オフラインの合同LT勉強会を開催しました!

経緯

昨年、コネヒト主催の勉強会に遊びにきてくれた、カオナビの富所さんに「所属してるエンジニアに刺激のあるイベントをやりたい」とお声がけして開催に至りました。

  • 登壇をして、その場でFBや反応をもらえること
  • 改まった場ではわからない、社外のリアルな声を聞くこと

この2つを刺激としてインプットし、成長につなげる場を作ることを目指し、まずはクローズドに交流&LT会を実施することになりました。*1

イベントの内容&趣旨

何度かカオナビさんと打ち合わせをさせていただき、以下の内容となりました。

  1. 全員自己紹介タイム
  2. 懇親会
  3. LT大会

当日の進行はカオナビさんにお任せして、弊社としては非常に助かりました。ありがとうございます!

会場

今回は、カオナビさんのオフィスに遊びに行かせてもらいました。 東京タワーが見える素敵なイベントスペースで、心地よく交流とLTの時間を過ごせました!

交流会

エンジニア業務のお話だけでなく、採用の取り組みであったり、組織カルチャーの話など、社内のコミュニティだけでは得られない、貴重な話ができました。

LT枠

当日の飛び入りも含め、合計8名の発表が行われ、大いに盛り上がりました。

発表内容も、エンジニアリングな内容や、プロジェクト管理のツールの話や、インディー開発など多岐に渡り、どの発表からも刺激をもらいました。

実施後のアンケート

実施後に、参加した弊社メンバーにアンケートを実施しました。 結果として狙っていた、登壇でその場でFBや反応をもらう改まった場では得られない社外のリアルな声を聞くという点は、叶えられたかと思っています

アンケート結果

今回のイベントに期待してたこと(抜粋)

  • 他社のエンジニアと接点を持つ
  • 社外のエンジニアとの交流。社外LTの雰囲気が把握できる。
  • 自分でLTを発表すること
  • 社外にいるエンジニアの人とざっくばらんに技術的な話を楽しくしたい

定量評価

具体的なお声(抜粋)

  • 時間があれば飛び込みでもLTできる雰囲気がいいなと思いました。
  • 色々なテーマのLTがあって面白かった反面、テーマを絞ったLTもいいなーと思いました
  • 久々にリアルでエンジニアと話せたし、境遇似た人との会話は自分も楽しく周りも楽しんでいたように見えたのでよかった
  • クローズドな交流会だと色々とぶっちゃけた話ができるのがとても良かったです。

まとめ

クローズドなイベント開催がひさびさなこともあり、盛り上がるか心配していましたが、大盛況で終えることができました。関わってくれたみなさんのお陰です。本当にありがとうございます!

オンライン勉強会が主流になり失われていた、リアルな交流ができたと思っています。特にエンジニアになって2~3年の若手メンバーだと、経験する機会が少ない文化だと思いますが、得られることも多いので、今後も継続して開催していきたいと思います。興味のある方は、ぜひお声がけください!

*1:コネヒトにはインプットとアウトプットを支援する制度があります:https://tech-vision.connehito.com/program/smile.html

【iOS】GoogleSignIn v7.0.0へのアップデート対応方法

こんにちは!2023年1月にiOSエンジニアとしてjoinしました、yoshitakaです!

今後iOS関連の内容で定期的に発信していきたいと思いますので、よろしくお願いいたします!

ママリのアプリではログイン連携でGoogleSignInを使っておりますが、先日GoogleSignInのメジャーアップデート対応を行ったので、内容を簡単にまとめたいと思います。

Google Sign-Inとは

Google Sign-In とは、Googleが提供している認証系ツールで、スマホアプリやWebアプリの認証をGoogleアカウントでできるようにするものです。

developers.google.com

Google Sign-Inを使ってやっていること

ママリユーザーが自分のGoogleアカウントを連携させることで、ママリ内でGoogleアカウントでのログインを可能にしています。

内部の処理としてはユーザーがGoogleログインに成功後その認証情報をサーバで確認、連携済みの場合にママリへのログインを許可しています。

アップデートで変わること

v7.0.0リリースノートより以下3点の変更がされました

  • すべての構成を Info.plist で指定できるように
  • Swift Concurrency のサポート
  • API の改善

アップデートで対応したこと

  • clientIDを Info.plist で指定
  • API の改善による修正

の2点を対応しました。

GoogleログインからidTokenをサーバへ送る際の実装部分(対応前)はこのようになっておりました。

let config = GIDConfiguration(clientID: clientID)
GIDSignIn.sharedInstance.signIn(with: config, presenting: self) {
    [weak self] user, error in
    if error == nil, let idToken = user?.authentication.idToken {
        self?.loginViewModel.loginWithGoogleId(idToken: idToken)
    }
}

Info.plist の修正

まずは、clientIDの指定をInfo.plistに移します。

ドキュメントを参考にGIDClientIDの値を追加しました。

ソースコードを修正する場合はこちらを追加

<key>GIDClientID</key>
<string>${GOOGLE_SIGNIN_ID}</string>

今回はRelease,Develop, Localで値を変えたいため

Build SettingsのAdd User-Defined Settingより環境ごとに値を指定して使います

ログイン部分の修正

次にログインの実装部分を修正します。

Info.plistに追加したことでGIDSignIn.sharedInstance.signInclientIDを渡す必要がなくなりました。

また、ログインが成功したかどうかを表す新しい GIDSignInResult が追加されました。

以前user?.authentication.idTokenから取得していたidToken

signInResult?.user.idToken?.tokenStringとすることで同じ値が取得できました。

GIDSignIn.sharedInstance.signIn(withPresenting: self) { 
    [weak self] signInResult, error in
    if error == nil, let idToken = signInResult?.user.idToken?.tokenString {
        self?.loginViewModel.loginWithGoogleId(idToken: idToken)
    }
}

まとめ

APIの改善により他にも変わっている部分がいくつかありました。

この記事では修正が必要になった部分のみ記載しておりますが、公式ドキュメントに詳しくありましたので対応される際は確認してみてください!

developers.google.com

developers.google.com

ママリ iOSアプリのモジュール分割

こんにちは!コネヒトでiOSエンジニアをやっていますyanamuraです。

ママリのiOSアプリでモジュール分割を行いましたのでその内容について記載しました。

なぜモジュール分割

目的としては大きく2つありました。

1つ目はApp Extensionsをつくるのを楽にしたいためです。

以前ママリでApp Extensionsを使ったプロトタイプで作ろうとした際、App Extensionsからママリアプリの一部のコードを再利用しようとしたところ、依存関係の問題で芋づる式にたくさんのコードを取り込まなければならなくなりとても大変な思いをしました。App Extensionsで共通利用しそうな部分はモジュール化して分割しておくべきだったなという経験からモジュール分割しようという思いに至りました。

2つ目はSwiftUIのプレビューを速くしたいためです。

ママリのiOSアプリではSwiftUIを導入していましたが、Previewがめちゃめちゃ遅いというか場合によってはタイムアウトしてしまい使いものにならないといった問題がありました。モジュール分割することでこの問題を少しでも解消したいという狙いが当初ありました。しかし、MacをApple siliconのものにリプレースして、Rosetta2ではなくApple silicon上でXcodeが動くようにこちらで対応した結果劇的に改善されPreview問題は解決しましたので、こちらの目的は薄くなりました。

モジュール分割の方針

ママリのiOSアプリの構成は、CocoaPodsを使ってパッケージを管理しています。

SwiftPackageManagerを使ってマルチモジュールやパッケージを管理したいところですが、ステップを刻んで少しずつ進めていこうと思い、まず第一歩としてパッケージ管理はCocoaPodsのままで、モジュール分割はEmbedded Frameworkを用いる構成にしました。

モジュールの分け方

モジュールの分け方は、縦(機能ごと)に分けたり横(レイヤーごと)に分けるなどといろいろなやり方がありますが、ママリではアーキテクチャはMVVMになっておりView,ViewModelとModelで2分割しました。

この分け方であれば、目的が満たせる最低限の分割で、最初にはじめるにはよいのではと考えたためです。

モジュール分割する際に起こった問題

いきなりEmbedded Framework化すると変更量が半端ない

ママリの場合1万行くらいの規模をEmbedded Frameworkに切り出すことになったのですが、ビルドが通るようになるまでにかなりの修正が必要で、一旦はじめてしまうと終わるまで結構な時間を要することになります。モジュール分割のために普段の開発を完全に止めるわけではなく、その間もコードの編集が行われるため、同期が大変になります。

これを避けるために、一旦プロジェクト内のディレクトリ構成だけ分割し、Embedde Framework化する前に修正が必要なところ(依存関係の修正やpublic化など)を修正しました。これによりEmbedded Frameworkに実際に分割した際に必要な変更を減らし、スムーズにEmbedded Framework化できました。

structのpublic化

Swiftのstructにはmemberwise initializer(https://github.com/apple/swift-evolution/blob/main/proposals/0018-flexible-memberwise-initialization.md) というstructのpropertiesを初期化するinitializerが自動で生成されます。しかしこのinitializerの可視性はinternalとなっているのでstructをpublicにするとmemberwise initializerを使っていた場合はpublicなinitializerを手動で追加しなければなりませんでした。

手動の追加ですが、Xcodeの機能でstruct名を右クリックしてRefactor->Generate Memberwise Initializer使うとコードを生成することができます。

static framework/libraryのimport問題

アプリを動かすと以下のようなメッセージがログに表示され、simulatorで動かすとクラッシュするといった問題が発生しました。

Class PodsDummy_XXX is implemented in both /xxxBuild/Products/Debug-iphonesimulator/YYY.framework/YYY (0xfffffffff) and xxx/yyy.app/zzz). One of the two will be used. Which one is undefined.

これはアプリとモジュール分割したframework(dynamic)で同じstatic libraryに依存していた場合に発生しました。 Embedded Frameworkを作成するとデフォルトではdynamic libraryに設定されています。アプリとdynamic frameworkの両方で同じstatic libraryに依存するとシンボルの重複が発生してしまいます。

手っ取り早い解決方法がモジュール分割したframeworkをdynamic frameworkではなくstatic frameworkに変えてやることです。Build SettingsのMach-O TypeをStatic Libraryに変更することでstatic framework化することができます。ママリでは現状static frameworkにするデメリットはなさそうでしたのでこちらのやり方で対応しました。

もう一つの方法は、アプリ側のOTHER_LDFLAGSから重複するstatic libraryを消してリンクしないようにする方法です。 CocoaPodsの場合はPodsfileで頑張る必要がありそうでした。https://github.com/CocoaPods/CocoaPods/issues/7126

モジュール分割してよかったこと

よかったことは、責務があやふやなところが炙り出されたことです。 これまでもフォルダ構成で責務は別れていたように見えましたが、いざモジュール分割のためにファイルを振り分けてみると、これは本来こっちじゃないよねといったものや、Model側からのViewへの依存といった問題点などが浮き彫りになり、よいリファクタリングの気づきになりました。

また、差分ビルドの時間を時間を比較してみたところ、10秒くらい速くなりました。(一つメソッドを追加して計測)

差分ビルド時間(sec)
before 40
after 30

まとめ

今回モジュール分割をすることにより、目的は達成することができました。今後はSwiftPackageManagerを使った構成に移行していこうと思っています。


コネヒトでの開発に興味を持っていただいた方はカジュアルにお話しましょう〜
TwitterのDMなどでも大丈夫ですのでお気軽にどうぞ
日本中の家族をITの力で笑顔にしたい、iOSエンジニア募集! - コネヒト株式会社のモバイルエンジニアの採用 - Wantedly

Amazon RDS Blue/Green Deployments利用時に躓いたエラーやハマりポイントなどをまとめてみた

新年あけましておめでとうございます。

初詣への移動中、気がついたら年を越していたインフラエンジニア @sasashuuu です。

2023年1本目の記事をお届けしたいと思います。

本日は、Amazon RDS、Amazon Auoraでリリースされた新しい機能である Amazon RDS Blue/Green Deployments 利用時に躓いたエラーやハマったポイントがいくつかあったので、それらをまとめた内容を取り上げました。本機能を利用する方々の一助になれば幸いです。

はじめに

本記事は2022年12月時点で検証していた際の内容となります。記事公開の時点では当時と状況が変わっている可能性がありますので、最新情報等はAWS公式サイト(Using Amazon RDS Blue/Green Deployments for database updates 等)を参照してください。

Amazon RDS Blue/Green Deploymentsとは

Amazon RDS、Amazon AuroraにおいてBlue/Green デプロイが行える機能です。細かい仕様については触れませんが、ざっくりと機能利用時のワークフローを説明すると、下記のような流れです。

※Auroraの場合の例

  • 対象のAurora(Blue環境)から新しいリソース(Green環境)がクローンで複製される
  • Blue/Green環境間でレプリケーションが開始される
  • クラスターの識別子やエンドポイントの変更なしに、Green環境のリソースをスイッチオーバーで稼働中のBlue環境リソースと入れ替える

https://docs.aws.amazon.com/images/AmazonRDS/latest/AuroraUserGuide/images/blue-green-deployment-aurora.png ※画像は公式ドキュメント Overview of Amazon RDS Blue/Green Deployments for Aurora より転載

このように本番環境からステージング環境を作成し、レプリケーションにより同期、さらにスイッチオーバーでエンドポイントの変更などなしに新規Auroraリソースの本番環境への昇格までもおこなってくれるという便利な機能です。

詳細については 公式 を参考にしていただければと思います。

躓いたエラーやハマりポイント

前提

まず前提として、公式ドキュメント Limitations for blue/green deployments に書かれているように、以下の条件下ではそもそも本機能自体が利用できません。

  • Amazon RDS Proxy
  • Cross-Region read replicas
  • Aurora Serverless v1 DB clusters
  • DB clusters that are part of an Aurora global database
  • AWS CloudFormation

具体的な内容

そしてここからは、実際に本機能を使ってみて躓いたエラーやハマりポイントについてまとめます。ざっくり以下のような内容です。

  1. パラメータグループにおけるバイナリログ出力の有効化が必要(binlog_format ⇒ MIXED)
  2. パラメータグループ変更時の適用(リブート)漏れがあるとGreen環境を作成できない
  3. クラスタに紐づけているサブネットグループにおいて3az以上の指定が必要
  4. Green環境作成前のBlue環境に対する何らかの書き込み操作が必要

ひとつずつ解説していきます。

1. パラメータグループにおけるバイナリログの出力の有効化が必要(binlog_format ⇒ MIXED)

本機能はバイナリログを使用したレプリケーションを伴うので、バイナリログ出力を有効にしておく必要があります。クラスター用のパラメータグループの設定値に binlog_format があるので、その設定値を MIXEDにしておく必要があります。これを行わなければ下記のようなエラーが発生します。

Blue Green Deployments requires cluster parameter group has binlog enabled.

ちなみにこちらは公式ドキュメントの Preparing an Aurora DB cluster for a blue/green deployment で解説されていました。

Before you create a blue/green deployment for an Aurora MySQL DB cluster, the DB cluster must be associated with a custom DB cluster parameter group with binary logging turned on. For example, set the binlog_format  parameter to ROW.

ただ、上記の説明では ROW を例に出していますが、中には ROW だとうまくいかなかった事例などもあるそう(Aurora の Blue/Green デプロイで少し遊んでみた(Aurora MySQL v1 → v3 Blue/Green 失敗編))で、また、公式ブログ(新機能 – Amazon Aurora と Amazon RDS でのフルマネージド型 Blue/Green Deployments)でも MIXED を指定するように促していることから、MIXED を指定する方が良さそうです。

2. パラメータグループ変更時の適用(リブート)漏れがあるとGreen環境を作成できない

Blue環境側でクラスターパラメータグループを変更したのち(適用タイプがstaticのものなど)、本機能を使用してGreen環境を作成しようとすると、下記のエラーが出ることがあります。変更したパラメータグループが適用されていないために起こるエラーです。これは本機能利用以前の話(詳細はAWS公式ドキュメント パラメータグループを使用する などを参照)にはなりますが、失念するとGreen環境が作成できないため、対象クラスター内のインスタンスのリブートを忘れないように行う注意が必要です。

Blue Green Deployments requires writer instance to be in-sync with cluster parameter group.

3. クラスタに紐づけているサブネットグループにおいて3az以上の指定が必要

公式ドキュメント等での言及については見当たらずだったのですが、どうやらクラスターにアタッチしているサブネットグループには3つ以上のAZを指定しないといけないという仕様がある模様です。 Amazon RDSのBlue/Green Deploymentsを実験して気づいたハマりポイントまとめAmazon AuroraでBlue/Greenデプロイを検証する。 のブログの記事で取り上げられていたおかげで問題に対処することができました。

4. Green環境作成前のBlue環境に対する何らかの書き込み操作が必要

Green環境の作成前に、Blue環境で何かしらの書き込み処理を行わなければ、下記のエラーが発生してしまいます。

Correct the replication errors and then switch over.
Read Replica Replication Error - IOError: 1236, reason: Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file'

これは一般的にはレプリケーション先(Green側)が参照しようとするバイナリログがレプリケーション元(Blue側)に存在しないことで起きてしまうエラーであり、Amazon RDS Blue/Green DeploymentsにおいてAWS側の内部動作上の問題として認知されているそうです。(※サポートへ確認済み)

対策として、バイナリログの出力を有効にしたのち、Green環境作成前にBlue側のデータベースに対して何かしらの書き込み処理を行う必要があるとのことです。(任意のテーブルに任意のレコードINSERT等)

その他のエラーやハマりポイントについて

本記事で紹介した事例以外に他にもいくつか取り上げられているものがあるみたいです。下記のブログなどが参考になるので、併せて参考にしていただければと思います。

おわりに

Amazon RDS Blue/Green Deploymentsは、リリースされて日が浅い新機能ですので、オープンにはなっていない細かい部分の仕様などによる不具合が見られますが、これから改善されていく見込みかと思われます。弊社では本機能を使用し、Aurora MySQL 5.6 (Amazon Aurora MySQL Version 1)のEOL対応(詳細は Amazon Aurora MySQL 互換エディションバージョン 1 のサポート終了に向けて準備する などを参照)などに活用していければと思っている期待の機能です。皆様も機会があればぜひ利用してみてください。