valid,invalid

関心を持てる事柄について

無職を経てSmartBank, Inc.に入社しました

掲題の通り、SmartBank, Inc に入社しました。

  • From: Quipper, Inc.
  • To: SmartBank, Inc.

smartbank.co.jp

2021 in review - valid,invalidに書いたとおり転職は2020年の出来事で、入社日は 2020-08-01。2021 の間違いではなく 2020 である。入社してから半年程度はステルスでプロダクト開発を行っていたため書くきっかけを見失ってしまい、以降ずっと執筆をサボっていた。

当記事は入社後 1 年半の振り返りではなく2020年のオファー受諾時や入社時に何を考えていたかを主に記述する。(が、結果として当時の期待やオファー受諾の決め手が概ね間違っていなかったことを追認することになった)

続きを読む

CLIでpull requestsをまとめてapproveするワンライナー

GitHub公式CLIのghを使って複数のpull requestsをまとめてapproveする。

まれに使うのでメモ。

前提

gh install済み、かつcurrent directoryが対象のrepositoryであること

コマンド例

Approve all pull requests in the repositrory

$ for pr in $(gh pr list --json 'number' -t '{{ range $i, $pr := . }}{{ $pr.number }} {{end}}'); do gh pr review -a $pr; done

Approve all pull requests with dependencies label in the repositrory

$ for pr in $(gh pr list --json 'number' -t '{{ range $i, $pr := . }}{{ $pr.number }} {{end}}' --label dependencies); do gh pr review -a $pr; done

環境

  • gh version 2.4.0 (2021-12-21)
  • zsh 5.8 (x86_64-apple-darwin19.6.0)

Advent of Code 2021 に Go で取り組んだ

存在は知っていたが取り組んだことなかったのでやってみたシリーズ*1の1つ、Advent of Code 2021に取り組んだ。

adventofcode.com

Advent of Code とは Advent Calendar にちなんで毎日2問ずつクイズが出題され、25日まで解かせるというイベントである。2016年ぐらいからやっているらしい。

結果、8割クリア

結果、独力で完答はできず、一部は自力で解くのを諦めてRedditの回答スレッドを見て写経して終わらせることになった。

解けなかったのは 20, 21, 22 の part2、および 18, 23, 24。全49問なので8割クリアといったところ。

前半の15 daysぐらいは楽勝な問題と良い歯ごたえの問題が並んでおり、1日15分~60分ぐらいを使って楽しんで取り組めた。アルゴリズム面よりも英語の問題文の読解に悩む問題のほうが多かったぐらいだった。

しかし後半、day16あたりから仕様理解やデバグの難しさが急激に上がり始め、day20以降は半ば諦めつつ「30分考えて良いアプローチができなかったら諦める」撤退戦略を取ることになった。プライベートな時間の制約もあったのでやむなしと言いつつもアプローチの仕方すら思いつかない問題があるのには力不足を感じずにはいられない。

f:id:ohbarye:20211230154043p:plain
この絵、何?ってずっと思ってたけど終わってようやく理解した

使用した言語は Go

使用したメイン言語は2021年最後のクォーターあたりから仕事で書くようになったGo。習熟度を試す意味合いもあるし、仕事ではWebアプリケーションやバッチ処理を書いているので、そこからちょっと離れてアルゴリズムの書き味はどんなものか知りたかったというのもある。

最近あまりやっていないけど競技プログラミングRubyで取り組んでいたので、そことの比較でいうと…まぁ、ちょっとした便利utilみたいなものを用意しないと辛いかな…というのは思ったが、だいたい使うイディオムやデータ構造は決まっているのでそこまででもなかった。

どれか忘れたけどゼロ値が便利に使える問題だとクールにGoをキメられた感が出て良い。

特段良かったところはコードの変更がとても簡単なところ。僕の場合、こういった趣向で探索的にプログラムを書いていると変数名やインタフェースがひどいコードを書くことになる。一筆書きで正答できればそれで良いのだけど、一発でうまくいかずにアルゴリズムや使用するデータ構造を変えるにあたり、IDEの支援が効くのはめちゃめちゃ便利だった。致命的なミスはコンパイル時に死んでくれるので、ある程度対象を絞り込んだ状態でできるデバグの体験も良かった。

モチベーション

後半は問題を読解するのも辛い時もあったが、Twitterで回答する人々(日本語)を見たり、(去年のだけど)完答しましたブログを見たり、Redditのネタスレクールなvisualizationを見るために問題文を読む気持ちを奮い立たせたりしていた。

来年の抱負

来年は9割を自力で正答できるところまでいきたい。

力量不足は感じたが今年学んだ言語で取り組めたことは良かったので、来年も新しい言語でやれると良い気持ちになれるかもしれない。

*1:正確に言うとAoC 2020が終わったあとに存在を知ってちょっと取り組んでみたけど出遅れてアドベント感が薄くて続かなかった

Node.js/React周辺プロジェクトステータス (2021年12月時点)

個人的な観測範囲にある Node.js / React 関連のプロジェクトなどについて 2021 年 12 月時点の情報メモ。

ここ 2 年ぐらいコンシューマー向けの SPA や Node.js アプリケーションを書いておらず、react-admin で作った管理画面*1を書いているぐらいなのだが、久々にいろいろ見たらステータスが変わっていたので。

Node.js

https://nodejs.org/en/about/releases/

着々と version up してる。

  • v10 は 2021-04-30 に EOL
  • v14 が 2021-10-26 に Maintenance LTS になった
  • v16 が 2021-04-20 にリリースされ、2021-10-26 に Active LTS になった

GitHub Actions の hosted runner でも v16 がデフォルトになった。

https://github.blog/changelog/2021-12-10-github-actions-github-hosted-runners-now-run-node-js-16-by-default/

yarn

https://yarnpkg.com/

2020 年には v2 が出ているが、npm install yarnすると未だに yarn v1 がインストールされて若干混乱する。v1 はメンテナンスモードであり、今後は修正されないバグもあるとのこと。

yarn を node.js 本体に入れようという議論もあったが却下され、corepack が v16.10 から本体に含まれるようになった。

Cybozu Frontend Monthly #10 に詳しい。

Migration | Yarn - Package Manager 見つつ v2 への migration やらないとな〜

npm

https://docs.npmjs.com/cli/

v8 系が最新。

npm は v7 から peer dependencies の解決アルゴリズムが若干変わったようで、これまで許容していた version mismatch がエラーになったりする。

GitHub Actions のubuntu-latestイメージの更新で CI が何もしてないのに壊れた…状態になって知ることになった。

lerna

https://github.com/lerna/lerna

monorepo で複数のパッケージを管理するツール。有名どころだと Babel などを筆頭にけっこう多くのプロジェクトで使われていた気がする。

メンテナが monorepo に強い失望感を示していて厳しそうな感じ。

https://github.com/lerna/lerna/issues/2975

You should use something else, or, y'know, stop bothering with monorepos entirely, they were a mistake.

Babel もいつの間にか lerna を止めていた。

https://github.com/babel/babel/pull/12138

react

https://github.com/facebook/react

2020年10月におとなしめのv17がリリースされてからはしばらく落ち着いている。

v18 では Suspense を使って面白い感じになるらしいが、業務で SSR する機会はあまりないかも。

enzyme

https://github.com/enzymejs/enzyme

React 17 への対応イシューが open のまま滞っている。イシューを watch していると、フリーライダーからの心ないコメントが目に付くこともありメンタルヘルス的にも芳しく無さそう…。

そうこうしている間に React 18 対応を push する issue が出てきた。

https://github.com/enzymejs/enzyme/issues/2524

enzyme のことは応援しているが業務のコードからは削っていっている…

create-react-app

https://github.com/facebook/create-react-app

Meta (Facebook) がサポートしているというのがウリ文句だった気もしつつ、v4.0.3 が 2021-02-22 に出てからしばらくリリースがない。

メジャーアップデートは 2020-10-23 の v4.0.0 が最後で、issues はかなり溜まっていてさばけていない印象。

react-admin v4 に見る変化

大部分 react-admin が隠蔽してくれちゃっているので、ここ最近は細かいライブラリやツール群をあらかた気にせずともやってこれたというのもある。

関連: React Adminの感想 - valid,invalid

そんな react-admin が 2022 年に v4 を迎えるにあたり内部での大きな変更を予定している。依存ライブラリもガッツリ変わるようなので 4.0 Roadmap · Issue #5933 · marmelab/react-admin · GitHub を見ることで各種ライブラリの趨勢を垣間見ることができる。

connected-react-router

https://github.com/supasate/connected-react-router

本体は継続的に更新されてはいるが react-admin が Redux 依存を弱めていきたいオーラを出しているので取り除かれるようす。

redux-saga

https://github.com/redux-saga/redux-saga

直近も commit は積まれているが Latest release が 2019 年。

react-admin は v4 になる前から内部的には redux-saga を使っていない状態になっており、利用者がカスタムコードを足せるようになっていただけだった。

react-final-form

https://github.com/final-form/react-final-form

final-form 起因で直せないバグがあること、メンテナンスに不安があることを理由に脱却が決められていた。

formik が移行先として挙がっていたが最終的には react-hook-form になったようだ。

ちなみに移行先の候補に上がっていなかったけど redux-form も"unmaintained"と評されていた。

React のフォームライブラリ戦国時代あったな〜

redux

https://github.com/reduxjs/redux

先述の通り依存を弱めていきたい感じであり、クエリキャッシュも Redux ベースではなく react-query へ移行するとのこと。


新規に SPA をゼロから作ろうとするときに「昔つかったことあるから…」みたいなことを言わず、この辺のツール / ライブラリ選定の選球眼を改めて磨きたいところ。

*1:管理画面の構成は以前に書いた通りこんな感じ => https://ohbarye.hatenablog.jp/entry/2021/01/22/admin-feature-architecture-patterns

Kaigi on Railsに登壇します

2021-10-22 (土), 23 (日)に行われるKaigi on Railsに登壇します。

2日目の23日 13:50~14:20 の30分枠で "Safe Retry with Idempotency-Key Header" という題にて発表します。あと3週間ですね。

イベント参加はこちらから↓

kaigionrails.doorkeeper.jp

私の発表概要はこちらで見られます↓

kaigionrails.org

タイトルの通り、安全なリトライの実現手段である Idempotency-Key Header の話をします。

冪等性とリトライの話は皆さん大好物だと思うのですが、すでに一通り語られているな〜という印象もありました。そこで、今回は比較的ニッチでまだ語り尽くされていないところを狙い、IETFIdempotency-Key Headerの提案仕様を核にすえた構成でプロポーザルを出してみたところ採択いただきました。

提案仕様自体は当然ながら実装言語を問わないものですが、業務を通じてRuby実装を読んだり書いたりしたので、RubyRailsの話とブレンドすることで内容に"コク"が出ることを企図しています。

この試みが吉と出るか大凶と出るかは当日にお確かめください。

ちなみに以前にも軽くまとめ記事を書いたのは"伏線-ジャブ-"でした。

ohbarye.hatenablog.jp


ところで、色々ありしばらく沈黙していたのですが、今回が現在の所属社名を出しての初めての登壇になります。

登壇は個人的な活動ではありますが会社としてもサポートしてもらっているし、何より業務で取り組んだ成果の発表でもありますので、採用のやっていきも見せたいところです。

smartbank.co.jp

弊社ないし同発表に興味が湧きましたら、カジュアル面談の応募フォームからIdempotency-Key Header付きのリクエストお待ちしています。*1

smartbank.co.jp

*1:たぶんリクエストの数だけ応募レコードが作られます

Idempotency-Key Headerの現状・仕様・実装の理解を助けるリソースまとめ

Idempotency-Key Header に関する調査と実装を半年ぐらい前に行ったので、そのとき参考にしたリソースと 2021 年 9 月時点で得られる最新情報をメモしておく。

前提: Idempotency-Key Header とは

HTTP リクエストのうち冪等ではないとされるリクエス*1を冪等にし、安全なリトライを可能にするための仕組みの 1 つ。

Jayadeba Jena, Sanjay Dalal, Erik Wilde 氏らによって 2021 年 11 月に仕様が提案された。現在は IETF のもと、インターネット標準化過程(Standard Track)にあり、IETF Meeting や GitHub issues にて議論が行われている。real world ではすでに多くの企業で類似する実装が行われている。

現状と今後

GitHub issues を見る限り論点はいくつか残っており、RFC になるにはまだ時間がかかりそう。

また、de facto と言えるほどサーバ側の実装パターンはまだ固まっていない印象。実装する際には後述する Airbnb や Stripe の記事などを参照しつつ、各実装者が多少なり手心を加えて設計をしなければならなさそうだ。

今後の最新議論を追いかけるなら GitHub issues や IETF meeting をウォッチすることになると思う。


ここからリソースまとめ。

仕様を説明するもの

The Idempotency-Key HTTP Header Field

https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-00

2021-07-01 に提出された最新の draft。IETF HTTPAPI WG によって accept されたもの。 ちなみに初出は 2020-11-17*2

解決したい課題、API サーバおよびクライアントに期待する振る舞い、先行する実装等々、全体像を掴むのに十分な情報がある。

POST リクエストを冪等処理可能にする Idempotency-Key ヘッダの提案仕様

https://asnokaze.hatenablog.com/entry/2020/11/19/015243

2020 年 11 月、初期の draft 公開を受けての記事。日本語でわかりやすい簡潔な説明。

2021 年 9 月時点では日本語で "dempotency key header" を検索したときに最上位に来るのが当記事。

ちなみに、そのあと英語の記事がいくつか続いた後に僕の Scrapbox のページが来るぐらいに情報が少ない。英語で検索しよう。

2021-09 時点での最新議論

IETF 111 Meeting の発表資料

https://datatracker.ietf.org/meeting/111/materials/slides-111-httpapi-idempotency-header-00

2021-07-26 の IETF 111 Meeting 資料。現在のステータスと議論の状況をざっと掴める。

Discussion on GitHub

https://github.com/ietf-wg-httpapi/idempotency

draft を受けての各界からの反応・意見・課題について議論している repository。

いくつかななめ読みしてみる。

#2 Clarification for status code for various scenarios

各シナリオで返す status code や命名について。「英語が母国語じゃない自分としては〜」のような意見が寄せられるのも面白い。

#3 Feedback from Google Standard Payments

protocol agnostic な実装であるべきでは?と Googler からの指摘。個人の意見として書いているようだが、gRPC を推す Google 側の position talk と見られる節はある。

#5 How does this header compare with OASIS Repeatable Requests Header?

類似する実装として OASIS (Organization for the Advancement of Structured Information Standards) によるRepeatable Requests Headerが存在するがどう使い分けるか、歩調を合わせるか、はたまた統合の道を辿るか。

各種 SaaS の先行実装

IETF なのですでに実装 (Running code) がある*3

draft の "4. Implementation Status" に記されているものを参照するとよい。

https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-00#section-4

主に決済・金融系の SaaS が公開する API で実装されている。先行実装にはIdempotency-Key の名前を使うものもあれば、Request-Idと呼んでいるものもある。

SaaS で実装されている、いくつか代表的なものを挙げる。

Stripe

https://stripe.com/docs/api/idempotent_requests

動画もあった。

https://www.youtube.com/watch?v=nnMqSQtSZUQ

各言語の client 実装があるが、いずれも利用者が Idempotency Key のことを知らなくても自動でセットするようになっている。知らなくても安全にリトライできる良い設計と思う。

もちろん、key のことをよく知る実装者であれば form 入力値などによって「一意性」を決定し、key を生成できる。

以下は Ruby client の例。

https://github.com/stripe/stripe-ruby/blob/v5.38.0/lib/stripe/stripe_client.rb#L869-L873

      # It is only safe to retry network failures on post and delete
      # requests if we add an Idempotency-Key header
      if %i[post delete].include?(method) && config.max_network_retries > 0
        headers["Idempotency-Key"] ||= SecureRandom.uuid
      end

Amazon Pay

https://developer.amazon.com/ja/docs/amazon-pay-api-v2/idempotency.html

日本語で読める。

Note: 冪等キーは各 Amazon Pay 事業者アカウントに固有のものであり、無期限に保存されます。これは、同じキーを異なる事業者アカウントに使用できることを意味します。

key が無期限に保存されるタイプ。

draft によれば、expiry を設定するかどうかは任意。

2.3. Idempotency Key Validity and Expiry

The resource MAY enforce time based idempotency keys, thus, be able to purge or delete a key upon its expiry. The resource server SHOULD define such expiration policy and publish in related documentation.

PayPal

https://developer.paypal.com/docs/business/develop/idempotency/

Idempotency-Key header の draft を書いたうちの 1 人が PayPal のメンバーなのだが、PayPal 自身はPayPal-Request-Id header を使っているというのが興味深いポイント。PayPalには"歴史"がありそうだ。

実装するときに参考になるリソース

各種 SaaS の仕様は参考になるが、当然ながら、記されているのは外から見た振る舞いの定義のみ。

仕組み自体はシンプルなので draft を読み込めばコア部分はわりとすんなり実装できるものの、細かい点において迷うポイントがあった。key を保存するストレージに何を選ぶか、エラーシナリオで返す status code は何か、など。

いざ自分で実装してみようと思ったときに参考になる記事をいくつか。

Designing robust and predictable APIs with idempotency

https://stripe.com/blog/idempotency

2017 年、@brandur が Stripe 在籍時に書いた記事。分散システムにおいてなぜ idempotent な HTTP リクエストが重要か、といった基本思想のおさらい。

加えて、クライアントと API を設計する際に従うべきいくつかの基本原則。

  • 障害が一貫して処理されるようにする - クライアントにリモートサービスに対する操作を再試行させる。そうしないと、データが一貫性のない状態のままになってしまい、将来的に問題が発生する可能性がある
  • 失敗が安全に処理されるようにする - idempotency および idempotency key を使用して、クライアントが一意の値を渡し、必要に応じてリクエストを再試行できるようにすうる
  • 失敗が責任を持って処理されることを確認する
    • Exponential Backoff や jitter を使って、立ち往生している可能性のあるサーバに配慮する

先述した Stripe の Ruby client の実装を読むと Exponential Backoff や jitter のお手本が見られる。

Implementing Stripe-like Idempotency Keys in Postgres

https://brandur.org/idempotency-keys

これも 2017 年の@brandur の記事。

記事はとても長いのだが Sinatra で書かれた実装が公開されており、この実装をベースに解説しているので非常に参考になる。

https://github.com/brandur/rocket-rides-atomic

同実装では key のストレージとして RDBMS を使用している。これは key の保存とリソースの状態の更新が atomic でなければならないため。リクエスト単位で atomic なのではなく、1 リクエストの中で atomic な処理が複数ある複雑なケースではそういうこともある。

また、リクエストハンドラの中だけでなく idempotency key の reap (expire したレコードの削除) や completer (中途半端に終わったリクエストをあとで非同期に完了させる) の実装も参考になる。

Patterns of Service-oriented Architecture: Idempotency Key

https://multithreaded.stitchfix.com/blog/2017/06/26/patterns-of-soa-idempotency-key/

2017 年に concept を解説している記事。トレーサビリティへの言及が良い。

  • とある idempotency key が既存のトランザクションを見つけるために使用されたログを記録しておく
  • サービスがいつ既存のレコードを見つけたのか知ることができる
  • 不適切に設計された idempotency key algorithm を使っていることを発見できる

Good diagram.

Avoiding Double Payments in a Distributed Payments System

https://medium.com/airbnb-engineering/avoiding-double-payments-in-a-distributed-payments-system-2981f6b070bb

2019 年の Airbnb による記事。分散システムに結果整合をもたらすテクニックのうち、write repair を実現するのが Idempotency-Key である、という導入から始まる。

Airbnb が実装している Orpheus と呼ばれる Java library と設計の基本理念が参考になる。API call を3つのフェーズ(Pre-RPC, RPC, Post-RPC)に分けて考え、API が DB に書き込むのは Pre, Post-RPC フェーズのみとし、APIがさらに downstream や external services (payment processor や bank) にリクエストする部分を RPC とするあたりなど。

他にも、クライアントのリトライや Idempotency-Key の生成方法、key を DB から読み出すときは必ず master を参照する、などのテクニックが詳説されている。

Idempotency-Key IETF standards draft

https://brandur.org/fragments/idempotency-key-draft

2021 年 7 月、 https://news.ycombinator.com/item?id=27729610 を見ての@brandur の記事なので比較的新しい。HackerNews と合わせて読むと面白い。

Googler のコメントでは「ヘッダーではなくペイロードに入れたほうが良い」と主張があり、これは gRPC との相性のためと@brandur は見ている。

@brandur は「リクエストボディと分離しておくことで、クライアントが意識せずに idempotency を実現できる」と反論している。

実際に Stripe の API クライアントはそのように実装されているので説得力がある。

その他

国内ではメルペイから数多くの知見が公開されている。


各リソースを再訪して細かくコメントしていたら本記事が思ったより長くなってしまった。全体をもう少し整理したら薄い本にできるような気もする。

*1:https://datatracker.ietf.org/doc/html/rfc7231 , https://developer.mozilla.org/en-US/docs/Glossary/Idempotent あたり参照

*2:https://datatracker.ietf.org/doc/html/draft-idempotency-header-01

*3:IETF の仕様策定プロセスではラフコンセンサス。仕様より先に複数の独立した実装があることがほとんど。 https://www.nic.ad.jp/ja/tech/ietf/section4.html