LCL Engineers' Blog

夜行バス比較なび(高速バス比較)・格安移動・バスとりっぷを運営する LCLの開発者ブログ

デバイスサイズが異なるシミュレータで一度にアプリを起動して動作確認の効率を上げる

モバイルアプリエンジニアの山下です。

iPhone Xが発売されて4ヶ月が経とうとしてますが、iOSエンジニアにとっては確認する端末がまたひとつ増え苦労しています。

Xcode 9からシミュレータ自体の並行起動は可能になりましたが、アプリの実行は1つのシミュレータでしかできないため複数の端末で動作確認する際は1つずつ手動で切り替えて実行する必要がありました。

この確認作業はとても面倒ですし、都度ビルド時間が掛かるため塵も積もって大きなタイムロスになってしまいます。

そこで今回はFacebookが公開している FBSimulatorControl に含まれるコマンドラインツール 『fbsimctl』 を利用し、ターミナルからワンライナーで全てのデバイスサイズのシミュレータを同時起動できるようにしてみたいと思います。

fbsimctl インストール

Homebrewでインストールします。対応バージョンは Xcode 8以降になります。

$ brew tap facebook/fb
$ brew install fbsimctl --HEAD

org.carthage.CarthageKit/dependencies/GCDWebServer: Operation not permittedというエラーが出た場合は、一旦 Carthageをアンインストールするとインストールできます。

$ brew uninstall --force carthage
$ rm -rf ~/Library/Caches/org.carthage.CarthageKit

実行手順

シミュレータの起動からアプリを起動するまでの手順です。

シミュレータを起動

fbsimctl listでシミュレータ一覧を表示します。

$ fbsimctl list
3DC0B025-D2E2-4A06-B2A6-E99AB36D7D67 | iPhone X | Shutdown | iPhone X | iOS 11.2 | x86_64
0EB218F9-673C-4C5E-8380-3A0AF2EDFBC8 | iPhone SE | Shutdown | iPhone SE | iOS 11.2 | x86_64
9B9C6D4A-4FB9-483F-8227-8FB41B8683F2 | iPhone 8 Plus | Shutdown | iPhone 8 Plus | iOS 11.2 | x86_64
6B626E57-C1E2-4787-BF6A-93728E4CDE95 | iPhone 8 | Shutdown | iPhone 8 | iOS 11.2 | x86_64
...

fbsimctl <UDID> bootでシミュレータを起動します。fbsimctl listで表示された一覧の先頭の文字列がUDIDで複数渡すことで同時に起動させることができます。

$ fbsimctl 3DC0B025-D2E2-4A06-B2A6-E99AB36D7D67 0EB218F9-673C-4C5E-8380-3A0AF2EDFBC8 6B626E57-C1E2-4787-BF6A-93728E4CDE95 9B9C6D4A-4FB9-483F-8227-8FB41B8683F2 boot

.appのパスを取得

シミュレータへアプリをインストールするためには.appファイルが必要です。
以下のコマンドでビルドを行い、生成された.appファイルのパスを取得します。

$ xcodebuild -workspace <ワークスペース> -scheme <スキーム> -arch x86_64 -sdk iphonesimulator -configuration <Configuration> -showBuildSettings | grep -m 1 "CONFIGURATION_BUILD_DIR" | grep -oEi "\/.*"

/Users/XXX/Library/Developer/Xcode/DerivedData/<プロジェクト名>-dtjybaoudfhaodfetewcfjjbd/Build/Products/Test-iphonesimulator

シミュレータにアプリをインストール

前で生成した.appファイルをシミュレータへインストールします。

$ fbsimctl install /Users/XXX/Library/Developer/Xcode/DerivedData/<プロジェクト名>-dtjybaoudfhaodfetewcfjjbd/Build/Products/Test-iphonesimulator/<プロジェクト名>.app

アプリを起動

Bundle Identifierを指定してアプリを起動します。

$ fbsimctl launch <Bundle Identifier>

f:id:lcl-engineer:20180124124758g:plain

シェルスクリプトを作成

上記の操作だけでもアプリの起動まで簡単に行えますが、シェルスクリプトを作成してさらにラクをしたいと思います。
プロジェクトルートに以下のシェルスクリプトを作成します。ファイル名はlaunch_app.shにしました。

#!/usr/bin

PROJECT_NAME=$1
SCHEME_NAME=$2
BUNDLER_NAME=$3
CONFIGRATION=$4

# `.app`のパスを取得
APP_PATH=`xcodebuild -workspace ./${PROJECT_NAME}.xcworkspace -scheme ${SCHEME_NAME} -arch x86_64 -sdk iphonesimulator -configuration ${CONFIGRATION} -showBuildSettings | grep -m 1 "CONFIGURATION_BUILD_DIR" | grep -oEi "\/.*"`

fbsimctl install "${APP_PATH}/${PROJECT_NAME}.app"
fbsimctl launch ${BUNDLER_NAME}

シェルスクリプトを作成したらシミュレータを起動し、以下のコマンドで実行します。

$ sh ./launch_app.sh <プロジェクト名> <スキーマ> <Bundle Identifier> <Configuration>

例:  sh ./launch_app.sh test-app test-app-dev com.test-app development

これで1つのコマンドでアプリのインストールから起動まで行えます。
起動するデバイスサイズが毎回同じ場合は bootコマンドも含めると良いですね。

その他のコマンド

fbsimctl は他にもいくつかのコマンドがあります。

アプリ再起動

$ fbsimctl relaunch <Bundle Identifier>

シミュレータを閉じる

$ fbsimctl shutdown

ヘルプ

コマンドについて詳しくは以下のコマンドで表示できます。

$ fbsimctl help

最後に

今回の運用を始めてから確認作業に掛かっていた手間と時間が圧倒的に減りました。
fbsimctlはXCTestにも対応しているため、同様の操作でデバイスサイズごとのテストも一度に行えそうです。

PostgreSQLで安全にテーブル定義を変更する

Webエンジニアの森脇です。

PostgreSQLで、サービス稼働中に安易にALTER TABLE等を実行すると、ダウンタイムに繋がることがあります。安全にテーブル定義を変更するために、弊社で気をつけている点を紹介します。

なお、本記事の内容は PostgreSQL 9.5.4 環境で確認しています。

PostgreSQLのロックについて

参照のみのテーブルに対して、ALTER TABLEを実行した場合でもダウンタイムに繋がることがあります。原因について理解するために、PostgreSQLのロックについて簡単に紹介します。

PostgreSQLでは、SELECTでも「ACCESS SHARE」というロックを獲得します。最も弱いロックですが、ALTER TABLE等で獲得される「ACCESS EXCLUSIVE」というロックと競合します。

これは、他のトランザクションでSELECTしているテーブルに対しては、ALTER TABLEが実行できないということを意味します。

セッションA セッションB
begin; begin;
select * from A;
alter table A add foo varchar; → 待ち状態になる
commit;
→ alter tableが実行される

また、ALTER TABLE中のテーブルに対して、SELECTを行うこともできません

セッションA セッションB
begin; begin;
alter table A add foo varchar;
select * from A; → 待ち状態になる
commit;
→ selectが実行される

ALTER TABLEを実行すると該当のテーブルに対しては、更新だけではなく参照も不可となり、サービス提供不能状態に陥る可能性があります。

安全にテーブル定義を変更するための注意点

「ACCESS SHARE」「ACCESS EXCLUSIVE」等のロックの時間を極力短くすることが、サービスの稼働に影響与えないために重要です。

トランザクションの範囲を限定する

前述したように、SELECTでもロックを取得します。バッチ処理であまり考えずにトランザクションを張ってしまうと、意図せず長時間「ACCESS SHARE」ロックを獲得してしまう可能性があります。必要な箇所のみに、トランザクションを限定できないか検討しましょう。

どうしても長時間ロックを獲得してしまう処理がある場合は、その処理が動いていない時にALTER TALBEを実行するようにします。

同一トランザクション内で、複数テーブルに対してALTER TABLEを実行しない

一つのトランザクション内で、複数のテーブルに対してALTER TABLEを実行すると、以下のようなケースでデッドロックが発生する可能性があります。

セッションA セッションB
begin; begin;
select * from A;
alter table B add foo varchar;
select * from B;
alter table A add bar varchar;
ERROR:  deadlock detected
DETAIL:  Process 1734 waits for AccessExclusiveLock on relation 5116933 of database 5116060; blocked by process 23.
Process 23 waits for AccessShareLock on relation 5116941 of 

※ Railsのmigrationを利用している場合に、一つmigrationファイルに複数のテーブルに対する変更を書いてしまうと、デッドロックが発生してしまう可能性があります。

実行コマンド別の注意点

いくつかのコマンドは、レコード数が大きいテーブルに対して実行すると長時間ロックとなってしまいます。主要コマンドについて、回避策を紹介します。

default制約付きのカラム追加

カラム追加は一瞬で完了しますが、default制約を設定すると、カラム追加時に全レコードの更新が発生します。ALTER TABLEの実行時間が長くなるため、default制約を設定したい場合は、コマンドを2回に分けて実行します。

  • 一度 default制約なしでカラムを追加
  • その後にdefault制約を付与する

CHECK制約付きのカラム追加

CHECK制約を設定すると、同じく実行時間が長くなります。NOT VALIDオプションを指定すると、検証をスキップできるため安全にカラムを追加できます。

NOT NULL制約の追加

既存カラムに対するNOT NULL制約の追加時にもテーブルスキャンが発生し、長時間となります。回避策としては、以下のように少し複雑な手順が必要です。

  • 既存テーブルをコピーし、新規テーブルを作成
  • 新規テーブルのカラムに対してNOT NULL制約を追加
  • テーブルをRENAMEし、新旧のテーブルを入れ替える

※ 常に書き込みが発生しているテーブルの場合は、データの整合性をとる仕組みが必要です。

Index追加

Indexの追加は書き込みをブロックしてしまいますが、CONCURRENTLYオプションを付与すると書き込みをブロックせずに追加可能です。

※ ただし、Index追加にかかる実行時間は長くなります。

VACUUM FULL / REINDEX

pg_repackを利用すると、安全に実行できます。

その他

以下の記事では他パターンも詳しく書かれていますので、是非御覧ください。

まとめ

弊社ではPostgreSQLで複数のサービスを運営していますが、今回紹介した方法を利用することで基本的には無停止でサービス稼働ができています。皆様の参考になれば幸いです

Zapier × ChatWorkでちょっと面倒な日々のタスクを自動化する

フロントエンドエンジニアの岡田です。

LCLのエンジニアチームでは、積極的にリモートワークを行っています。私も週に2日は完全リモートワークで、他の3日間も在宅勤務と会社での勤務を併用しています。

そこでエンジニアチームで大切にしているのが、チャットツールでの情報共有です。 たとえば、リモートワーク開始時には以下のような連絡をしています。 f:id:lcl-engineer:20180120064942p:plain

また、デイリースクラムもチャットで行っています。 f:id:lcl-engineer:20180119115843p:plain

たったこれだけですが、毎日やっていると、改善点が見えてきます。

改善したいこと

勤務時間の連絡

  • 勤務時間を打つのが面倒(メモ帳からコピー&ペーストしていた)
  • Googleカレンダーの更新を忘れる(連絡した勤務時間とカレンダーが違っていることがあった)

デイリースクラムの投稿

  • チャットルームを探すのが面倒(1日1回しか使わないルームなのでピン留めするほどではない)
  • 同じルームに全員で投稿するため、あとから自分の投稿を見直しにくい

そこで、この課題をZapierを使って解消しました。

Zapierとは

Zapierとは、異なるウェブサービスをつなげて実行することのできるサービスです。例えば、Googleカレンダーに登録した予定をChatWorkへ通知する、といったことが簡単に実現できます。いくつか制限はありますが、無料で使えます。 zapier.com

今回はZapier × ChatWork連携(フリープランの範囲)で、毎朝の連絡を自動化してみました。

準備

ZapierをChatWorkと連携させる方法は、以下のページに詳しく載っています。 (ChatWork APIの申請が必要です) blog-ja.chatwork.com

設定

とても簡単なので、画面に従って登録していくだけで完成します。 ここではポイントを説明します。

勤務時間の連絡

まずは、勤務時間の連絡の設定です。

Googleカレンダー

先にGoogleカレンダーを登録します。 以下のように設定しています。 f:id:lcl-engineer:20180118175653p:plain

Zapier

次はZapierの設定です。 1つめのTRIGGERにはGoogleカレンダーを選び、「Event Start」を選択します。 f:id:lcl-engineer:20180118161139p:plain

オプションは以下のように設定しています。 f:id:lcl-engineer:20180118165724p:plain Time Beforeを、40分前に設定しているのは、けっこうな頻度で遅延が発生するためです。 以前、30分以上遅く通知されたことがあったので、40分にしています。

Search Termにテキストをいれると、イベントのフィルタリングが可能です。ここでは「在宅」というキーワードの入ったイベントのみで絞り込んでいます。なお、キーワードはカレンダーのタイトル(summary)だけでなく、詳細部分(description)も検索してくれるのが便利です。

Test this Stepでは、設定内容を確認することができます。 「view your event」というリンクを押すと、取得したデータが表示されます。 f:id:lcl-engineer:20180118170646p:plain

ここで表示されない場合は、設定に問題のある可能性があります。 表示されない場合は以下を確認すると良いと思います。

  • 「Re-test Google Calendar to get another event」ボタンを押してイベントを再取得してみる

  • Edit Optionsページに設定した条件のカレンダーイベントが存在するか確認
    今回の設定の場合、40分以内に「在宅」というキーワードの入ったイベントがGoogleカレンダーにあるか

  • Googleカレンダーのテストをする
    以下の画面で「Test」ボタンを押します。ボタンが「success!」に変われば成功です。 f:id:lcl-engineer:20180118164921p:plain

ChatWork

イベントが取得できたら次はChatWorkに通知します。 設定画面は以下のようにしています。 f:id:lcl-engineer:20180119082016p:plain カレンダーのタイトル(summary)と詳細部分(description)、時間等を組み合わせてメッセージを作っています。 ChatWorkの絵文字も入れられます。

実際の投稿です。
f:id:lcl-engineer:20180120064616p:plain
24時間表記の方が好みですが、時間の形式が変えられないので諦めました。
絵文字が入って楽しそうな雰囲気になりました。

効果

これで以下の問題が解消されました。

  • 勤務時間を打つのが面倒
    →カレンダーイベントを繰り返し設定にすることで毎回入力しなくて良くなった
  • Googleカレンダーの更新を忘れる
    →予定変更時もGoogleカレンダーの更新忘れがなくなった

デイリースクラム

次は、デイリースクラムの設定です。

Googleカレンダー

本来の使い方からは外れてしまいますが、こちらもGoogleカレンダーを使います。その理由は、フリープランには、2つのサービスまでしか連携ができないという制約があるためです。1つはChatWorkを使うことが決まっているので、残り1つのサービスで以下の2つができなければなりません。

  • 特定の時間になったら実行
  • 任意のテキストを保存できる

この2つを満たすのは、Googleカレンダーしか思いつきませんでした。 Zapierの連携サービスはたくさんあるため、もしかすると他にもあるのかもしれません。

イベントは、以下のように設定しています。 f:id:lcl-engineer:20180119114152p:plain このイベントは、自分以外の人に見せる必要はないので、非公開にしています。

description欄にURLを入れる場合は、リンクを消しておく必要があります。
f:id:lcl-engineer:20180120071312p:plain
リンクが付いたままにしておくと、ChatWork投稿時にhtml形式で出力されて、崩れてしまいました。

Zapier

次はZapierの設定です。 こちらも「Googleカレンダー」「Event Start」を選択し、オプションは以下のように設定しています。 f:id:lcl-engineer:20180119114632p:plain

ChatWork

ChatWorkの設定画面は以下のようにしています。 f:id:lcl-engineer:20180119115351p:plain

ChatWorkのタグも入れています。

実際の投稿は以下のようになります。 f:id:lcl-engineer:20180119115843p:plain

効果

これで以下の問題が解消されました。

  • チャットルームを探すのが面倒
     →時間になると自動で投稿されるので探さなくて良い
  • 同じルームに全員で投稿するため、あとから自分の投稿を見直しにくい
     →カレンダーに入っているので確認しやすい

まとめ

Zapierは、設定が簡単で気軽に導入できるのが良いですね。 他にもいろいろなタスクを自動化していきたいです。