Posts for: #Tech Selection

キャッシュ機構の導入

1時に寝て7時に起きた。リリース延期を決めたので3月末へのストレスやプレッシャーからは解放されていつもよりよく眠れた気がした。あくまで気がしただけ。

キャッシュライブラリの選定

ある機能を作るための準備としてキャッシュ機構を作ることにした。avelino/awesome-go を眺めて適当なキャッシュライブラリを選定する。シンプルな用途向け、オンメモリで goroutine-safe で保守されていて実績があるものでのバランスを取るときむらさんのキャッシュライブラリになった。

ちょうどいまのお仕事を始める前に スカウトをもらった会社 のテックリードがきむらさんだったので、いまはその会社にいるんだと思い出したのが半年前。きむらさんとはリアルで何年も会っていないし、仲がよいわけでもないけれど、なぜか facebook でも繋がっているので存在を忘れるということもない。

閑話休題。ある特定の mongodb コレクションのデータをキャッシュしたい。

キャッシュの生成。

cache := gcache.New(128).Build()

特定の用途で一部の処理だけ使いたいのでこんな感じでキャッシュの処理を実装した。

value, err := cache.Get(id)
if err == nil {
    return value.(*myData), nil
} else if err != gcache.KeyNotFoundError {
    log.Error("failed to get value from cache", map[string]any{
        "key": id,
        "err": err,
    })
}

// 通常処理

cache.Set(id, setting)

念のため、キャッシュをすべてクリアするときは次を呼ぶ。これをキャッシュ制御 api から呼び出せるようにしておく。何らかの理由やバグなどでキャッシュをクリアする必要もあるだろう。

cache.Purge()

データを削除したときは id 指定でキャッシュを削除する。

cache.Remove(id)

キャッシュが使われているかどうかは内部的に統計を取っているのでそれをキャッシュ制御 api から取得することでわかる。 キャッシュが使われていれば HitCount が増え、mongodb にアクセスしていれば MissCount が増える。単体レベルでプログラムのデバッグにはなる。

return CacheStat{
    HitCount:    cache.HitCount(),
    MissCount:   cache.MissCount(),
    LookupCount: cache.LookupCount(),
    HitRate:     cache.HitRate(),
}

zeromq のメッセージングを go でやり取りする

0時に寝て何度か起きて7時に起きた。たぶんよく眠れたと思う。

zeromq のライブラリ選定

zeromq のクライアントをサンプル的に実装している。windows と linux の両方で使う予定なので pure go 実装の go-zeromq/zmq4 を使ってみることにした。zeromq のライブラリはいくつかあるが、大きく2つに分けられる。

  • c 言語の zeromq ライブラリのラッパー
  • zeromq のプロトコルを pure go 実装

c 言語のライブラリのラッパーだとビルド環境をプラットフォームごとに用意しないといけない。pure go ならクロスコンパイルも簡単。例えば、linux 上で windows のバイナリをビルドするには次のようにする。go ならたったこれだけでよい。

GOOS=windows GOARCH=amd64 go build -o bin/myapp.exe ./cmd/myapp/main.go

但し、zeromq の pure go 実装は開発があまり活発ではないし production ready でもない。まだまだベータ版というか、stable な実装にはなっていないようにみえる。うちの用途はとてもシンプルなメッセージングに使うだけなので動けば問題ないだろうという想定で最初の選択肢として go-zeromq/zmq4 を試してみる。これはまだチュートリアル的に動かしているレベルなのでコードが誤っているかもしれないが、ひとまずメッセージのやり取りができるところまで確認した。windows でも動く。これから1ヶ月ほどかけて実運用レベルのデータやテストを実施して本当に使えるかどうかの検証をしていく。

メッセージを送る側は Push というソケットを使う。送信は chan を使って非同期に送る必要性はないのだけど Pull にあわせて汎用性をもつ実装にするとこんな感じかな。

func Push(
	ctx context.Context, cfg config.Queue, msgCh <-chan zmq4.Msg,
) (<-chan error, error) {
	push := zmq4.NewPush(ctx, zmq4.WithDialerRetry(time.Second*3))
	if err := push.SetOption(zmq4.OptionHWM, cfg.SendHWM); err != nil {
		return nil, fmt.Errorf("failed to set socket option: %w", err)
	}
	endpoint := "ipc://" + cfg.Path
	if err := push.Dial(endpoint); err != nil {
		return nil, fmt.Errorf("failed to dial: %w", err)
	}
	if addr := push.Addr(); addr != nil {
		return nil, fmt.Errorf("dialer with non-nil addr")
	}

	errCh := make(chan error, messageChanSize)
	go func() {
		defer func() {
			push.Close()
			log.Debug("push queue was closed", nil)
			close(errCh)
		}()
		for msg := range msgCh {
			if err := push.Send(msg); err == nil {
				errCh <- nil
				continue
			} else if errors.Is(err, context.Canceled) {
				log.Info("push queue is closing ...", map[string]any{
					"err": err,
				})
				return
			} else {
				errCh <- err
			}
		}
	}()
	return errCh, nil
}

メッセージを受け取る側は Pull というソケットを使う。Recv() でキューからメッセージの到着をブロックする。context をキャンセルすると Recv() が即時でエラーを返すので終了処理も制御しやすい。

func Pull(ctx context.Context, cfg config.Queue) (<-chan zmq4.Msg, error) {
	pull := zmq4.NewPull(ctx)
	endpoint := "ipc://" + cfg.Path
	if err := pull.Listen(endpoint); err != nil {
		return nil, fmt.Errorf("failed to listen: %w", err)
	}
	if addr := pull.Addr(); addr == nil {
		return nil, fmt.Errorf("listener with nil addr")
	}

	ch := make(chan zmq4.Msg, messageChanSize)
	go func() {
		defer func() {
			pull.Close()
			close(ch)
			log.Debug("pull queue was closed", nil)
		}()
		for {
			log.Debug("-- waiting messages ...", nil)
			msg, err := pull.Recv()
			if err == nil {
				ch <- msg
			} else if errors.Is(err, context.Canceled) {
				log.Info("pull queue is closing ...", map[string]any{
					"err": err,
				})
				return
			} else if err == io.EOF {
				log.Debug("got EOF", nil)
			} else if err != nil {
				log.Error("failed to recieve", map[string]any{
					"err": err,
				})
			}
		}
	}()
	return ch, nil
}

テックブログ公開

0時に寝て何度か起きて6時半に起きた。起きてから8時までだらだらしてた。寒くて起きれない。2月入ってしまった。早いなぁ。

テックブログのレビュー/公開

先週に草稿は書き上げていた ものの、レビューが滞っていた。他社のテックブログを勝手に書いて公開するわけにはいかないので慎重にレビューをお願いしていた。私がそういう特性の人間なのか、課題管理システムを長年使い続けてきたからそうなったのか、その両方なのか。私は Unit Bias が大きい方の人間だと思う。自分の中で完了したタスクをクローズせずに放置しておくのが精神的に耐えられない。すぐに対応しない残作業があるなら新しい issue に作った上で古い issue をクローズすることもたまにある。放置するのは嫌なので今日は関係者にどんどん確認してレビューを進めた。

公開して知人にシェアしてみたものの、あまり反応がよくないので大して関心を唆る読みものにはなっていないようだ。扱っている内容が悪いわけではなく、私の構成や文章力が拙いのだと思う。悔しいのでもう1回ぐらい、この記事に関連するテックブログを書いて関心をもってもらうような読みものにしたい。関西人だからかぶせてかぶせて天丼にしていく。

テックブログ草稿

2時に寝て7時に起きた。生活リズムがやや乱れ気味。メンバーの1on1以外はスポット的に時間が空いていてテックブログを書いてた。

フロントエンドの技術選定のテックブログ

顧問のはらさんに勉強会をお願いして、技術選定の観点を教えてもらい、その後 next.js と sveltekit でチュートリアルやって感触を確かめ、最終的には CTO 判断でエイヤで決めた。その過程や調査結果などをそのままテックブログに書いた。お手伝い先のテックブログも hugo で動いている。私にとっては慣れた環境なので環境構築やプレビュー確認などはすぐにできた。テーマの違いによるテーブルやスタイルの定義などを他の記事を参考にしながら調整するのに手間取ったぐらい。半日ぐらいがっつり時間をとって草稿を書き上げた。これからレビューしてもらって問題なければ公開することになる。私の名前で普通に書いたけど、それが OK なら事例紹介に近いものにもなる。いつも半年働いてから、私が自分で納得できる品質を提供できていれば事例紹介をお願いしている。いまじゃなくてももう3ヶ月経ってからでもいいかもしれない。

私は会社のテックブログを書くのが嫌じゃない。私ぐらいになると、あちこちの会社でテックブログを書いてきたからお手伝いした会社は記事を1つぐらい書いて記念を残したいとまで考えるようになってきた。昔、ある会社でお手伝いしたときに調査した技術情報を社内 wiki にまとめただけで、長文をちゃんと書ける人は少ないと高い評価を得たことがあった。テックブログを書く開発者が少ないことを考慮するとさもありなんと言えるかもしれない。

今年はしばらく svelte に注目

1時に寝て7時に起きた。なんか朝うまく起きれなくなってきた。なんでだろう?

svelte アプリの開発に着手

12月の1週間分ぐらいの工数をかけて行っていた フロントエンドの技術選定 の意志決定をした、というよりはしてもらった。私は調査結果をまとめ、react を選択しても svelte を選択しても開発視点ではどちらも同じという判断を下した。あとはお手伝い先の会社にとってどちらの技術に取り組みたいかという視点しかないなと考えて CTO に最終決断を委ねた。その結果 svelte を採用することに決まった。この調査や意志決定についていずれテックブログに書きたい。私がどのぐらい開発に参加するかはまだ未定だけど、初期のリポジトリの整理ぐらいはしておこうと svelte アプリ開発に着手した。初めて関わる技術はなんにせよおもしろい。お仕事で学びがあれば個人でもなにかしら svelte アプリを作ってみたい。

java の ldap クライアント

昨日のコードリーディングの続き。いま使っているライブラリは Apache Directory LDAP API というものだけど、このライブラリの設計があまりイケてない。古い java の考え方で設計されているライブラリのような印象を受けた。他にも java の ldap クライアントはないのかな?と調べたら so でちょうど議論されていた。

この so によると、UnboundID LDAP SDK for Java がベストアンサーになっている。また機会があれば触ってみようかなと思った。

技術選定の調査開始

1時に寝て2時に起きて吐き気に苦しんで6時に起きた。後半はよく眠れた気がする。

フロントエンドの技術選定の調査

はらさんにお願いした フロントエンド勉強会 の内容を踏まえて技術選定を行う。次の3つを候補とした。

  • react
  • svelte
  • solid

客観的な指標で3つの技術を調査して比較した時点で solid はうちのチームにはあわないと候補から除外することにした。なので react vs svelte の一騎討ちとなる。なにか理由がない (デファクトスタンダード) なら react だし、うちのチームにとって有効だと判断できる項目があるなら svelte になる。ここで next.js と svelte kit でちょっとコードを書いてみて自分なりの感触も探ろうと考えている。

インフレ勉強会

エンジニアのためのインフレ研究会 #1 に参加した。お仕事の調べものをしながら聞いてた。私はもう常連で前々から同じような話を聞いているものの、発表者の資料も説明もわかりやすいものだったと思う。

3年目の創立記念日

0時に寝て何度か起きて7時に起きた。金曜日は普通の週でも疲れているが、今週はハードだったからさらにバテバテ。

隔週の雑談

顧問のはらさんと隔週の打ち合わせ。オフィス移転に伴う諸々を雑談したり、おもには夕方から講師をしてもらうフロントエンド勉強会の最終確認のようなことをしていた。

フロントエンド勉強会

私がマネージャーとなって今月決めないといけない大きな意志決定の1つにフロントエンドの技術選定がある。とはいえ、私はフロントエンドに関して素人なのでなにかしら取っ掛かりがほしい。その参考の1つとして、はらさんにお願いして技術選定というテーマでフロントエンド勉強会を開催してもらった。感謝。いまお手伝い先では私が毎週チーム勉強会を行っている。これも1ヶ月以上続けている。そろそろ定着しつつあってチーム外からも毎週数人が参加してくれるようになってきた。勉強会という開発文化の取り組みとしてもちょうどよいように思ったのでお手伝い先も巻き込んで講師だけ社外の人が務める勉強会となった。結果は15人以上参加してくれて質疑応答も盛り上がってよかったと思う。

State of JS アンケート (ここは翻訳されたサイト) という、主にはフロントエンドの開発者の調査結果がある。これはフロントエンドの開発者のみのアンケートなので偏りはあるだろうというのも考慮しつつ、最近のトレンドを理解する上でよさそうに思えた。React をデファクトスタンダードとして、対抗する候補に Svelte のみを私は考えていたが、もう1つ Solid を加えてもよいのではないかとアンケート調査をみていて思うようになった。

私にとってもっとも参考になった技術選定の考え方としてリニューアルを前提にフロントエンドを作るというもの。技術選定で難しいことの1つは、いま流行っている技術が未来もそうかどうか誰にもわからない。未来に人気がなくなって保守されなくなって開発中止となり、フロントエンドの作り直しを強いられることを避けたいという心理や懸念は一般的だと思われる。その懸念を逆転の発想をもって、例えば、作ってから3年経ったら既存のフロントエンドはすべて捨てて作り直すと決めておけば多くの悩みは解消される。こういう言い方をすると多くのフロントエンド開発者は怒るかもしれない。私にとってはプロダクトのコアはバックエンドであってフロントエンドはそうではない。だからフロントエンドはそのときの流行りの技術で動けば何でもよいという考え方は納得感が高い。

創立記念日

今日が会社の創立記念日。無事に3周年を迎えた。いつか創立記念日をお休みにしたいが、未だそのときではない。

2年目は大きな失敗も経験して経営やキャリアの両方で反省する機会にもなった。その過程でうちの会社はなにをやるのかという基本方針とプロダクトの種のようなものができた。3年目はプロダクト開発の前段階としての実証実験のようなことを実際のお客さんの業務を通じて行っている。しかもそれがいま2社目。会社を作ったときに最初の10年間のステージを3つに分けた。そしてそのステージにおけるフェーズ1の終わりが近づいていて、目標としていたことも達成の見込みがたっている。うまくいけば来年の中旬以降から実証実験の結果を踏まえたプロダクト開発に移っていけるかもしれない。そうなればフェーズ2に移行する。起業してから3年経ってもありがたいことにお仕事はあるし応援してくれる人たちもいる。周りの人たちに恵まれていて感謝することも多い。過去の自分がやってきたことに自信をもっているからその人脈も継続できているし、少しずつ新しい関係性を作っていくことにも注意を払っている。あと何年働けるだろうかと考えることもしばしばある。もうそんなに長くないこともわかっているので悔いのないよう挑戦していきたい。