以前,confdを使ったデプロイを紹介したけど,あれにいろいろとコメントが付いたおかげで,swarmによるデプロイが実現できた.
こういう話を探していたんだけど,前の記事を書いた時は全然わからなかったからね……. みんなswarmでいろんなノード追加して遊んでたからね…….
ありがたや.
続きを読む以前,confdを使ったデプロイを紹介したけど,あれにいろいろとコメントが付いたおかげで,swarmによるデプロイが実現できた.
こういう話を探していたんだけど,前の記事を書いた時は全然わからなかったからね……. みんなswarmでいろんなノード追加して遊んでたからね…….
ありがたや.
続きを読むみなさん,Docker使ってますか?
開発環境に導入する話はよく聞きますが,本番Dockerで運用してますか?
本番をDockerにする上で障壁になりそうなホットデプロイ.
普段,コンテナではなくインスタンス上で直接サービスを動かしている限り,そこまで苦労はしていないんじゃないだろうか. たとえば,Railsであればunicornなどは,graceful restartに対応している. そのため,デプロイの際にサービスを止めずにデプロイすることができるのは,当たり前のことに思える.
しかし,Dockerとなると,ポートという障壁が出てきて,一筋縄ではいかない. ECSのような楽な解決方法がある一方で,Docker swarmを使うようなシーンでは,やはり一筋縄ではいかないのではないだろうか.
続きを読むみなさん二段階認証ってちゃんと設定してますか?
俺はずっとGoogleAuthenticatorを使って二段階認証してました.
Gmailくらいならまぁ……という気がしてますが,AWSのアカウントはさすがに怖いしね!
二段階認証,大変良いのですが,認証設定をしたiPhoneなりAndroidなりがかなり大切になってきます.
まだ,他にログインできる人がいる,例えば会社で使ってるAWSアカウントなら,たとえ端末を紛失しても復旧はできるし,IAMアカウントを停止させることもできます.
ただ,個人で使ってるAWSだったり,Githubだったりって,端末を紛失したら復旧手段がないじゃないですか. もうサポート問い合わせしかない.
そうでなくとも,例えば端末を新しくした時に,二段階認証の設定をちゃんとやりなおしていないと,新しい端末ではログインできなくなったりします.
こういうのが最近怖くて…….
続きを読むみなさんさようなら.
最近ようやくRxSwiftを触り始めています.
RxSwiftでAPI通信を含むような動作をさせたいとき,何を使おうかと迷ったので,その話をします.
swiftで書けるってことは,別に NSURLSession
をそのまま使って rx_response
を呼んでもいいわけです.
ただ,そうはいっても世の中にはいくつかAPI呼び出し用のライブラリがあります.
Alamofireあたりはかなり有名ですよね. 前にアプリを作った時は,まだAlamofireが出てきていなくて,AFNetworkingを使っていました.
で,最近ではAPIKitというのがちょいちょい名前聞きますね. 名前聞くっていうか,高専時代の友人が作ってるんで,最初から知ってるんですけど,そういうつながりなしにも名前を聞くようになってきました.
続きを読むgolangが結構好きになりつつある. そんな中でも延々と悩まされ,設計を考え続けているのが,error型だ.
もしかしたら,あまりgolangでmysqlのような,がっつりSQLでできているRDBを使う人は少ないのかもしれない.
だけど,自分が作っているものが,がっつりWebサービスで,どうしてもRDBに近いところで動かさなきゃいけないので,こういうことで悩む.
func hoge(tx *sql.Tx) (e error) { defer func() { if err := recover(); err != nil { tx.Rollback() e = errors.New("unexpected error") } }() tx.Exec(...) }
どうしてもtransactionを使うとこういう処理を書かざるをえない.
もちろん, sql.Tx
は error
型を返すのだが,それ以外のところで,例えば nil
参照等が起こらない保証がない.
どうしても,そういうリスクは消し去ることが出来ない以上,どこかでrollbackを仕掛けておくべきだろう.
というわけで,しばらくこの方式で,transaction周りではrecoverで防いでいたのだが,いざ本当にrecoverしなきゃいけないようなエラーが起こった時に,困ったことに気付いた.
当然だった. 自分で握りつぶしてunexpectedにしているんだから,何が起こったのか全然わからなかった.
ただ,recoverした段階ではerror
自体は取得できているはずだ.
というわけで,苦戦してみた.
func hoge(tx *sql.Tx) (e error) { defer func() { if err := recover(); err != nil { transaction.Rollback() switch ty := err.(type) { case runtime.Error: e = ty case string: e = errors.New(err.(string)) default: e = errors.New("unexpected error") } } }() }
なるほど,こうすれば,何が起こったのかをerrorとして渡すことができる.
まぁ本当はすべてのエラーを渡したいところではあるが,recoverした段階では err
はただの interface{}
になってしまっているので,実際どんな型にキャストできるかはわからない.
なので仕方なく, runtime.Error
と,任意の errors
から出てくるエラーだけ拾っている.
まぁ,大抵のものは拾えたりする.
これは今でも悩みどころだ.
error
としてメソッドの戻り値にしてしまうのはとてもよかった.
ただ,メソッド呼び出しが何階層も深いところで起こっていて,最終的に呼び出し元のメソッドに error
型が渡ってきた時,はたしてそのエラーが,どのメソッドで発生したものかを特定することができるだろうか?
これはなかなか難しい問題だ. もちろんすべてのエラーについて,たとえばメソッド名を頭につけるような変更をしておけばいい.
ただ,これはこれで書く人間にやたら負担を強いるし,必ずしもstringにキャストできるerrorばかりではないので,あまり現実的とは言い難い.
また,エラーを通知することを考えると,いったいどの階層でエラーをハンドリングして通知すればいいのだろうか?
自作のメソッド,A, B, Cがあったとする.
A -> B -> C という階層順でメソッド呼び出しを行い,Cのどこかでエラーが発生したとしよう. error型の戻り値は,C -> B -> Aと伝搬する.
さぁ,果たしてエラーの通知はどこで行うべきだろうか?
Aでエラーの通知を行うと,Aから見た場合,「Bを呼び出したらエラーになった」という情報しか得られない. しかし,実際にエラーになったのはメソッドCの中である.
では,Bでエラー通知を行うとしよう.
そうすると,Bから見た場合「Cの呼び出しでエラーになった」という情報しか得られず,結局Cの中のどこがダメだったのかわからない.
やはりここはCでエラー通知を行おう. 戻り値を返す手前でエラー通知してしまえばいいのだ.
たしかに一見正しい情報を伝えることができる.
しかしその後のことを考えよう. Cでエラー通知をしたから,AもBもエラー通知をしなくても良いのか?
こうしてしまうと,Cについては良くても,たとえば,A -> Dのようなメソッド呼び出しがあった場合,Aは常にDがメソッド内でエラー通知しているかを確認しなければならない. メソッドの呼び出しが単純ならばそれほど困らないが,いろんな場所で使いまわされるメソッドであったり,逆にいろんなメソッドを呼び出すようなメソッドを作る場合,信じられないくらいの負担になってくる.
また,自身が呼び出し順序的に最下層のメソッドになっているという判定はどうしたらできるだろうか? これは非常に難しい問題になってきてしまうので,結局最終的には「全部のメソッドでエラー通知するしかない」となってきてしまう.
これはこれで合理的ではある. こうしておけば,エラーがどんな順序で起こったかがすべてわかるからだ.
まぁ行数が増えるのでまったくうれしくないのだが…….
しかし問題もある. goはgorutineを使ったスレッド実行がお得意だ. スレッド実行になった場合,スレッド1でCがエラーになって通知されたとしよう.同時にBもAもエラー通知をしてくる. ひとつのエラー発生で3回通知されてしまう.
さらにややこしいことに,スレッド2ではBでエラーが発生したとしよう.同時にAもエラー通知をしてくる.
ふたつのエラーで合計5回のエラー通知がされる.
これは非常にわかりにくい. その上,どのメソッドがどのコンテキスト(今回はどっちのスレッドで実行されていたのか)でエラーになったのかがわかりにくくなる.
これでは通知が来た時,いったいいくつの処理,いくつのスレッドでエラーが発生して,リカバリには何をしたらいいのかがわからなくなってしまう.
複数回通知しても,それらが同じ原因であるなら,同じコンテキストで発生したエラーであることを明示しておかないと,エラー通知の意味がなくなってしまう.
そうなると,じゃぁエラー通知の瞬間にgorutineの情報を得ようと考えるだろう.
しかしそれはできない.
できないんだ.
結局今のところ「エラー通知をうまいことする」というのは非常に難しい問題になっている.
やはり最上位階層で,「これがエラーだったら,別の処理にしよう」という段階で通知を仕込むのが最も適切だろう.
あと,最近思うことなのだが,panic自体はそんなに悪い手法ではないのではないだろうか?
例えばWebサーバを考えた時,panicしなければサーバの処理は継続される. もちろん,そういうレベルのtry-catcheであれば,error型の得意とするところなので,エラー処理を書けばいいだろう.
だが,goを元にした大抵のWebサーバフレームワーク(gojiやmartini)は,panicが起きても,スタックトレースを出して500を返して,次のリクエストを受けられるようになる. つまりサーバのプロセス自体は殺されない.
ということは,むしろエラー通知をしたいレベルのものであれば,error型で拾って,コントローラ層等で通知を考えたり,その後の処理を考えるよりは,panicしてしまって,panicの通知を考えたほうがいいのではないだろうか. スタックトレースが出るのであれば,どこでpanicされたのかは一目瞭然だし,任意のerrorを送ることもできる.
むしろこっちのほうが,通知やリカバリということを考えたら,使い勝手がいい方法なのではないだろうか.