降りつぶし.net~同期するWebアプリ・スマホアプリの開発・運用~

第4回 Webアプリとスマホアプリの同期

この記事を読むのに必要な時間:およそ 5.5 分

いくつかの落とし穴

実は,前述のユーザデータ同期のフローには,落とし穴があります。

  • A.スマホの時刻がWebアプリサーバとずれていた場合,同期が破綻する
  • B.複数のアプリで同じ時刻に同じ駅のユーザデータを異なる値で更新した場合,同期が行われず,各アプリに異なる値が残ったままとなる

これらは,それこそGmailやEvernoteやDropboxでは「あってはならない」レベルの問題です。この後,それらへの対応について,考察します(実は無対処という対応なのですが……⁠⁠。

なお,前述のフローには,トランザクション分離レベルにおけるPhantom readsが発生し得る部分があります。3~6の処理の間に,ユーザがWebアプリで新たな記録を挿入しコミット,その直後に7が処理された場合,その新たな記録がここに混入するのが,Phantom readsです。

しかし,降りつぶし.netのデータベーステーブルはMySQLのInnoDBです。デフォルトの分離レベルREPEATABLE READのままですが,InnoDBの場合Phantom readsが発生しないため,7においては,新たな記録は読み取られないこととなります。

一方,それはそれで問題があり,新たな記録がその同期での返信に含まれないことになってしまいます。これについては,常に「同期開始時刻」を操作の基準とすることにより解決できます。次回の同期実行時に取得されるデータは,前回の同期開始時刻以降のデータです。そして送信されなかった新たな記録は,まさに「前回の開始時刻以降」であり,スマホアプリに返信されることになります。

スマホの時刻ずれ

降りつぶし.netのスマホアプリの主たるユーザとして想定されているのは,日本の鉄道に乗下車し,その記録をスマホで管理したいと考えている人です。前回第3回のミニコラムのとおり,まじめに駅めぐりを行うことは時間との真剣勝負でもあり,そのユーザが使うスマホの時刻が大幅にずれていることはまず考えられません。

ただしそれでも,時刻が多少ずれることはあり得ます。そしてこれは,原理上は解決が困難な問題です。NTPと類似の実装を行えば,同期実行時の時刻をすり合わせることはできます。しかし,個々のユーザデータが更新されたときの時刻が,同時実行時の時刻と同じずれであるはずもなく,補正は不可能です。

しかし,数秒~多くて数分のずれが発生しているとして,その間に,複数のアプリに対して矛盾する記録を入力することは,本来の使い方からはあり得ないはずです。

以上の割り切りにより,この問題には対処しないことにしました。

同じ時刻での同時更新が反映されない

「同じ時刻」となる確率は,更新タイムスタンプの粒度によります。iOSにおいては現在時刻は倍精度浮動小数点値の秒単位で,またAndroidにおいてはミリ秒単位で取得できます。が,第2回で説明したとおり,WebアプリのMySQLでこの値を4バイト整数カラムに格納するため,全体としては秒単位の粒度しか確保できません。

ここをはじめから8バイトのミリ秒値として管理していれば,⁠同じ時刻」に複数のアプリで衝突するデータが入力されてしまうリスクは大幅に減少していたところです。しかしこれも,時刻ずれ問題で割り切ったとおり,アプリの性格からして,まず起こりえない状況です。なのでこちらも,秒単位の管理のままで何も対処しない,としました。

もし対応するならば,たとえば,ランキング機能などを実装する可能性があるWebサーバ側を「正」と考え,同時刻の矛盾データがあった場合はWebアプリのデータをスマホ側に上書きすれば解決できます。

同期実行のタイミング

これらの仕様上の割り切りとともに,同期の実行タイミングについても,仕様の割り切りを行っています。

ひとくちに複数の端末間でのデータの同期といっても,その実行のトリガにはいくつかのパターンがあります。設定された日時なり曜日なりに,バックグラウンドで定期的に自動実行される同期。あるいはユーザが能動的に指示することで実行される同期。その中間として,アプリ起動中にのみ定期的に自動実行される同期があります。

しかし,バックグラウンドでの定期的な自動実行は,iOSにおいて不可能なのです。

iOSでは,サードパーティアプリに許されているバックグラウンド実行は,おおまかに言えば,

  1. フォアグラウンドで実行されていたアプリがユーザ操作によりバックグラウンドに回された直後
  2. 予め設定したアラーム,またはプッシュ通知によって,画面および音などでその旨が通知され,ユーザがUI操作によりその通知に対する何らかの実行を選択した場合

の2つしかありません。つまり,ユーザの能動的な操作抜きに定期的な動作を実行することは不可能なのです。

よって,スマホアプリの同期動作は,常に「手動実行のみ」または「アプリがフォアグラウンドにある間の自動実行」となります。

「手動同期のみ」のメリット

降りつぶし.netでは,⁠自動実行は一切行わない」と割り切ることとしました。

また自身の話になってしまいますが第1回で書いたとおり,そもそも自分のために開発をはじめたソリューションなのでお許しください⁠⁠,筆者は旅行ブログでリアルタイムレポートを書いています。そしてそのブログ内には,ブログパーツとして,乗下車記録の表示が埋め込まれています。このとき,記事の追加に合わせて,乗下車記録を更新させたかったのです。これは手動更新でしか実現できませんし,また自動更新されると齟齬をきたすこととなってしまいます。

そしてこの割り切りにより,スマホアプリ側での同期実行中に,少なくとも同じスマホから乗下車記録を更新することができなくなりました。実はこれは,スマホアプリにとって,大変好都合なのです。

詳細は次回以降となりますが,スマホアプリ側のSQLiteでは,確立した接続は同じスレッドからしか呼び出せません。またスクロールやスワイプのたびにクエリが発生する地図アプリにおいては,そのクエリをメインUIスレッドで行うことは不可能です。そこで,データベース専用のスレッドを作成し,そのスレッドにキューを介してクエリを渡し,結果をメインスレッドで受け取る,という処理を行っています。

この処理を複数のスレッドを立てて複数走らせることは,スマホの限られたリソースの中では,できればやりたくありませんでした。それらの結果,データベースへの接続は1つとなり,アクセスはすべてシリアル化され,1つの処理の途中で次の処理を行うことができなくなりました。同期中の別操作は実質上ブロックされ,同期が完了した直後に実行されることとなります。

しかし同期実行を完全手動にすることにより,スマホの表示は同期実行用の画面となり,必然的に同期以外のデータベースアクセスが発生しないこととなったのです。

図6 Android用アプリ「降りつぶしroid」の同期実行中画面。ステータスバーに状態が表示される

図6 Android用アプリ「降りつぶしroid」の同期実行中画面。ステータスバーに状態が表示される

もちろん,⁠デメリットは承知のうえで,自動実行の可否をユーザが設定できるようにすべき」という考え方もあります。たとえば起動時やタブ(iOS⁠⁠・アクティビティ(Android)の切替時のみの自動実行とし,同期中のユーザ操作はさせないことにする,あるいは同期を長らくしていない場合にアラートで手動実行を促す等々,さまざまな仕様が考えられます。今後の機能追加の課題の1つです。

次回からスマホアプリ編

降りつぶし.net最大のキモである同期機能の紹介も終わり,次回からはスマホアプリ編となります。次回は,Webアプリの次に公開した,iPhoneアプリ「i降りつぶし」の紹介です。

ミニコラム・知力体力時の運

本稿執筆時点の2013年8月末時点で,筆者の乗下車駅数は9472駅。全国全駅乗下車達成まであと99駅となりました。

駅めぐりを本格的に初めてから7年あまり,いろいろなことがありました。不通の多い路線は統計上運休が多くならない時期を考慮。いざ不通区間に遭遇すれば,現場およびネットで得られる情報とノートPCにインストール済の時刻表ソフトをにらめっこ。平時でも,駅と駅の間をたとえば6kmを60分間で歩くなどあたりまえ,そこに突然の雨でしかも歩道のない国道だったりすると,トラックの風圧で傘もさせず,ずぶぬれになりながらもひたすら歩いたり。コストを安くすべく,鉄道営業規則を研究したり,乗り放題のきっぷが発売されたと知れば,青春18きっぷの利用とどちらがトクか,今後も残るのか,など考えながらも,数年先までのスケジュールを立てなおしてみたり。

まさに駅めぐりは知力体力時の運,それらすべてを使わないとできないものですが,さらに,そもそも「駅じたいのリスク」があるのです。幸いにして筆者はなんとかクリアできていますが,それももちろん「運⁠⁠。残り99駅をなんとか乗り切りたいものです。

リスクのあった駅一覧

降りるのに苦労した駅
JR北海道石北本線上白滝駅1日1往復しか停まらない
JR東日本山田線浅岸駅1日2.5往復しか停まらず周囲は無住区で熊出没の可能性
苦手だと苦労する駅
JR北海道石北本線下白滝駅駅前唯一の家屋で大きな犬が放し飼い,寄ってくる
JR九州吉都線鶴丸駅駅出入口の真横に猛然と吠える飼い犬
南海電鉄高野線紀伊細川駅駅に棲みつく猫18匹
JR東日本陸羽東線鳴子温泉駅温泉の硫黄臭(硫化水素+二酸化硫黄)
JR九州日南線福島高松駅牧草ロールが駅前に山積みで乳酸発酵臭
JR北海道函館本線昆布駅,目名駅,熱郛駅他2013年夏,カシワマイマイ大量発生でホームにも駅舎内にも大量の死骸
瞬間,死を覚悟した駅
大井川鉄道井川線奥大井湖上駅湖上の島の駅から唯一の橋を対岸に渡ったら熊と遭遇
JR西日本大糸線頸城大野駅周囲に人家のない無人駅で蜂に刺された

著者プロフィール

よねざわいずみ

合資会社ダブルエスエフ代表社員。学習塾講師やら芸能ライターやら劇団主宰やらいろいろ経て現在はよろず請負プログラマ。最近の開発はPHP,JavaScript,Java,MTプラグインなど。お仕事随時募集中。

Twitter:@yonezawaizumi

鉄道旅行ブログ:http://feelfine.blog.izumichan.com/