<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[ARIGATOBANK Tech Blog - Medium]]></title>
        <description><![CDATA[ARIGATOBANKエンジニアが書き綴るテクニカル記事集。思想や技術選定、課題や取り組みなどについて発信していきます。 - Medium]]></description>
        <link>https://medium.com/arigatobank-tech-blog?source=rss----41cd1e4ce434---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>ARIGATOBANK Tech Blog - Medium</title>
            <link>https://medium.com/arigatobank-tech-blog?source=rss----41cd1e4ce434---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 03:24:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/arigatobank-tech-blog" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Google Cloud 認定 Professional Cloud Architect 合格記]]></title>
            <link>https://medium.com/arigatobank-tech-blog/google-cloud-%E8%AA%8D%E5%AE%9A-professional-cloud-architect-%E5%90%88%E6%A0%BC%E8%A8%98-4f007cb53d73?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/4f007cb53d73</guid>
            <category><![CDATA[google-cloud]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Masahiro Maruyama]]></dc:creator>
            <pubDate>Mon, 11 Mar 2024 07:26:22 GMT</pubDate>
            <atom:updated>2024-03-12T05:02:49.986Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*t0Yv03OGWVkM_aCl_4lllQ.jpeg" /></figure><p><strong>〜2度に渡る受験と合格までの経緯〜</strong></p><p>こんにちは。ARIGATOBANK の丸山です。主にバックエンド開発を行っております。</p><p>Google Cloud 認定 Professional Cloud Architect 試験に合格しましたのでそれまでの経緯、勉強内容や感想などを綴りたいと思います。</p><h3>Professional Cloud Architect とは</h3><p>公式から引用しますと、</p><p>「Professional Cloud Architects は、組織が Google Cloud 技術を利用できるように支援します。クラウドアーキテクチャと Google Cloud に関する専門的な知識を活かして、ビジネス目標を実現するために、スケーラブルで高可用性を備え、堅牢かつ安全な動的ソリューションを設計、開発、管理します。」</p><p>とのことです。</p><p>詳しくは<a href="https://cloud.google.com/learn/certification/cloud-architect?hl=ja">公式の案内</a>をご参照ください。</p><h3>試験を受けたきっかけ</h3><p>本試験を受けるきっかけとしてはGoogle Cloud Japan 主催の Google Cloud Innovators Gym Japan (G.I.G Japan)というGoogle Cloud のスキル習得のための招待制のプログラムを受けた事から始まります。</p><p>上記プログラムでは主に Cousera で提供される学習コースを受講し、期間中の各セッションで学習内容の説明や質疑応答などでスキルや知識を深めます。</p><p>最終的に以下のいずれかの認定資格の取得を以てプログラム修了と認められるため、資格の取得を目指しました。</p><ul><li>Professional Cloud Architect</li><li>Professional Cloud Developer</li><li>Professional Data Engineer</li></ul><p>私自身は主にアプリケーション開発がメインなので実務的には Professional Cloud Developer が近い内容なのですが、上記プログラムで習得出来るスキルに近いとのことだった Professional Cloud Architect の取得を目指しました。</p><h3>Professional Cloud Architect 受験に向けた勉強内容</h3><p>まずは<a href="https://cloud.google.com/learn/certification/cloud-architect?hl=ja">公式</a>の模擬試験を問いてみました。</p><p>Couseraで学習した内容などが出題されますが、選択式の回答がどれも合っているように思えたり、逆にどれも間違ってるように思えたりして初めは正答率は50%くらいでした。。</p><p>次に オンライン学習コンテンツを利用し、こちらの問題集をやり込むことにしました。（私はUdemyを利用しましたが、公式からも <a href="https://www.cloudskillsboost.google/paths">Cloud Skills Boost</a> というコンテンツが提供されているようです ）</p><p>問題集は全170問あるため全て正解しようと思うとなかなかシビれます。</p><h3>不合格</h3><p>受験方法は</p><ul><li>遠隔監視オンライン試験</li><li>テストセンターでオンサイト監視試験</li></ul><p>の2パターンあり、遠隔監視オンライン試験は自宅でも受験出来るようですが、試験環境を整えられるか不安だったので、テストセンターでオンサイト監視試験で受験する事にしました。場所はテストセンター秋葉原昭和通。</p><p>しかし1度目の Professional Cloud Architect 試験は不合格となりました。。</p><p>試験終了後すぐに合否がわかるのですが不合格が表示された瞬間、その場で自分が数分フリーズしていたのを覚えています🫥</p><p>不合格の場合、2週間は同じ試験を受けることができないため期間的にG.I.Gプログラムは未修了という事が確定。。</p><p>メンタルはズタボロです。</p><p>しかしここで腐らず再起を誓い、しっかり準備して次の受験に備えることを決めます。</p><h3>2度目の Professional Cloud Architect 受験に向けた勉強内容</h3><p><a href="https://cloud.google.com/learn/certification/cloud-architect?hl=ja">公式</a>の模擬試験、こちらは何度やっても正答率100%になるまでやり込みました。</p><p>オンライン学習の問題集、こちらも正答率100%になるまでやり込もうと意気込みましたが、全170問はなかなか厳しく、前日の時点で94%程度。</p><p>しかしこれだけやり込んでおけば大丈夫だろうと、1度目の受験に比べかなりの自信を持って試験当日を迎えます。</p><h3>合格</h3><p>1度目の試験から約2ヶ月を空け、今回も1度目と同様のテストセンターでオンサイト監視試験で受験。</p><p>今回は見事合格！</p><p>試験終了後、すぐに合格が表示されましたが、嬉しさと興奮で今回も自分が数分フリーズです🫥</p><p>Cousera の内容を理解し、出題傾向を完全に把握するという当たり前ながら大変なハードルを超えられたことは自分の自信に繋がり、実務にも価値のあるスキルが得られた（はず）と思います。</p><h3>最後に</h3><p>総勉強時間は約20〜25時間くらい( Cousera は除く)。自分はどちらかというと開発の人間なので上記の勉強時間でしたが、実務で Google Cloud を普段から活用している方ならもっと短い勉強時間で合格出来ると思います。</p><p>合格に向けて勉強していくことで実務でまだあまり触れていない部分（ネットワーク関連、コンプライアンス関連、データ転送関連など）についても知識を得られた事が自分にとって特に有益だったと感じました。</p><p>今回の合格だけで満足せず、次の資格取得、スキル取得に向けて頑張っていきたいと思います！</p><p>余談ですが、Google Cloud 認定試験に合格すると頂ける品物が色々あるのですが、自分は使い勝手良さそうなこちらのバックパックにしました。こういった手元に残る品物を頂けるのも嬉しいですね。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ELr4mE7WbuFDTT8H-BWuMQ.png" /></figure><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “<a href="https://www.arigatobank.com/">arigatobank</a>” は、バックエンドシステムをすべて Google Cloud で開発・運用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Google Cloud をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f007cb53d73" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/google-cloud-%E8%AA%8D%E5%AE%9A-professional-cloud-architect-%E5%90%88%E6%A0%BC%E8%A8%98-4f007cb53d73">Google Cloud 認定 Professional Cloud Architect 合格記</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go 1.21 で追加された log/slog を本番環境に実戦投入しました]]></title>
            <link>https://medium.com/arigatobank-tech-blog/go-1-21-slog-5f0d9804204f?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/5f0d9804204f</guid>
            <category><![CDATA[logging]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[structured-logging]]></category>
            <category><![CDATA[go]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Wed, 06 Dec 2023 23:41:33 GMT</pubDate>
            <atom:updated>2023-12-06T23:41:33.737Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*73r85drQeLqRhMiFu_CUxg.jpeg" /></figure><p>この記事は <a href="https://qiita.com/advent-calendar/2023/go">Go Advent Calendar 2023</a> 7 日目の記事です。</p><p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>私たちのチームでは、バックエンドシステムのすべてを Go 言語で構築しています。これまでロギングライブラリとして主に <a href="https://github.com/uber-go/zap">zap</a> を利用してきましたが、最近 Go 1.21 で追加された <a href="https://pkg.go.dev/log/slog">log/slog</a> を本番環境の一部サービスで利用し始めました。</p><p>この記事では、log/slog を開発や本番環境へ投入するにあたってカスタマイズした実装の詳細について共有します。</p><h3>log/slog とは</h3><p>Go 1.21 で標準ライブラリに加わった log/slog は、構造化ロギングをサポートするログパッケージです。</p><p><a href="https://pkg.go.dev/log/slog">slog package — log/slog — Go Packages</a></p><p>基本的な使い方はこんな感じです。</p><pre>package main<br><br>import (<br>  &quot;log/slog&quot;<br>  &quot;os&quot;<br>)<br><br>func main() {<br>  slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))<br>  slog.Info(&quot;hello, world!&quot;, slog.String(&quot;user&quot;, &quot;gopher&quot;))<br>}</pre><p>実行すると以下のように出力されます。</p><pre>{&quot;time&quot;:&quot;2023-12-03T16:56:12.194852+09:00&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;msg&quot;:&quot;hello, world!&quot;,&quot;user&quot;:&quot;gopher&quot;}</pre><p>これまで構造化ロギングには <a href="https://github.com/sirupsen/logrus">logrus</a> 、zap、<a href="https://github.com/rs/zerolog">zerolog</a> などのサードパーティライブラリが利用されてきました。Go の標準ライブラリには初期から <a href="https://pkg.go.dev/log">log</a> パッケージがありますが、基本的な機能の提供に留まります。構造化ロギングの重要性の高まりや、大規模なプログラムにおいて複数ログパッケージの混在による一貫したログ出力の難しさなどの課題を受けて、今回新たに標準ライブラリとして追加されました。<br>（参考: <a href="https://go.dev/blog/slog">Structured Logging with slog</a>）</p><p>log/slog の API は、フロントエンドの Logger とバックエンドの Handler に分かれています。共通のバックエンドである Handler を標準ライブラリとして提供することで、既存のログパッケージを含めて相互運用可能になっています。また、context.Context 型を標準でサポートし、Handler でコンテキスト情報を抽出できるなど、後発パッケージならではの設計がなされています。</p><h3>log/slog を利用したログ出力実装の詳細</h3><p>開発および本番環境への投入に向けて、Google Cloud や OpenTelemetry との統合を実現する必要がありました。そこで、log/slog を利用して以下の 3 つを実装しました。</p><ul><li>Cloud Logging と既存の zap 構成との互換性を保つ ReplaceAttr 関数</li><li>OpenTelemetry の TraceID と SpanID を Cloud Logging の指定フィールドに埋め込む Handler</li><li>既存ログモジュールと互換性のある独自ログ出力関数</li></ul><p>最終的には以下のコードに対して次のような出力が得られる実装になりました。</p><pre>// ハンドラの設定<br>slog.SetDefault(slog.New(NewHandler(&quot;foobar&quot;)))<br><br>// ログ出力<br>Info(ctx, &quot;Hello, ARIGATOBANK Tech Blog!&quot;, slog.String(&quot;user&quot;, &quot;gopher&quot;))</pre><pre>{<br>  &quot;time&quot;: &quot;2023-12-04T08:48:47.220523+09:00&quot;,<br>  &quot;severity&quot;: &quot;INFO&quot;,<br>  &quot;caller&quot;: &quot;sample/main.go:25&quot;,<br>  &quot;message&quot;: &quot;Hello, ARIGATOBANK Tech Blog!&quot;,<br>  &quot;user&quot;: &quot;gopher&quot;,<br>  &quot;logging.googleapis.com/trace&quot;: &quot;projects/foobar/traces/8416afb165e7189003dce7f8617c17ba&quot;,<br>  &quot;logging.googleapis.com/spanId&quot;: &quot;9620652c5aabf556&quot;,<br>  &quot;logging.googleapis.com/trace_sampled&quot;: false<br>}</pre><p>以下で各実装について詳しく説明します。</p><h4>slog.HandlerOptions.ReplaceAttr の実装</h4><p>ReplaceAttr はログ出力の直前に呼び出される関数で、フィールドの変換や削除に利用します。</p><p>ここで実装したのは 2 つです。1 つは Cloud Logging の構造化ロギングにおける <a href="https://cloud.google.com/logging/docs/structured-logging#special-payload-fields">特別な JSON フィールド</a> への対応、もう 1 つは既存のログモジュールで利用していた zap の構成との互換性を維持する対応です。</p><p>具体的には以下のように実装しています。</p><pre>func NewHandler(projectId string) *Handler {<br>  opt := slog.HandlerOptions{<br>    AddSource:   true,<br>    ReplaceAttr: replaceAttr,<br>  }<br>  return &amp;Handler{<br>    Handler:      slog.NewJSONHandler(os.Stderr, &amp;opt),<br>    GcpProjectId: projectId,<br>  }<br>}<br><br>func replaceAttr(groups []string, a slog.Attr) slog.Attr {<br>  switch a.Key {<br>  case slog.LevelKey:<br>    // Cloud Logging 対応: level -&gt; severity<br>    return slog.String(&quot;severity&quot;, getSeverity(a.Value.Any().(slog.Level)))<br>  case slog.MessageKey:<br>    // Cloud Logging 対応: msg -&gt; message<br>    return slog.Attr{Key: &quot;message&quot;, Value: a.Value}<br>  case slog.SourceKey:<br>    // zapcore.ShortCallerEncoder 相当<br>    return slog.Any(&quot;caller&quot;, getCaller(a.Value.Any().(*slog.Source)))<br>  }<br>  switch a.Value.Kind() {<br>  case slog.KindDuration:<br>    // zapcore.StringDurationEncoder 相当<br>    return slog.String(a.Key, a.Value.Duration().String())<br>  }<br>  return a<br>}</pre><h4>slog.Handler.Handle の実装</h4><p>Handler はログ出力の実体を担うインターフェースです。カスタマイズとしては、標準ライブラリが提供する slog.JSONHandler などに出力を任せつつ、出力したいフィールドを追加する機能などをここに実装していくことになるでしょう。</p><p>特に slog.Handler.Handle は context.Context 型が引数に入ってきます。私たちのチームでは OpenTelemetry を利用しているので、SpanContext から TraceID や SpanID などを取り出してログに埋め込む機能を実装しました。ここでも Cloud Logging の特別な JSON フィールドに出力し、Cloud Trace の Trace や Span と紐づくようにしています。</p><pre>type Handler struct {<br>  slog.Handler<br>  GcpProjectId string<br>}<br><br>const (<br>  keyGcpTrace        = &quot;logging.googleapis.com/trace&quot;<br>  keyGcpSpanId       = &quot;logging.googleapis.com/spanId&quot;<br>  keyGcpTraceSampled = &quot;logging.googleapis.com/trace_sampled&quot;<br>)<br><br>func (h *Handler) Handle(ctx context.Context, r slog.Record) error {<br>  if h.GcpProjectId != &quot;&quot; {<br>    spanCtx := trace.SpanContextFromContext(ctx)<br>    if spanCtx.IsValid() {<br>      r.AddAttrs(<br>        slog.String(keyGcpTrace, fmt.Sprintf(&quot;projects/%s/traces/%s&quot;, h.GcpProjectId, spanCtx.TraceID().String())),<br>        slog.Bool(keyGcpTraceSampled, spanCtx.IsSampled()),<br>        slog.String(keyGcpSpanId, spanCtx.SpanID().String()),<br>      )<br>    }<br>  }<br><br>  return h.Handler.Handle(ctx, r)<br>}</pre><h4>独自のログ出力関数を定義する</h4><p>log/slog では、各ログレベルごとに context.Context 型の引数を取る関数と取らない関数が用意されています。例えば Info レベルのログ出力関数として以下の 2 つがあります。</p><pre>func InfoContext(ctx context.Context, msg string, args ...any)<br>func Info(msg string, args ...any)</pre><p>しかし、開発者にとってはどちらを使うべきか迷いが生じたり、誤って Context を引数にとらないメソッドを利用し TraceID が出力されないといった不具合に繋がる可能性があります。 私たちのチームでは以前から Logger ライブラリを社内で独自に整備していたこともあり、log/slog の利用においても以下のようなログ出力関数を独自に定義することにしました。</p><pre>func Info(ctx context.Context, msg string, args ...any)<br>func Infof(ctx context.Context, format string, args ...any)<br>// 他のログレベルの関数も同様</pre><p>ここで問題になるのが、出力に含まれるファイル名や行番号の取得です。これらの独自関数から単純に slog.InfoContext などの関数を呼び出すと、ログ出力元のファイル名や行番号として独自関数の定義位置が出力されてしまいます。</p><p>この問題については <a href="https://pkg.go.dev/log/slog#hdr-Wrapping_output_methods">log/slog のパッケージドキュメントにも言及</a>があり、解決策が Example に示されています。ドキュメントに沿って以下のように実装しました。</p><pre>func Info(ctx context.Context, msg string, args ...any) {<br>  log(ctx, slog.LevelInfo, msg, args...)<br>}<br><br>func log(ctx context.Context, level slog.Level, msg string, args ...any) {<br>  logger := slog.Default()<br>  if !logger.Enabled(ctx, level) {<br>    return<br>  }<br><br>  var pcs [1]uintptr<br>  runtime.Callers(3, pcs[:]) // Skip(3): Callers, log, Debug[f]/Info[f]/Warn[f]/Error[f]/Fatal[f]<br><br>  r := slog.NewRecord(time.Now(), level, msg, pcs[0])<br>  r.Add(args...)<br><br>  _ = logger.Handler().Handle(ctx, r)<br>}</pre><p>独自関数の導入には多少の迷いがあり、Go らしく Linter を実装して利用する関数を強制することも検討しました。最終的には slog.InfoContext などの名前がほんのちょっとだけ長いと感じたことや、既存の使い勝手を維持したいという考えから導入を決めました。</p><p>また、これらの関数は、実質的には slog の Handler を呼び出す実装でしかありません。いざとなれば slog.InfoContext などの標準ライブラリの関数を使えますし、それにより出力内容に影響を与えることはありません。この柔軟性に気付いたとき、log/slog の設計の巧妙さに改めて感心しました。</p><h3>今後の log/slog に期待すること</h3><p>log/slog が標準ライブラリに追加されたことにより、ログ出力の共通バックエンドとしてのインターフェースが標準化されました。</p><p>今後は各 Observability バックエンドが Handler や ReplaceAttr 関数の実装を提供し、開発者にとって適切なログ出力の実装が容易になることを期待しています。OpenTelemetry における instrumentation のようにエコシステムが充実すると良いですね。</p><p>例えば Cloud Logging のフィールドに対応する Handler や ReplaceAttr 関数が Google Cloud 自身のメンテナンスによって公開されることを期待したいです。</p><h3>まとめ</h3><p>この記事では Go 1.21 で新しく標準ライブラリに加わった log/slog の本番環境投入にあたって実装した内容について紹介しました。これから log/slog の利用を検討している方にとって、参考になる情報があれば幸いです。他にもこういう実装を slog でやってみたよ、という話があればぜひ教えてください。</p><h3>参考資料</h3><ul><li><a href="https://pkg.go.dev/log/slog">log/slog パッケージドキュメント</a></li><li><a href="https://go.dev/blog/slog">Structured Logging with slog</a></li><li><a href="https://github.com/golang/example/blob/master/slog-handler-guide/README.md">A Guide to Writing slog Handlers</a></li><li><a href="https://www.youtube.com/watch?v=gd_Vyb5vEw0">A consistent logging format for Go</a></li><li><a href="https://cloud.google.com/logging/docs/structured-logging">Structured logging | Cloud Logging</a></li><li><a href="https://zenn.dev/knowledgework/articles/cloud-logging-special-payload-fields">Cloud Logging 構造化ログの特別な JSON フィールドまとめ</a></li></ul><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “<a href="https://www.arigatobank.com/">arigatobank</a>” は、バックエンドシステムをすべて Go で開発しています。今後も「お金で困っている人をゼロにする」世界を実現するために Go をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5f0d9804204f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/go-1-21-slog-5f0d9804204f">Go 1.21 で追加された log/slog を本番環境に実戦投入しました</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cloud Next Tokyo ’23 にて Cloud Spanner × BigQuery をテーマに登壇します]]></title>
            <link>https://medium.com/arigatobank-tech-blog/google-cloud-next-tokyo-23-%E3%81%AB%E3%81%A6-cloud-spanner-bigquery-%E3%82%92%E3%83%86%E3%83%BC%E3%83%9E%E3%81%AB%E7%99%BB%E5%A3%87%E3%81%97%E3%81%BE%E3%81%99-9684a9f437f4?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/9684a9f437f4</guid>
            <category><![CDATA[bigquery]]></category>
            <category><![CDATA[cloud-spanner]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[events]]></category>
            <category><![CDATA[google-cloud]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Tue, 31 Oct 2023 05:39:13 GMT</pubDate>
            <atom:updated>2023-10-31T05:39:13.842Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>11月15日(水)・16日(木) 東京ビッグサイトで開催される「<a href="https://cloudonair.withgoogle.com/events/next-tokyo">Google Cloud Next Tokyo ‘23</a>」にて、ブレイクアウトセッションのスピーカーとして登壇します。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O07mMuJzCTYGnNA-prhygQ.jpeg" /></figure><h3>Google Cloud Next Tokyo ’23 について</h3><p>このイベントは、<a href="https://cloudonair.withgoogle.com/events/next-tokyo">公式サイト</a>にもあるように、Google Cloud の最新情報や実践的な活用事例を学べるカンファレンスです。</p><p>生成 AI からセキュリティ、アプリケーション開発、データ分析に至るまで、多岐にわたるセッションやハンズオンが企画されており、参加者にとってはボリューム満点の 2 日間となっています。</p><p>昨年の Google Cloud Next Tokyo ’22 はオンラインでの開催でしたが、今回のイベントは 4 年ぶりのリアル開催です。</p><p>私は <a href="https://cloudonair.withgoogle.com/events/next-tokyo?talk=d2-db-03">11月16日(木) DAY2 の 16:00–16:30 のブレイクアウトセッション</a>に登壇いたします。</p><h3>セッションの内容について</h3><p>今回のセッションは「Cloud Spanner の Fine-grained Access Control と BigQuery の Federated Query で作るコスト パフォーマンスに優れたリアルタイム分析基盤」です。ARIGATOBANK が実際に構築したリアルタイム分析基盤のアーキテクチャと構築過程を中心にお話しします。</p><p>Cloud Spanner における BigQuery へのデータ連携はなかなか悩ましい点がありました。現状 Datastream は未サポートであり、Change streams を利用した Dataflow のストリーミングパイプラインはデータベース数の増加とともにコストも高まります。多くの方が同じ悩みを感じているかもしれません。</p><p>ARIGATOBANK では Google Cloud のカスタマーエンジニアとの連携や、さらには Cloud Spanner の新機能リリースタイミングなども幸いし、最終的には Cloud Spanner の Fine-grained Access Control と BigQuery の Federated Query を組み合わせるアプローチを採用しました。結果として高いコストパフォーマンスのリアルタイム分析基盤を実現できました。</p><p>このセッションでは、分析基盤のアーキテクチャの詳細や構築過程、さらには Google Cloud へのフィードバックを通じた協業の成果を紹介いたします。特にアーキテクチャ部分は、多くの方にも参考にしたいと感じていただける内容となっていると思います。ぜひご聴講ください。</p><p>セッションの後には Ask the Speaker という来場者からの質疑応答ブースが設けられています。質問やフィードバックがある方は、この機会にぜひ直接ディスカッションできれば幸いです。</p><h3>さいごに</h3><p>Google Cloud Next Tokyo ’23 は、Google Cloud の最新情報や活用事例を学べる機会として非常に価値のあるイベントです。ARIGATOBANK としても、自社での取り組みや成果を共有し、参加されるみなさんと知識や経験を共有できることを楽しみにしています。</p><p>イベント当日は、データベースに関するセッションやその他の興味深いトピックに関するセッションも盛りだくさんです。私も特に「<a href="https://cloudonair.withgoogle.com/events/next-tokyo?talk=d2-db-04">Google における次世代データベースの構築 〜 Cloud Spanner の内側 〜</a>」のセッションに注目しています。興味を持たれた方は、ぜひこちらもチェックしてみてください。</p><p>多くのみなさまのご参加を心からお待ちしています。</p><h4>We are Hiring!</h4><p>ARIGATOBANK の提供するモバイルアプリ “<a href="https://www.arigatobank.com/">arigatobank</a>” は、バックエンドシステムをすべて Google Cloud で開発・運用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Google Cloud をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9684a9f437f4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/google-cloud-next-tokyo-23-%E3%81%AB%E3%81%A6-cloud-spanner-bigquery-%E3%82%92%E3%83%86%E3%83%BC%E3%83%9E%E3%81%AB%E7%99%BB%E5%A3%87%E3%81%97%E3%81%BE%E3%81%99-9684a9f437f4">Google Cloud Next Tokyo ’23 にて Cloud Spanner × BigQuery をテーマに登壇します</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cloud Innovators Gym Japan (G.I.G Japan)プログラム参加レポート]]></title>
            <link>https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-google-cloud-innovators-gym-japan-g-i-g-japan-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-cf3013ed6b41?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/cf3013ed6b41</guid>
            <category><![CDATA[google-cloud-certified]]></category>
            <category><![CDATA[google-cloud]]></category>
            <category><![CDATA[gigs]]></category>
            <dc:creator><![CDATA[nankouyuukichi]]></dc:creator>
            <pubDate>Wed, 27 Sep 2023 03:00:30 GMT</pubDate>
            <atom:updated>2023-09-27T03:00:30.253Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ARIGATOBANK の南光です。</p><p>Google Cloud Japan 様よりご招待頂き、第6期 Google Cloud Innovators Gym Japan (G.I.G Japan)に参加し修了しましたので、プログラムの内容やを記事にします。</p><h3>G.I.G プログラムとは</h3><p>Google Cloud が主催する、エンジニアに向けた Google Cloud スキルを習得のための招待制のトレーニングプログラムです。Google Cloud Innovators Gym の略で、約 3 ヶ月間のトレーニングを通じて、Google Cloud の基礎知識を習得し、Google Cloud 認定資格を取得することを目指します。</p><p>トレーニングは以下の 2 つの学習コンテンツで構成されています。</p><ul><li>集合形式のオンラインセッション</li><li>Coursera を使ったオンライン自習</li></ul><p>また、トレーニングの修了には以下のいずれかの Google Cloud Certificate 試験の取得が必要です。</p><ul><li>Professional Cloud Architect</li><li>Professional Data Engineer</li><li>Professional Cloud Developer</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QlpxiTkWtIMNF5n5XwL1Zw.jpeg" /><figcaption>プログラムに参加するとG.I.G グッズを送ってもらえました</figcaption></figure><h3>トレーニング参加前の私の状況</h3><p>私は Google Cloud Certificate 試験のうち、「Professional Cloud Architect」と「Professional Data Engineer」2つの資格を取得したことがあり、「Professional Cloud Developer」を取得しようとトレーニングに参加しました。</p><p>実務面でも、ARIGATOBANK ではサービスの提供基盤として Google Cloud を利用し、Google Kubernetes Engine や Cloud Run などのサービスを利用しており、Google Cloud に初めて触れるわけではありませんでした。</p><p>しかし、サービス概要は理解していましたが Google Kubernetes Engine や Cloud Run などの構築経験は少なく、ベストプラクティスに沿った設計や具体的な設定方法について学びたいと思いトレーニングに参加しました。</p><h3>セッション</h3><p>集合形式のオンラインセッションは合計3回あり、Google Cloud のエンジニアによるプロダクト紹介やハンズオンを行います。</p><p>それぞれ以下のような内容でした。</p><ul><li>Google Cloud のサーバーレス ソリューション</li><li>Google Cloud のデータベース ソリューション</li><li>Google Cloud の CI / CD ソリューション</li></ul><p>プロダクト紹介では Google Cloud のサービス概要を網羅的に聴くことができます。<br>比較的新しいサービスや既存のサービスのアップデート情報なども聴くことができ、知らなかったサービスもあり情報を更新することができました。<br>ハンズオンでは実際に Google Cloud のコンソールを操作し、デモ環境の構築等を行いました。</p><p>また、セッションの後に懇親会も企画されており、興味のあるジャンルを選択し参加者同士でグループトークをしました。<br>私は普段の業務に近い「Web アプリ開発」を選択しましたが、他社の Google Cloud のサービス利用状況や、技術選定の方法などを貴重な話を聴くことができました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YHhAh9Zq9M49-a57b_tirw.jpeg" /><figcaption>懇親会に参加するとビールとおつまみセットを送ってもらえます</figcaption></figure><h3>Coursera</h3><p>Coursera は、オンライン学習プラットフォームです。G.I.G の期間中 Google Cloud が提供している学習セッションを無償で利用できます。</p><p>また、受験予定の試験ごとにいくつか学習コースが用意されており、その中から最低5つのセッション受講が必要です。</p><p>私は「Professional Cloud Developer」試験を受験予定でしたので、主に以下の内容を重点的に受講しました。</p><ul><li>Google Cloud を使ったアプリケーション開発</li><li>アプリケーションのデプロイ、デバッグ、パフォーマンス測定</li><li>認証と認可の設定、Pub/Sub, Cloud Function を使ったイベントドリブン処理</li><li>Google Kubernetes Engine</li><li>Cloud Run</li></ul><p>特に Coursera を使った学習では、動画やテキストでの学習だけでなく、実際に Google Cloud のコンソールの操作ができます。<br>公式ドキュメントを読んでサービスの概要を知っていても、コンソールを操作し環境を構築、CLIで設定を変更するとより理解が深まり、「 Cloud Endpoints を使って OpenAPI をベースに API のエンドポイントを構築する」や「 Cloud Run Workflow を使ってバッチ処理を実行する」など、業務で使いたいと思うことがありました。</p><p>また、Google Cloud のベストプラクティスについても学ぶことができたので、業務環境として使っていた Google Kubernetes Engine や Cloud Run についても理解が深まりました。</p><h3>Google Cloud Certificate 試験</h3><p>最後に、Google Cloud Certificate Professional Cloud Developer 試験を受験しました。</p><p>G.I.G プログラムに参加することで試験の受験費用が無料になるクーポンがもらえるので、費用面では安心して受験することができましたが、試験の準備が遅く期日ギリギリのタイミングで試験を受験することになりました。</p><p>G.I.G プログラム修了のためには必ず期日までに試験に合格し、Google Cloud Certificate 認定を受けた上で認定コードを Google Cloud Japan に報告する必要があります。</p><p>不合格になった場合は2週間同じ試験を受けることができないため、受験そのもののプレッシャーに加えて1回で合格しなければいけない状況の中で試験を受けることになりました。</p><p>また、テストセンターで試験を受けてからGoogle Cloud Certificate 認定証がメールで届くまでには1週間以上かかることもあり、テスト受験後も期日までに認定証が届くかハラハラしながら過ごすことになってしまいました。</p><p>私は運良く試験に合格し、ギリギリのタイミングで認定コードを報告することができましたが、今後 G.I.G プログラムに参加される方は十分に余裕を持って試験を受けることをお勧めします。</p><h3>まとめ</h3><p>Professional Cloud Developer 試験を取得し、無事プログラムを修了することができました。<br>3ヶ月間のトレーニングを通じて、Google Cloud を使ったアプリケーション開発について学ぶことができ、当初知りたかったベストプラクティスや設定方法について学ぶことができました。</p><p>また、交流会等によって他社の技術選定の方法や、Google Cloud の利用状況などを聴くことができたのも予想外の収穫でした。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rcYMoEDWfC4FlhNP6yosrQ.jpeg" /><figcaption>試験に合格するともらえるグッズ</figcaption></figure><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “arigatobank” は、バックエンドシステムをすべて Google Cloud で開発・運用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Google Cloud をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf3013ed6b41" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-google-cloud-innovators-gym-japan-g-i-g-japan-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-cf3013ed6b41">Google Cloud Innovators Gym Japan (G.I.G Japan)プログラム参加レポート</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[知っておきたい Cloud Spanner の行削除の仕組み: クエリパフォーマンス悪化の事例]]></title>
            <link>https://medium.com/arigatobank-tech-blog/%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84-cloud-spanner-%E3%81%AE%E8%A1%8C%E5%89%8A%E9%99%A4%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF-%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E6%82%AA%E5%8C%96%E3%81%AE%E4%BA%8B%E4%BE%8B-f01fda70b4bd?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/f01fda70b4bd</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[cloud-spanner]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[google-cloud]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Mon, 25 Sep 2023 02:05:30 GMT</pubDate>
            <atom:updated>2023-09-25T02:05:30.260Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M7Y5LswBLi1Lhr5-FDp5Jg.jpeg" /></figure><p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>今回は、Cloud Spanner における行削除の仕組みと、その挙動を認識していなかったために発生したクエリパフォーマンス(レイテンシ)悪化の事例、および対策についてお伝えします。</p><h3>Cloud Spanner の行削除の仕組み</h3><p>いきなり本題ですが、Cloud Spanner における行削除の仕組みは以下の通りです。</p><ul><li>Cloud Spanner では、削除された行はすぐにはデータベース上から物理的に削除されない</li><li>削除された行は非同期のガベージコレクションにより数日間維持された後に物理的に削除される</li><li>削除された行が物理的に削除されるまでの間、クエリは<strong>削除された行もスキャンする</strong></li><li>この挙動は内部メカニズムでありユーザーが制御できる点はない</li></ul><p>削除した行がすぐには物理的に削除されない点は、マルチバージョン同時実行制御の仕組みを持つデータベースとしては一般的な挙動です。 Cloud Spanner でもこの仕組みが採用されていることは<a href="https://cloud.google.com/spanner/docs/true-time-external-consistency#timestamps_and_multi-version_concurrency_control_mvcc">ドキュメント</a>から確認できます。</p><p>注意すべきは、ガベージコレクションされていない削除済みの行がスキャン対象となる点です。</p><p>これからお伝えするクエリレイテンシ悪化の事象は、削除済み行のスキャンが原因で発生しました。ここからはその事象を詳しく見ていきます。</p><h3>発生した事象について</h3><p>ARIGATOBANK のバックエンドシステムでは、マイクロサービスにおける分散トランザクションの整合性担保を <a href="https://microservices.io/patterns/data/transactional-outbox.html">Transactional outbox パターン</a> を用いた実装で実現しています。</p><p>Message Relay コンポーネントは、Cloud Spanner にある Outbox テーブルからポーリングで未処理メッセージを取り出し、Pub/Sub に送信します。 メッセージの送信に成功したメッセージは処理済み時刻が設定され、次回のクエリでは参照されなくなります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kfTEG6mdncHd0X_DLaJP8A.jpeg" /><figcaption>Transactional outbox パターンを用いた Message Relay の実装</figcaption></figure><p>クエリパフォーマンスの悪化は、Message Relay における未処理メッセージを取り出すクエリで発生しました。</p><p>開発時はクエリの実行計画からインデックススキャンが適用されることを確認していましたし、運用当初はパフォーマンスに問題は見られませんでした。しかしながら、機能追加に伴い非同期メッセージの追加頻度が増えた時点からクエリの実行時間が顕著に延びていることに気づき、調査を開始しました。</p><h3>調査から原因判明まで</h3><p><a href="https://cloud.google.com/spanner/docs/using-query-insights">Query Insights</a> から、該当クエリの状況を確認しました。まず確認したのは以下の 2 点です。</p><ul><li>改めて実行計画を確認し、インデックススキャンされている(フルスキャンになっていない)こと</li><li>スキャン行数(Rows scanned)、および返された行数(Rows returned)が開発時の想定通りに抑えられていること</li></ul><p>クエリレイテンシと CPU 実行時間のグラフは以下のようになっていました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Kag0YYeaMw6RKrFtHs0rSA.png" /><figcaption>クエリレイテンシと CPU 実行時間の推移</figcaption></figure><p>機能追加のタイミング以降、明らかにパフォーマンスが悪化しています。いくつか改善しているポイントがありますが、何か改善のアクションを取ったわけではありません。その後再び時間とともに悪化するという挙動でした。</p><p>また、検証のため開発環境にダミーデータを投入して同じクエリを実行しましたが、スキャン行数やレスポンスサイズが本番環境よりも大きい場合でもパフォーマンスに問題は見られませんでした。</p><p>この時点でテーブル行数の増加による影響であると見当をつけていました。しかしながら、インデックススキャンでスキャン行数が抑えられているにもかかわらずテーブル行数増加に伴いクエリのレイテンシが悪化する状況は、一般的なデータベースの挙動としては不可解です。</p><p>そこで、ここまでの事象や挙動、推測をもとに Google Cloud のサポートケースを作成し、レイテンシ悪化の原因調査について協力を仰ぎました。結果として「<strong>削除済みの行に対するスキャン行数の肥大化」</strong>が原因であると判明しました。</p><p>このとき Google Cloud のサポートチームから Cloud Spanner における行削除の仕組みについて説明があり、はじめて削除済みの行がスキャン対象となることを認識しました。</p><p>ガベージコレクションによって行が物理削除されると当然ながら削除済み行のスキャン行数は減少します。時折クエリレイテンシと CPU 使用率が改善しているのもガベージコレクションによるものと考えられ、すべての事象が説明できるようになりました。</p><h3>削除済み行のスキャン行数を確認する</h3><p>Cloud Spanner には Cloud Console の Web 画面から利用できる強力なパフォーマンス分析ツールが揃っています。 特にクエリのパフォーマンスは <a href="https://cloud.google.com/spanner/docs/using-query-insights">Query Insights</a> や、クエリの実行計画を視覚化して確認できる <a href="https://cloud.google.com/spanner/docs/tune-query-with-visualizer">クエリプランビジュアライザ</a> から分析できます。</p><p>注意点として、<strong>削除済み行のスキャン行数は上記ツールの画面上には表示されません</strong>。削除済み行のスキャン行数は、画面に表示されたビジュアライザから実行計画を JSON 形式でダウンロードしたファイルに含まれています。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/364/1*G2qrDVjAoALi1mxgN_OqWQ.png" /><figcaption>削除済み行のスキャン行数は JSON に含まれる</figcaption></figure><p>また、実行計画を取得する方法は 2 つあります。1 つはクエリの実行とともに実行計画を取得する方法(単に実行ボタンを押す場合)、もう 1 つは実行計画のみを取得する方法(下図の“説明のみ”を選択した場合)です。</p><p>いずれの場合も JSON がダウンロードできますが、実行計画のみを取得した場合はスキャン行数などの情報が含まれない点に注意してください。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/324/1*qa7YKaaJxVDRq2v5li3uNw.png" /><figcaption>“説明のみ” ではスキャン行数が取得できない</figcaption></figure><p>また、コマンドラインクライアントの <a href="https://github.com/cloudspannerecosystem/spanner-cli">spanner-cli</a> でも実行計画とともにスキャン行数などの情報を取得できます。</p><pre>% spanner-cli -p PROJECT_ID -i INSTANCE_ID -d DATABASE_ID -t -e &#39;EXPLAIN ANALYZE SELECT ...&#39;<br><br>...<br><br>3 rows in set (309.74 msecs)<br>timestamp:            2023-09-06T09:56:51.751186+09:00<br>cpu time:             474.42 msecs<br>rows scanned:         (スキャン行数) rows<br>deleted rows scanned: (削除済み行のスキャン行数) rows<br>optimizer version:    5<br>optimizer statistics: auto_20230905_03_39_08UTC</pre><h3>考察: インデックスの仕組み</h3><p>さて、Cloud Spanner では GC されていない削除済みの行がスキャン対象となることがわかりましたが、今回パフォーマンス悪化の発生したクエリはインデックススキャンでした。削除済みの行がスキャン対象となる挙動が、インデックススキャンに対してどのように影響を与えるのでしょうか。</p><p>今回インデックススキャン対象に削除済みの行が含まれていた点から察するに、セカンダリインデックスの内部表現は、インデックス対象列を PK として保持するテーブルにより実装されていると考えています。</p><p>対象のテーブルに行が INSERT されると、対応するインデックスがインデックステーブルに INSERT されます。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KmkcO7Xv1mC2THS_BEV0sw.png" /></figure><p>インデックスに含まれる値が UPDATE された場合、古い値に対応したインデックスはインデックステーブルから DELETE され、新しい値に対応するインデックス行が新たに INSERT されます。この挙動は UPDATE 時のミューテーション数からも推測できます。UPDATE ではなく DELETE と INSERT なのは、インデックスが対象列を PK としているためと考えています。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6yqNOde8YBxq41H-G_hQpQ.png" /></figure><p>このときインデックステーブルから DELETE されたインデックス行は、非同期のガベージコレクションにより数日間維持されます。この間、インデックススキャンの対象となります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a23cOHdOVnoNhd-ACAdNRg.png" /></figure><p>改めて、今回のレイテンシ悪化の事例におけるインデックススキャンへの影響を考えます。</p><p>未処理メッセージを取り出すクエリは、処理済み時刻が未設定の行を SELECT します。処理済み時刻はインデックスに含まれるため、これはインデックススキャンです。</p><p>Message Relay は処理したメッセージに処理済み時刻を設定して UPDATE します。このとき「処理済み時刻が未設定」時点の行に対するインデックスは DELETE されるものの、ガベージコレクションされるまでは「削除済みの行」として残り続けます。これがインデックススキャンの対象となったと考えています。</p><h3>削除済み行のスキャンへの対策</h3><p>対策としては、削除済みの行に対してもスキャン範囲を限定するようなクエリが有効です。</p><p>今回の事例では Message Relay の実装を以下の通り修正しました。</p><ul><li>Outbox テーブルのインデックスに、最終更新時刻のカラムを追加</li><li>一定時間ごとに Outbox テーブルの未処理メッセージから最も古い最終更新時刻(t)をもつ行を取得</li><li>Outbox テーブルから未処理メッセージを取り出すクエリに「最終更新時刻 が t 以降」の条件を追加</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sNBKwPFyLPiOyW8_eBXYjQ.jpeg" /><figcaption>クエリの修正によるスキャン範囲の改善イメージ</figcaption></figure><p>結果として、削除済みの行に対するスキャン行数が常に一定以下に抑えられ、 パフォーマンスが改善しました。</p><h3>まとめ</h3><p>今回は Cloud Spanner で発生したクエリパフォーマンス悪化の事例をもとに、Cloud Spanner における行削除の仕組みについてお伝えしました。</p><p>削除済みの行がスキャン対象になる点は、この記事を書いている 2023–09–25 時点では Cloud Spanner のドキュメントに記載されていません。今後ドキュメントに記載されることを期待したいです。また、削除済み行のスキャン行数もクエリプランビジュアライザの UI に表示されるといいですね。（どちらも Google Cloud にはフィードバック済みです）</p><p>それまでの間はこの記事が、同じくクエリパフォーマンスに問題を抱えている方の参考になれば幸いです。</p><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “arigatobank” は、バックエンドシステムのデータベースに Cloud Spanner を全面的に採用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Cloud Spanner をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F26607946&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/2d2bf09affac95a4d168c66cf54d816d/href">https://medium.com/media/2d2bf09affac95a4d168c66cf54d816d/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f01fda70b4bd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84-cloud-spanner-%E3%81%AE%E8%A1%8C%E5%89%8A%E9%99%A4%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF-%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E6%82%AA%E5%8C%96%E3%81%AE%E4%BA%8B%E4%BE%8B-f01fda70b4bd">知っておきたい Cloud Spanner の行削除の仕組み: クエリパフォーマンス悪化の事例</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cloud Japan 主催 “第 3 回 Digital Native Leader’s Meetup” 参加レポート]]></title>
            <link>https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-%E7%AC%AC-3-%E5%9B%9E-digital-native-leaders-meetup-%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-4083db314f6e?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/4083db314f6e</guid>
            <category><![CDATA[meetup]]></category>
            <category><![CDATA[日本語]]></category>
            <category><![CDATA[google-cloud]]></category>
            <dc:creator><![CDATA[Satoshi Koizumi]]></dc:creator>
            <pubDate>Mon, 31 Jul 2023 07:56:20 GMT</pubDate>
            <atom:updated>2023-07-31T07:56:20.100Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ARIGATOBANK の小泉です。</p><p>時間があいてしまいましたが、先日 6月23日（金）に Google Cloud Japan 様よりご招待頂き「第 3 回 Digital Native Leader’s Meetup」に参加してきました。この記事では参加レポートとして、イベントの内容を振り返っていきます。</p><h3>Digital Native Leader’s Meetup とは</h3><p>Google Cloud Japan 主催の招待制オフライン Meetup イベントで、普段から Google Cloud を利用している現場のエンジニアや、リモート越しにサポートいただいているカスタマーエンジニアの方々が集まり、Google Cloud 活用の知見や事例についてオフラインで語り合える場です。</p><p>会場は前回同様 Google 渋谷オフィスでした。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D_lPuZd7FDcLdfoOh2ODwg.png" /><figcaption>社食と思われるエリアにて開催</figcaption></figure><p>イベントは Beer Bust 形式で、軽食やドリンクがたくさん準備されていると事前に聞いていたので、その辺りもとても楽しみに行きました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jgqmr5iaIw_tjbUgiVrMtg.png" /><figcaption>（お寿司食べたかった・・）</figcaption></figure><p>色とりどりのとても美味しそうな軽食・・・・！だったのですが、LTの枠を頂いていて極度に緊張しており、軽食はほとんど喉を通りませんでした。</p><h3>イベント内容</h3><p>今回のテーマは「データベース」で、Spanner や Cloud SQL、 AlloyDB などをテーマとして、参加者によるアンカンファレンス形式で進行されます。</p><p>ARIGATOBANK のバックエンドシステムは Cloud Spanner を活用しており、なにか新しい情報や知見を得つつ、他の参加者の方にも良い知見をフィードバックできると良いなと思い参加しました。</p><h3>ロードマップセッション</h3><p>このセッションでは、Google Cloud が提供するデータベースに関する今後の機能について紹介がありました。</p><p>Google Cloud ではいくつかのデータベースソリューションが提供されていますが、それぞれの今後出てくる新機能や方向性等について説明がありました。弊社でも活用できそうなものがいくつかあり、リリースされたら検証を進めていきたいです。（やはり、Cloud Spanner に力を入れているという印象が強かったです。）</p><h3>ライトニングトークセッション</h3><p>ライトニングトークは Google Cloud を活用している企業のセッションが２つ（うち１つが私）と、Google Cloud の方のセッションでした。</p><h4>Database Migrate Service を使って AlloyDB に移行した事例</h4><p>Cloud SQL の PostgreSQL から AlloyDB に Database Migrate Service に移行した事例についてご紹介頂きました。</p><p>データベースの移行はダウンタイムをどれだけ短くできるかが重要且つ最も難しい点だと考えていますが、この移行ではダウンタイムは30秒程度に抑えられていて、素直にすごいなと思いました。</p><p>今後弊社でもシステム構成の最適化を進めていく中でデータベースの移行を行うタイミングが来るかもしれないので、ソリューションの１つとして非常に興味深かったです。</p><h4>はじめて Spanner を使ってみた1年間</h4><p>というタイトルで、Spanner についてLTをさせて頂きました！</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ATBMdf_ryddxsah4viy9lg.png" /></figure><p>Spanner のスケーラビリティがいかに素晴らしいか、弊社のトラフィック特性や全員お金贈り等の経験を踏まえてご紹介させて頂きました。</p><p>ARIGATOBANK では Spanner のスケーラビリティを最大限活用し、絶壁のようなトラフィックに備えて予め大規模にスケールアウトし、トラフィックが落ち着いたらアグレッシブにスケールダウンする運用を日頃からしています。直近では以前より更に細かくスケールの調整を行っていたり、オートスケーラーの導入を進めてます。</p><p>LT自体はひどい緊張で呼吸するタイミングを失ったりしたのですが、内容についてはまた別の記事で改めて紹介できればと思います。</p><h4>Cloud Spanner はじめの 1 歩</h4><p>Google Cloud の Database Specialist の方から、Cloud Spanner の紹介と、実際に触ってみるはじめの１歩についてのセッションでした。</p><p>90日間使える無料インスタンスについて、負荷試験やトランザクションの挙動を検証するのには不向きである話だったり、ドキュメントの歩き方についての話だったりと日頃 Cloud Spanner を使っていても勉強になるトピックは多かったです。</p><h3>アンカンファレンスセッション</h3><p>アンカンファレンスセッションでは参加者を「課題解決したい」人と「課題発見したい」人に大別し、活発にコミュニケーションが生まれるようにルールが設定されてグループ毎にディスカッションを行いました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ffXb7HwmMFFJbzBvCOoVJQ.png" /></figure><p>私がいたグループでは Cloud Spanner の導入を検討している方が多く、実際に使ってみた側として色々とお話させて頂きました。やはり Cloud Spanner のスケーラビリティやパフォーマンスに興味がありつつ、コスト面が気になるという方が多かったです。</p><p>（Google 様のイベントではあったものの）コスト面は高いは高いという話をしました！一方でかなり細やかにスケールができるので、ちゃんとコントロールさえすれば高いスケーラビリティや運用コストなど、 Cloud Spanner ならではのメリットを享受できるバランスには十分なり得るのではないかと考えています。</p><p>マルチリージョン構成への無停止変更など、出来ないと思っていたことが実はできるようになっていたという話や、よりパフォーマンスが良くなりそうな機能の話などを聞くこともでき、和やかな雰囲気で話ができとても楽しかったです。</p><h3>最後に</h3><p>「第 3 回 Digital Native Leader’s Meetup」に参加させて頂いたので、イベントレポートとしてまとめました。</p><p>他社がどんな課題に対してどんな取り組みをしていてどんな悩みを持っているのか、普段なかなか生の声を知ることができません。こういった情報をただ読む・聞くのではなく、情報交換できる機会はやはり貴重ですし、とても良い刺激になりました。</p><p>今後もこういった機会には積極的に参加してプロダクトの改善に繋げていければと思います。</p><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “arigatobank” は、バックエンドシステムをすべて Google Cloud で開発・運用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Google Cloud をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4083db314f6e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-%E7%AC%AC-3-%E5%9B%9E-digital-native-leaders-meetup-%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-4083db314f6e">Google Cloud Japan 主催 “第 3 回 Digital Native Leader’s Meetup” 参加レポート</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go で Printf のような自作関数の引数に静的解析をかける方法]]></title>
            <link>https://medium.com/arigatobank-tech-blog/go-%E3%81%A7-printf-%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E8%87%AA%E4%BD%9C%E9%96%A2%E6%95%B0%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%AB%E9%9D%99%E7%9A%84%E8%A7%A3%E6%9E%90%E3%82%92%E3%81%8B%E3%81%91%E3%82%8B%E6%96%B9%E6%B3%95-bdec85a4fe61?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/bdec85a4fe61</guid>
            <category><![CDATA[日本語]]></category>
            <category><![CDATA[static-code-analysis]]></category>
            <category><![CDATA[go]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Wed, 12 Jul 2023 05:06:07 GMT</pubDate>
            <atom:updated>2023-07-12T05:06:07.863Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4lPQ7NcS9X2wYt8XJQWIxQ.jpeg" /></figure><p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>この記事では、Go で Printf のようなフォーマット引数をとる関数を自作した場合に、静的解析で引数の正しさをチェックするための実装方法について紹介します。</p><h3>Printf 関数と静的解析</h3><p>Go の fmt.Printf や fmt.Sprintf、ユニットテスト時に利用する t.Logf などのフォーマット引数は、 go vet を使った静的解析によって引数の正しさをチェックできます。たとえば以下のコードに対して go vet を実行すると、fmt.Printf のフォーマット文字列に対する引数の誤りが検出されます。</p><pre>fmt.Printf(&quot;hello, %d&quot;, &quot;world&quot;)<br>fmt.Printf(&quot;hello, %s, %s&quot;, &quot;world&quot;)</pre><pre>% go vet ./...<br>./main.go:6:2: fmt.Printf format %d has arg &quot;world&quot; of wrong type string<br>./main.go:7:2: fmt.Printf format %s reads arg #2, but call has 1 arg</pre><p>Visual Studio Code などの IDE を利用していれば、コードを書いている時点でフォーマット引数の指定が正しいかどうかチェックされている場合が多いはずです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/934/1*jxmsC16u7eCMAaE9griMeg.png" /><figcaption>引数の誤りが検知される様子</figcaption></figure><h3>自作ログ出力ライブラリの引数に対して静的解析をかける</h3><p>ARIGATOBANK のバックエンドチームでは、Go のアプリケーションログを出力する Logger ライブラリを社内で独自に整備し利用しています。</p><p>Logger 関数のシグネチャは以下のとおりです。各種ログレベルに対して、メッセージとキーバリュー形式の任意の引数を取る関数、および Printf のようなフォーマット引数を取る関数が定義されています。</p><pre>func Debug(ctx context.Context, msg string, keysAndValues ...interface{})<br>func Debugf(ctx context.Context, format string, v ...interface{})<br>func Info(ctx context.Context, msg string, keysAndValues ...interface{})<br>func Infof(ctx context.Context, format string, v ...interface{})<br>func Warn(ctx context.Context, msg string, keysAndValues ...interface{})<br>func Warnf(ctx context.Context, format string, v ...interface{})<br>func Error(ctx context.Context, msg string, keysAndValues ...interface{})<br>func Errorf(ctx context.Context, format string, v ...interface{})<br>func Fatal(ctx context.Context, msg string, keysAndValues ...interface{})<br>func Fatalf(ctx context.Context, format string, v ...interface{})</pre><p>これらの自作 Logger ライブラリに定義した Debugf や Infof といったフォーマット引数を取る関数に対しては、前述のような静的解析が働いていませんでした。引数を誤って実装すること自体は少ないのですが、それゆえに動作確認やコードレビューでも比較的漏れがちで、稀に不具合を埋め込んだままリリースに至ってしまう場合もありました。</p><p>実はこのような関数でも、関数の先頭で fmt.Sprintf のような標準ライブラリの関数をダミーで呼び出す実装を加えることで、静的解析の対象となります。</p><pre>func Infof(ctx context.Context, format string, v ...interface{}) {<br>  if false {<br>    _ = fmt.Sprintf(format, v...)<br>  }<br>  // ...<br>}</pre><p>同様に go vet による静的解析でフォーマット引数の正しさをチェックすると、以下のように引数の誤りが検出されます。</p><pre>log.Infof(ctx, &quot;hello, %d&quot;, &quot;world&quot;)<br>log.Warnf(ctx, &quot;hello, %s, %s&quot;, &quot;world&quot;)</pre><pre>% go vet ./...<br>./main.go:11:2: github.com/arigatobank/*****/log.Infof format %d has arg &quot;world&quot; of wrong type string<br>./main.go:12:2: github.com/arigatobank/*****/log.Warnf format %s reads arg #2, but call has 1 arg</pre><p>もちろん外部のモジュールをインポートした場合でも有効です。また、Visual Studio Code のような IDE による静的解析結果にも反映されます。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KnW1j9HafnJlplAA9B6EZQ.png" /><figcaption>自作関数に対しても引数の誤りが検知される様子</figcaption></figure><h3>printf Analyzer について</h3><p>この静的解析は、go vet に標準で備わっている静的解析器のうち、printf Analyzer によって実装されています。</p><pre>% go tool vet help<br>...<br>    printf       check consistency of Printf format strings and arguments<br>...</pre><p>printf Analyzer については go doc に詳細なドキュメントがあります。</p><p><a href="https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/printf">printf package — golang.org/x/tools/go/analysis/passes/printf — Go Packages</a></p><p>printf Analyzer は、fmt.Printf や fmt.Sprintf などの関数およびそれらを呼び出す関数に対して静的解析が適用されます。一方で、動的メソッド呼び出しなどが挟まっている場合は検出できません。</p><p>今回の自作 Logger ライブラリの関数は、内部の Logger 実装をインターフェース化して差し替え可能にしていました。このため解析対象になっていなかったのですが、fmt.Sprintf の呼び出しを加えることにより解析対象となり、フォーマット引数の誤りを検出できるようになりました。</p><p>また、ダミー呼び出しを実装する以外に、-funcs フラグで printf Analyzer の解析対象に追加できます。go vet から呼び出す場合は -printf.funcs フラグとなります。</p><pre>func Foo(ctx context.Context) {<br>  myPrintf(ctx, &quot;hello, %d&quot;, &quot;world&quot;)<br>  myPrintf(ctx, &quot;hello, %s, %s&quot;, &quot;world&quot;)<br>}<br><br>func myPrintf(ctx context.Context, format string, v ...interface{}) {<br> // ...<br>}</pre><pre>% go vet -printfuncs myPrintf<br>./main.go:8:2: sample.myPrintf format %d has arg &quot;world&quot; of wrong type string<br>./main.go:9:2: sample.myPrintf format %s reads arg #2, but call has 1 arg</pre><p>今回は引数を与えずとも標準的に解析対象にできる方法として、ダミーの fmt.Sprintf 呼び出しを加える方法を選択しました。</p><p>なお、printf Analyzer の内部挙動については、以下の動画でコードリーディングを通して詳しく解説されています。analysis パッケージを利用した静的解析周りの実装の知識が少し必要ですが、analysis.Fact インターフェースの使い方などを含めて非常に興味深い内容でした。</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FY3p6W_90ax0&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DY3p6W_90ax0&amp;image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FY3p6W_90ax0%2Fhqdefault.jpg&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/0248fcdcd078db472d5979354969a273/href">https://medium.com/media/0248fcdcd078db472d5979354969a273/href</a></iframe><h3>まとめ</h3><p>今回は Go で Printf のようなフォーマット引数をとる自作関数の引数に静的解析をかける方法を紹介しました。静的解析が有効に働くだけで実装時の安心感が増します。また、コードレビュー時の CI による静的解析で、不具合を埋め込んだままリリースに至ることを防ぐことができるようになりました。</p><p>ARIGATOBANK のバックエンドチームでは、これ以外にも社内の実装ルールやライブラリの使い方に注意が必要なポイントなどを静的解析ツールとして実装しています。これらを Pull Request 時の CI に組み込み、より品質の高いコードをリリースできるようにしています。</p><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “arigatobank” は、バックエンドシステムをすべて Go で開発しています。今後も「お金で困っている人をゼロにする」世界を実現するために Go をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bdec85a4fe61" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/go-%E3%81%A7-printf-%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E8%87%AA%E4%BD%9C%E9%96%A2%E6%95%B0%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%AB%E9%9D%99%E7%9A%84%E8%A7%A3%E6%9E%90%E3%82%92%E3%81%8B%E3%81%91%E3%82%8B%E6%96%B9%E6%B3%95-bdec85a4fe61">Go で Printf のような自作関数の引数に静的解析をかける方法</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cloud Japan 主催 “第 2 回 Digital Native Leader’s Meetup” 参加レポート]]></title>
            <link>https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-%E7%AC%AC-2-%E5%9B%9E-digital-native-leaders-meetup-%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-f34cfcd13ff2?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/f34cfcd13ff2</guid>
            <category><![CDATA[google-cloud]]></category>
            <category><![CDATA[日本語]]></category>
            <category><![CDATA[meetup]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Mon, 22 May 2023 06:27:19 GMT</pubDate>
            <atom:updated>2023-05-22T06:27:19.868Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>かなり間が空いてしまいましたが、3 月 24 日(金) に「第 2 回 Digital Native Leader’s Meetup」に参加してきました。この記事では参加レポートとして、イベントの内容を振り返っていきます。</p><h3>Digital Native Leader’s Meetup とは</h3><p>Google Cloud Japan 主催の招待制オフライン Meetup イベントです。会場は Google 渋谷オフィスでした。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gE24pfHoj3egSdn0QJtFIA.jpeg" /><figcaption>Google 渋谷オフィスのフロント</figcaption></figure><p>普段から Google Cloud を利用している現場のエンジニアや、リモート越しにサポートいただいているカスタマーエンジニアの方々が集まり、Google Cloud 活用の知見や事例についてオフラインで語り合える場です。</p><p>イベントは Beer Bust 形式で、軽食（と聞いていましたが、実際はなかなかボリューム満点でした 😋）やドリンクをごちそうになりつつ、終始和やかな雰囲気で楽しめました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NFP-P3kYKPisnGlHimnerA.jpeg" /><figcaption>ボリューム満点の軽食</figcaption></figure><h3>イベント内容</h3><p>今回のテーマはデータアナリティクスということで、BigQuery をはじめとしたデータ分析や BI 関連プロダクトを中心としたセッションとアンカンファレンスが行なわれました。</p><p>ちょうど ARIGATOBANK でもデータ分析基盤の構築・改善に力を入れているタイミングで、実践的な話が聞けるということで興味を持って参加しました。</p><h4>アップデート＆ロードマップセッション</h4><p>はじめに短いオープニングセッションの後、Google Cloud のアップデート情報と今後のロードマップに関するセッションと、最新技術トレンドに関するセッションがありました。</p><p>アップデート＆ロードマップのセッションでは、直近で発表のあった BigQuery の新しい価格体系や新機能をはじめ、現在プレビュー中のものから最近 GA になったばかりの機能まで、Google Cloud のデータアナリティクス領域に関する最新情報を聞くことができました。</p><p>紹介があった中では、特に Cloud Logging のバケット内を SQL で分析できる Log Analytics は、すぐにでも業務に活用できる機能だと感じました。単発の調査で Datadog だけではログを追いかけづらい状況でも、この機能を利用することで効率化できそうです。</p><h4>最新技術トレンドセッション</h4><p>最新技術トレンドは、データサイロとデータメッシュについてのセッションでした。データサイロとデータメッシュに関する説明と、Google Cloud でデータメッシュを構築するにあたりキープロダクトとなる Dataplex と Analytics Hub について紹介がありました。データサイロとデータメッシュについては、まだまだデータ分析基盤の構築過程にある ARIGATOBANK においては少し遠い話のように感じたのが正直なところです。ただ、今後プロダクトや組織の成長とともに増えていくデータに対する向き合い方について、とても参考になる話が聞けたと感じました。</p><h4>Lightning Talk</h4><p>セッションの後は Lightning Talk でした。参加企業のエンジニアや Google Cloud のカスタマーエンジニアが、データアナリティクス関連のプロダクトを具体的にどのように利用しているか、といった実践的な内容が聞けるセッションでした。内容は以下のとおりです。</p><ul><li>BigQuery の Remote Functions 機能を利用してビッグデータと大規模言語モデルの API を連携したレコメンドエンジンの構築例</li><li>社内外のデータ分析ニーズに対して Looker, Looker Studio をフル活用して応えている事例</li><li>Google Cloud のカスタマーエンジニアが普段どのように社内のデータプラットフォームを活用しているのか</li></ul><p>特に最初のセッションは、私自身が BigQuery の Remote Functions に対して具体的な利用イメージを持っていなかったので、とても参考になりました。また Google Cloud のカスタマーエンジニアの方のセッションでは、データ活用に向けては適材適所でそれぞれのツールを駆使して地道なデータとの向き合いが必要であることを改めて感じました。</p><h4>アンカンファレンス</h4><p>後半はアンカンファレンスです。参加者 5 名ごとに 8 つのグループを作り、各グループに Google Cloud のカスタマーエンジニアの方がファシリテーターとして入る形でディスカッションが行われました。</p><p>はじめに、参加者の中には「課題解決したい参加者」と「課題発見したい参加者」がいるはずであり、それぞれにとるべき行動指針を示したルール説明がありました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uP7KRSRnq01lg2c3ah7-MA.jpeg" /><figcaption>アンカンファレンスのルール</figcaption></figure><p>このルールに則り、まずは各参加者が今日一番話したい、聞きたいことを自己紹介を交えて話し、そのあと休憩をはさみつつ 30 分 × 2 回のディスカッションを行いました。</p><p>私達のグループでは、前半は各社が BI をどのように活用しているかといった点や、Looker を導入している企業から実際の使い勝手や他 BI ツールとの両立に関する議論が中心でした。私自身、せっかくならこのイベントを通して Looker の使い勝手やメリット・デメリットを聞いて導入を検討できたら良いと思っていたので、非常に参考になりました。</p><p>後半は BigQuery のクエリを誰が作ってビジネスサイドとエンジニアサイドでどうやって共有しているか、といった話からプロダクトでデータベースと BigQuery をどう使い分けていくか、連携していくかといった話に波及していきました。各社それぞれ、やはりさまざまな苦労があるなあという印象でしたが、近い課題感を持っている企業も多く親近感が湧きました。</p><p>また、アンカンファレンスでのディスカッション内容からカスタマーエンジニアの方が Google Cloud プロダクト側へのフィードバックを吸い上げてくださっているのも印象的でした。こういった場があることで、プロダクトとユーザーの距離が近くなり、より良いプロダクトが生まれていくのだなあと感じました。</p><h3>最後に</h3><p>今回は Google Cloud Japan 主催の “第 2 回 Digital Native Leader’s Meetup” にご招待いただき参加してきたので、そのイベントのレポートをお伝えしました。</p><p>他社の具体的な事例や課題、工夫している点などが生の声で直接聞ける場は非常に貴重です。何より技術者の方々が集まり技術に対して熱く議論を交わす場で、とても楽しめました。今後もこのようなイベントに積極的に参加し、プロダクトの改善につなげていきたいと思います。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FjmtxK3swQ8lZO0EjxURDA.jpeg" /><figcaption>おみやげに頂いたシールとサーモマグ。ありがとうございました！</figcaption></figure><h3>We are Hiring!</h3><p>ARIGATOBANK の提供するモバイルアプリ “arigatobank” は、バックエンドシステムをすべて Google Cloud で開発・運用しています。今後も「お金で困っている人をゼロにする」世界を実現するために Google Cloud をフル活用して様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/"><strong>採用ページ</strong></a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f34cfcd13ff2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/google-cloud-japan-%E4%B8%BB%E5%82%AC-%E7%AC%AC-2-%E5%9B%9E-digital-native-leaders-meetup-%E5%8F%82%E5%8A%A0%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-f34cfcd13ff2">Google Cloud Japan 主催 “第 2 回 Digital Native Leader’s Meetup” 参加レポート</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GKE の Secret 手動管理を脱却すべく External Secrets Operator を導入しました]]></title>
            <link>https://medium.com/arigatobank-tech-blog/gke-%E3%81%AE-secret-%E6%89%8B%E5%8B%95%E7%AE%A1%E7%90%86%E3%82%92%E8%84%B1%E5%8D%B4%E3%81%99%E3%81%B9%E3%81%8F-external-secrets-operator-%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F-7c7722c5e114?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/7c7722c5e114</guid>
            <category><![CDATA[google-kubernetes-engine]]></category>
            <category><![CDATA[external-secrets-operator]]></category>
            <category><![CDATA[google-cloud]]></category>
            <category><![CDATA[日本語]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Tomoki Togashi]]></dc:creator>
            <pubDate>Mon, 27 Mar 2023 07:29:53 GMT</pubDate>
            <atom:updated>2023-03-27T07:29:53.365Z</atom:updated>
            <content:encoded><![CDATA[<p>External Secrets Operator 導入に至る経緯、選定理由、導入時の工夫について</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jvyli2nkFC1SkRrIzpkt7g.jpeg" /></figure><p>こんにちは。ARIGATOBANK バックエンドエンジニアの富樫です。</p><p>ARIGATOBANK バックエンドチームではチームの成長やシステムの規模拡大に合わせて、アプリケーションアーキテクチャや CI/CD の仕組み、サービスの運用を常に見直しながら日々開発しています。</p><p>この記事では、Google Kubernetes Engine (GKE) 上で扱うシークレットの管理方法を External Secrets Operator の導入によって改善した話を紹介します。</p><p>※ 以下、特に断りのない限りパスワードやトークン、キーなどの機密データを指して「シークレット」、Kubernetes の Secret リソースを指して「Secret」と表記します。</p><h3>これまでのシークレット管理</h3><p>ARIGATOBANK のサービスは基本的に GKE をアプリケーションのプラットフォームとして構築しています。このため、シークレットは Kubernetes の Secret リソースで管理します。Secret に含まれる値は base64 でエンコードされているのみで容易にデコード可能です。このためマニフェストの yaml ファイルは直接 Git リポジトリで管理しない方針に定めています。</p><p>これまでの Secret リソースの管理は以下の方法をとっていました。</p><ol><li>アプリケーションごとに Secret リソースの yaml を手作業で作成する</li><li>作成した Secret リソースを手作業で Kubernetes クラスタに apply する</li><li>Deployment リソースは envFrom プロパティを利用して Secret リソースを参照させる</li><li>Secret リソースの yaml はバックアップとしてチーム共有の 1Password に保存する</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gOmQKSmgK0rhilMD-sabag.png" /><figcaption>これまでの運用</figcaption></figure><p>手作業の多い運用ですが、2021 年 4 月頃の開発スタート時点では上記の方法が最適と考え採用していました。</p><p>Secret の運用管理については、ソースコードリポジトリ内で暗号化して管理する方法や GCP 上で管理して Kubernetes 上で生成する方法などさまざまなソリューションが存在しています。ただし、いずれも開発プロセスや運用に多少の煩雑さを持ち込むことは避けられません。</p><p>検討当初はシステムの構成やソースコードの管理運用をできる限りシンプルにすることで、開発速度の低下要因や運用上のリスク減らしたい思惑がありました。</p><p>当時のチーム規模では上記の手運用にチーム内の相互チェックをガバナンスとして加える形で問題ないと考え、システムの規模拡大やチームの成長とともに必要になったタイミングで運用を再検討することにしました。</p><h3>運用ニーズから理想の運用をイメージする</h3><p>サービスローンチから約 1 年が経過する頃になると、運用上のニーズとして以下のような意見が出てきました。</p><ul><li>デプロイに必要な Kubernetes マニフェストすべてを Git リポジトリ上で管理したい</li><li>マイクロサービス間の認証につかうシークレットが同じ値であることを仕組みで保証したい</li><li>Secret マニフェストの管理・設定を手動で実施しているため、操作が必要になった際のデプロイ手順が煩雑</li></ul><p>また、開発を進めていくなかで Cloud Functions や Cloud Run を採用するシーンがいくつかありました。これらのサービスにはシークレットを Secret Manager で管理し環境変数への埋め込む機能が提供されています。この管理方法が非常に手軽で便利であったため、GKE でも Secret Manager を使って Secret リソースを管理したいと考えるようになりました。</p><p>このときイメージした運用は以下のようなものです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/873/1*FBXUVKThz3q56Gx77RBjNQ.png" /><figcaption>やりたい運用</figcaption></figure><ol><li>シークレットを Google Secret Manager に登録する</li><li>シークレットの所在を示す設定ファイルをアプリケーションの Kubernetes マニフェストと同様に Git リポジトリで管理する</li><li>何らかのソリューションを用いて上記設定ファイルから Kubernetes 内に Secret リソースを生成する</li><li>Deployment リソースの envFrom プロパティは生成後の Secret リソースを参照する</li></ol><p>この運用であればニーズを満たせると考え、実現するためのソリューションを検討し始めました。</p><h3>External Secrets Operator 選定のポイント</h3><p>検討にあたっては Google Cloud のカスタマーエンジニアの方からさまざまな参考情報をいただきました。（大変お世話になりました！）最終的には以下の 2 つに絞り込んで比較検討しました。</p><ul><li><a href="https://github.com/external-secrets/external-secrets/">External Secrets Operator</a></li><li><a href="https://github.com/kubernetes-sigs/secrets-store-csi-driver">Kubernetes Secret Store CSI Driver</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ALA2zlI6dRhqgF254Z5khQ.png" /><figcaption>2022–07–20 時点での比較</figcaption></figure><p>主に上記のポイントを比較し、結果として <strong>External Secret Operator</strong> を採用しました。</p><p>最も大きな理由として Secret Store CSI Driver は汎用性に欠けると感じた点が大きいです。特に Pod への mount に応じて Secret が生成される点から生まれる制約が多く、例えば Ingress の TLS を Secret で設定したい場合には利用できません。一方で External Secrets Operator はシンプルに Secret の生成にフォーカスしたソリューションです。Creation Policy, Deletion Policy によるライフサイクルの設定も可能で、多少特殊な Secret 利用シーンにも柔軟に対応可能であると感じました。</p><p>また、External Secrets Operator は Secrets Manager へアクセスするサービスアカウントを Pod の実行サービスアカウントと分離できる点が優れています。外部からアクセスされ得る Pod に対して Secret Manager へアクセスする権限を持たせる必要がないため、比較的安全性を確保できます。</p><p>以上のことから、今回は External Secret Operator を採用しました。しかしながら kubernetes-sigs 配下の Kubernetes Secrets Store CSI Driver が将来的にデファクトスタンダードになっていく可能性もあります。今後のアップデートについては引き続き注視していく必要があるものと考えています。</p><h3>安全な移行を実現するための工夫</h3><p>ここからは External Secrets Operator の導入にあたり、安全に移行し運用していくために行なった工夫を紹介していきます。</p><h4>ExternalSecret のリリースと、生成された Secret を利用する Deployment のリリースを分ける</h4><p>値に誤りがあると直接システム障害に繋がるため、ExternalSecret リソースの導入は慎重に行なう必要がありました。サービスごとに以下の手順で移行を進めました。</p><ol><li>ExternalSecret リソースを作成し、Secret を自動生成する</li><li>これまで利用していた Secret と ExternalSecret により自動生成された Secret を比較し、内容に差分がないことを確認</li><li>Deployment リソースの envFrom プロパティで参照する Secret を切り替える</li></ol><p>まだ全サービスの移行が完了したわけではありませんが、Secret 値が移行前と全く同一であることを確認しながら、とても安全に移行作業をすすめることができています。</p><p>このように Secret 生成と Deployment への設定値注入を分割して作業できることも External Secret Operator を選択したメリットであると感じています。</p><h4>シークレット追加リリース時の Secret 同期ラグ対策</h4><p>アプリケーションにシークレットを追加する場合は「ExternalSecret リソースへのシークレット定義追加」「アプリケーションでシークレットを読み込む修正」の 2 つを同時にリリースしています。</p><p>ExternalSecret を変更すると External Secret Operator がそれを検知し Secret を更新しますが、これには数秒かかります。このとき同時に Apply した Deployment リソースによる Pod のロールアウトが先に動く場合があり、この場合アプリケーションはシークレットを期待通り読み込むことができません。</p><p>以前は事前に Secret を手動で更新してから Deployment を Apply していたため、こういった問題は起きていませんでした。とはいえ以前のように Secret のリリースと Deployment のリリースを毎回分けてしまうと、せっかくの手軽さが台無しになってしまいます。</p><p>そこで対策として、アプリケーション起動処理でサーバーを起動する前に期待するシークレットが読み込めているかチェックし、問題がある場合は強制終了させています。プロセスが終了しても Kubernetes のオートヒーリングにより、シークレットが期待通り読み込まれるまで自動的に再起動されます。Pod には Readiness Probe を設定しているため、サーバーが起動してから一定時間正常に動作していない限りリクエストを受け付けることはありません。これは期待通り動作しており、現在特に問題は起きていません。</p><p>注意点として、この対策はシークレット値の変更に対しては効果がありません。現状は単純に値を変更するだけになるケースが少ないため(シークレットの主な用途であるマイクロサービス間の認証において、値の変更時は一時的に新旧を併存させる必要がある)、変更時は別名でシークレットを追加し、不要になったシークレットを後から削除する運用としています。</p><h3>さいごに</h3><p>本記事では、ARIGATOBANK バックエンドチームで行なった External Secrets Operator の導入についてご紹介しました。</p><p>External Secrets Operator 自体は、あまり気を使うことなく運用できていますが、今後知見としてシェアできることがでてきたら、またこのブログに記事を書こうと思います。</p><h3>We are Hiring !</h3><p>ARIGATOBANK では、今後も「お金で困っている人をゼロにする」世界を実現するために様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/index.html">採用ページ</a> からご連絡ください！</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2F05d5b2c8219942e08028d28a48f48c85&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Farigatobank%2Farigatobank-culture-deck&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2F05d5b2c8219942e08028d28a48f48c85%2Fslide_0.jpg%3F27779350&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href">https://medium.com/media/54a417e6b64ba0ca7621e4046762072f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7c7722c5e114" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/gke-%E3%81%AE-secret-%E6%89%8B%E5%8B%95%E7%AE%A1%E7%90%86%E3%82%92%E8%84%B1%E5%8D%B4%E3%81%99%E3%81%B9%E3%81%8F-external-secrets-operator-%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F-7c7722c5e114">GKE の Secret 手動管理を脱却すべく External Secrets Operator を導入しました</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ウォレットシステムのデータ設計]]></title>
            <link>https://medium.com/arigatobank-tech-blog/%E3%82%A6%E3%82%A9%E3%83%AC%E3%83%83%E3%83%88%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E8%A8%AD%E8%A8%88-74f54c744e33?source=rss----41cd1e4ce434---4</link>
            <guid isPermaLink="false">https://medium.com/p/74f54c744e33</guid>
            <category><![CDATA[日本語]]></category>
            <category><![CDATA[data-design]]></category>
            <category><![CDATA[payment-systems]]></category>
            <category><![CDATA[system-architecture]]></category>
            <dc:creator><![CDATA[Satoshi Koizumi]]></dc:creator>
            <pubDate>Tue, 16 Aug 2022 07:50:54 GMT</pubDate>
            <atom:updated>2022-08-16T07:50:54.129Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xMcx3voa4VB_ECv0DS6SWA.jpeg" /></figure><p>初めまして、小泉と申します。ARIGATOBANK で Tech Lead として決済システム全般の技術リードを行っています。</p><p>ARIGATOBANK では、2022年4月から電子マネーサービスを開始しました。</p><p>今回は、この決済システムにおける決済処理のデータ設計について、お伝えしたいと思います。</p><h3>ARIGATOBANK が提供する電子マネーサービス</h3><p>ARIGATOBANKは前払式支払手段（第三者型）として登録しており、各種キャンペーンの当選金を蓄積する「受取り待ち残高」と、Visaプリペイドカードとして利用できる「ARIGATOBANK残高」の２種類の電子マネーを提供しています。</p><p>お金の流れの概要は以下のようになります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*aA32XQT6C-JglmJJ4pZ75w.png" /></figure><p>利用者は、付与された受取り待ち残高をアプリ上から受け取る、もしくはクレジットカードかセブン銀行ATMから、ARIGATOBANK残高にお金がチャージできます。チャージした残高は、Visa加盟店で利用することができます。</p><h3>ウォレットシステムのアーキテクチャ</h3><p>ARIGATOBANK では、決済システムが「ウォレット」という名前で呼ばれておりますので、以降ウォレットシステム、と呼びます。</p><p>ウォレットシステムは、マイクロサービスアーキテクチャをベースに設計しています。</p><p>残高の管理や決済処理を担うコアコンポーネントと、ユースケースを実現する為のユースケースコンポーネント、更に外部との接続を担うフロントエンドコンポーネントといった、役割ごとにレイヤーを分けるイメージで全体を設計しました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*YZsQu2PJLbcAgVXqDqAr4g.png" /></figure><p>この記事では、コアコンポーネントで担っている決済処理のデータ設計について、付与から支払いの処理にフォーカスして説明します。</p><h3>コアとなるデータの設計</h3><p>ウォレットは今後多様なサービスと接続し、複雑なトランザクションや外部との接続といった拡張を想定しています。それらを実現していくための包括的な設計として、ウォレットシステムのコアコンポーネントは以下のように管理する対象をレイヤー分離して設計しました。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*kmDwOvkkmri2FVYyyR824Q.png" /></figure><h4>電子マネーの残高を管理する</h4><p>電子マネーをサービス化するにあたって最も大切な機構として、ユーザが保有している残高を管理する仕組みが必要です。</p><p>図の黄色部分が、残高を管理するモデルです。</p><p>この残高を管理する概念を、Account と命名しました。Account は保有者、口座の種類や残高といった属性情報を持ちます。</p><h4>残高の動きを管理する</h4><p>次に、残高の加減算を正しく管理する仕組みが必要です。</p><p>冒頭の図の緑色部分が、残高の動きを管理するモデルです。</p><p>残高の加減算は、ひとつの口座に着目してその口座の残高の加減算を記録する仕組みだと、整合性の確認ができないトランザクションが発生する可能性があります。これは加算される残高の原資を無視した場合に陥りがちなトラブルです。</p><p>そこで、ウォレットシステムでは残高は必ず口座から口座の移動によって管理する設計をしており、突然とある口座に残高が増えるといったことが発生しないように設計しています。</p><p>ある口座の残高が加算されると、必ず別の口座が減算されるので、マイナス残高を許容する口座が必要になります。そういった口座の特性は、口座の種類によって制御します。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*DVmMk3PS1Zjdm8rzygI3hg.png" /></figure><p>この、口座から口座への残高の動きを管理する概念は、Transfer と命名しました。</p><p>また、１回の取引が複数の Transfer から構成されることがあり、部分的に成立することを避ける必要があります。これを解決するために Transfer を束ねる Transfer Request という概念を定義しました。</p><p>Transfer Request で束ねられた Transfer は、部分的に成立するということはなく、全て成立するか、全て不成立かのどちらかとなります。システム上は、データベースのトランザクションによって一貫性を保ちます。</p><h4>決済の状態を管理する</h4><p>残高と加減算は管理できるようになりましたが、もうひとつ上位概念として、決済の状態を管理する仕組みを設計しました。例えば、決済が「成立」した後の「返金」は、残高の動きとしては２度発生していますが、単一の決済が状態遷移していると言えます。</p><p>先程示した下記の図の青い部分が、決済の状態を管理するモデルです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*kmDwOvkkmri2FVYyyR824Q.png" /></figure><p>この決済の状態を管理する概念を Payment、Payment の状態を変更する概念を Payment Request と命名しました。</p><p>今回設計した Payment の状態遷移図は以下の通りです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*LQG9DF9FgzEMbtthB4FVmw.png" /></figure><p>Created は、Payment を作成した状態で、残高は動いていません。</p><p>Captured は、決済が成立した状態を表します。</p><p>Canceled は、移動した残高を元に戻した状態を表します。</p><p>Held は少し特殊で、宛先口座にはまだ残高は移動していないが、元口座からは引かれている状態を表します。</p><p>ユースケース毎の実装では、Payment を作成して、Payment Request を発行することによって Payment の状態を遷移させ、状態遷移にあわせた Transfer を作る処理が行われます。</p><h3>具体的なユースケースの実装</h3><p>決済処理を行うデータの設計ができたので、各ユースケースでどのように処理するかを見ていきます。</p><h4>各キャンペーンの当選金の付与</h4><p>当選金の付与は、「キャンペーン管理口座」から「ユーザの 受取り待ち残高口座」へ決済処理を行います。</p><p>図に登場しているキャンペーン管理口座は、内部的にキャンペーンの原資となる口座で、所有者は ARIGATOBANK（弊社） です。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*zcNjOWefDIBkwMIvoVrS3A.png" /></figure><p>Payment を初期状態の Created で作成します。その後 Capture の Payment Request を発行すると、指定した金額の残高を移動する Transfer が生成され、Payment の状態が Captured に遷移し処理が完了します。</p><h4>受取待ち残高の受け取り（ARIGATOBANK残高へのチャージ）</h4><p>受取待ち残高に付与された残高は、ユーザがアプリで受け取り操作を行うことによって、ARIGATOBANK残高へチャージされます。</p><p>当選金の付与と同様の手順で、「ユーザの 受取待ち残高口座」から「ユーザの ARIGATOBANK残高口座」へ決済処理を行います。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*xehNEPeSDZj5IR3BlYZX7Q.png" /></figure><h4>Visa加盟店での利用</h4><p>ARIGATOBANK では、電子マネーを使う手段として、Visaプリペイドカードを発行しています。</p><p>プリペイドカードはクレジットカードとは異なり、支払いの際には事前に電子マネーをチャージしておく必要があります。</p><p>また、Visa加盟店での支払い処理は、支払い時点でリアルタイムに処理する与信枠の確保（オーソリ）と、日次などの周期で加盟店から連携される売上の確定（クリアリング）があります。処理の概要を図に示すと以下の通りです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*03k8tQ6Ucf2fJgLaNw23_g.png" /></figure><p>クレジットカードの場合は、オーソリ時点では与信枠を確保するだけなので、カード所有者へ請求はありません。クリアリングがあった利用について、1ヶ月分をまとめて翌月にカード所有者に請求を行います。</p><p>一方でプリペイドカードの場合は、オーソリの時点で電子マネーの残高を減算します。先にチャージしてある残高をオーソリ時に減算しているので、クリアリングであった利用を翌月まとめて請求することはありませんが、差額があればその差額を電子マネーの残高に反映するので、残高が変動します。</p><p>具体的な処理の流れを説明します。まずはオーソリです。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*mlml3QbfhaVfyxkMT7INrg.png" /></figure><p>まず、Visa加盟店での利用を管理するために、「帳簿口座」と命名した口座を作成しました。所有者は ARIGATOBANK（弊社） です。</p><p>また、ユーザの ARIGATOBANK残高を一時的に仮押さえしている状態を管理するために「ARIGATOBANK残高(HOLD)」という口座を作成しました。こちらはまだ売上が確定していない電子マネーになるので、所有者はユーザです。</p><p>オーソリ処理は、Payment を作成し、Hold の Payment Request を発行します。Payment は Held 状態となり、指定した金額の残高が ARIGATOBANK残高(HOLD)口座に移動します。</p><p>このオーソリに対して、同額のクリアリングが来た場合は、以下のようになります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*ux0mbH-L-QA25jQkrr5_GA.png" /></figure><p>新しい Payment は作成せずに、オーソリで作成した Payment に対して更に Capture の Payment Request を発行します。元のステータスが Held なので、Payment は ARIGATOBANK残高(HOLD) から帳簿口座への Transfer を作成し、Payment が Captured に状態遷移して処理が完了します。</p><p>オーソリ時と金額が違うクリアリングが連携され、差額の調整が必要になった場合は、以下のようになります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*SDiTDexG88mc9wuqQ4M6lw.png" /></figure><p>上記は、2000円のオーソリに対して、1500円の売上の確定が連携された例です。</p><p>Hold 時と金額が違う Capture の Payment Request を受け付けると、まず確定で連携された金額を ARIGATOBANK残高(HOLD) から帳簿口座に移動する Transfer と、同時に差額を ARIGATOBANK残高に戻す Transfer を作成し、Payment が Captured に状態遷移して処理が完了します。</p><p>この例では、ユーザは売上の確定のタイミングで残高が 500円返ってきたように見えます。</p><p>以上が、Visa 加盟店で利用する際の処理です。</p><h3>まとめ</h3><p>ARIGATOBANK の決済システムにおけるデータの設計について、ご紹介しました。また、具体的な残高の付与からVisa加盟店での利用までのユースケースについて、どのようなデータが作られるかをご紹介しました。</p><p>これからの機能追加で新しいユースケースが増えたり、新しい残高が増えることもあると思いますが、今後の改修に耐えうるいい基盤が作れたのではないかと思います。</p><p>また、特に決済システムのコアな部分のデータ設計は、後からの変更が容易ではありませんが、その一方で、なかなか他社の設計を見る機会は多くありません。今後電子マネーサービスの立ち上げをするようなことがある際には、何かの参考になれば幸いです。</p><h3>We are Hiring !</h3><p>ARIGATOBANK では、今後も「お金で困っている人をゼロにする」世界を実現するために様々な機能やサービスを創出していきたいと考えています。</p><p>新しいお金の流れをつくるプロダクト開発に一緒に取り組む仲間を探しています。興味をもっていただけましたら、まずはカジュアルにお話できればと思います。お気軽に <a href="https://www.arigatobank.co.jp/career/index.html">採用ページ</a> からご連絡ください！</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=74f54c744e33" width="1" height="1" alt=""><hr><p><a href="https://medium.com/arigatobank-tech-blog/%E3%82%A6%E3%82%A9%E3%83%AC%E3%83%83%E3%83%88%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E8%A8%AD%E8%A8%88-74f54c744e33">ウォレットシステムのデータ設計</a> was originally published in <a href="https://medium.com/arigatobank-tech-blog">ARIGATOBANK Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>