Posts for: #Ldap

考えないという傷のある現場

2時に寝て7時半に起きた。昨日からよく寝た。また心機一転。

ldap エントリーの crud api

先週から ldap クライアントや そのプール を実装して結合テストも一通り書いていた。今日はそれらを使った crud な web api を一通り実装した。クライアント実装もテストもしっかり出来ているので後は時間の問題で1つずつ確認しながらコードを書いていくだけだった。こういう作業になると、まとまった時間があればすぐに終わる。他のメンバーのコードレビューをみたり、設計の話しをしたり、他に意識を取られてあまり自分の作業に集中できなかった。

言葉が通じないもどかしさ

先週「参考にして」と指示したものが「全コピー」で驚いてしまった。私がいくつか指摘したらすぐに削除し始めてさらに落胆した。自分の頭で考えて作業していないようにみえる。

開発というお仕事は自分で考えて、コードの1つ1つに明確な意図や根拠をもって書くものだ。もちろん、情報不足や設計に自信がもてなくて一時的に曖昧な実装にするときもあるけれど、それは懸念事項として把握しておくことで将来対応すればよい。基本的に追加するコードにはすべて意味がある。

他人が分からなくても自分が理解できているのなら、自分の考えを説明し、それが論理的であったり筋が通っていれば、私は自分の考えと違っていても構わない。説明できないコードを追加して、ツッコミを受けてなにも説明できない状況をみているのは本当に悲しい。

山田ズーニーさんの著書に書いてあった「考えないという傷」を思い出した。

来週の出張準備

1時に寝て何度か起きて6時半に起きた。気分転換も兼ねた非日常の小旅行を終えてまた元の生活へ戻していく。

ドキュメント書き

昨日の続き。ドキュメントの一部レビューをお願いしているところがあるので別のところのドキュメントを書いていた。その過程で ldap の anonymous bind の設定ができないような制御になっていることに気付いてその設定を修正したりした。ドキュメントを書いていてバグをみつけた。

近況報告の資料作り

四国から帰ってきたばかりなのに来週は東京へ行かないといけない。開発はほぼ完了しているのでなにも憂いはないが、出張のタイミングで大きなふりかえりや次の開発フェーズの要件の発散などもやりたい。そのための資料の準備がまったくできていないことに昨日の夜、気付いた。明日・明後日ぐらいで準備して、できるだけ事前に参加者に共有したい。いまは忙しくないのだけど、なぜか空き時間をうまく作れなくても毎日バタバタしている感がある。

今日は月例の近況報告の資料を作っていた。客観的に、このタイミングで私を契約終了としてもよいだろうし、私の感覚的には次の開発フェーズを3-4ヶ月みて、その後にチームのメンバーにこのプロダクト開発を委譲するといった段取りがよいのではないかと考えている。そういった話しも来週はしてくるつもり。長く残ったとしても今年度いっぱいが区切りとしてよいのではないかな。

組織やプロジェクト横断的なメトリクスの視覚化

0時に寝て4時に起きてもう1回ぐらい起きて6時半に起きた。

もうすぐ期限がやってくる。私が担当している issue 対応はあらかた終わってメンバーに「大きいもので見落としある?」って尋ねて「ない」って返ってきたのでもうクローズに向けて調整していく感じ。今週末から月曜日と3日間お休みする (土日も含めて休むというのも変ではあるが) ので一安心。

dirsync 周りのリファクタリング

ずっとレビューが放置されていた。おそらくいま go-ldap のプロジェクトで最も活発なメンテナーが夏休みだったのではないかと推測する。昨日帰ってきたようで怒涛のレビューをされていて、私が3週間前に送っていた pr もレビューしてくれた。

概ね同意してくれて public な関数名をより適切な関数名に変えたところを、1度公開したものは互換性を維持するために deprecated のコメントをして残しておいてと言われたところだけ修正した。修正後、数時間ですぐにマージしてくれた。感謝。

go-ldap にいくつかコントリビュートした機能はうちのプロダクトのシステムに使われていて、それなりの qa テストをやった上で動いているので一定の品質は担保していると思う。直近1年間のコントリビューター を参照すると、私は2番目に貢献しているようにみえる。こういう見える化が自分のモチベーションになるならそれはそれでよいと思う。

組み込みの課題管理のプロダクトを作る上で、個人がみたいメトリクスを簡単に集計できるような機能を提供しようと考えている。それは自分が伸ばしたいスキルやプラクティスに対して、会社やプロダクトを横断的に計測できる仕組みがあるといいと私は考えている。とくにいまどきはプログラマーが転職するのは当たり前だが、転職したら前の会社でやっていたメトリクスがみれないとか、別の会社でのメトリクスと相対比較したいとか、そういうニーズはあるなと私自身が感じているからでもある。

go イベントのパネルディスカッション

mercari.go #23 Go1.21 パネルディスカッション オンライン開催 に参加した。視聴者が少なかったのか、youtube のコメント欄でちょくちょくツッコミもいれたら現場で拾ってくれておもしろかった。私が関心のあった話題を3つあげてみる。

gonew の提供

For a long time now, we have heard from Go developers that getting started is often the hardest part.

Experimenting with project templates

go で新規プロジェクトを始めるときにテンプレートからプロジェクトのレイアウトを作ってくれるユーティリティとして gonew というツールが公式から提供されたらしい。知らんかった。私も新しいリポジトリ作るときに標準的なものはファイルを基本コピペしているのでこういうのできれいに作れると嬉しいかもしれない。 

derrors の是非

pkgsite という pkg.go.dev というサイトのリポジトリの internal として実装されている derrors というパッケージがある。defer を使って必ず関数がエラーを返すときに wrap するという、ユニークな発想で実装されたツール。明示的なコードを書くという go の文化とはあわない気はするけど、ユニークな使い方ではあるのでおもしろい。

この延長でエラーが発生したときにレポートを生成する derrors のユーティリティもあったりする。google がやっていることだから、わりと開発者間でもこれと同じものを自前で実装する開発者が増えているといった話しも聞く。

go 2 はもうリリースされない

The answer is never. Go 2, in the sense of breaking with the past and no longer compiling old programs, is never going to happen. Go 2 in the sense of being the major revision of Go 1 we started toward in 2017 has already happened.

Backward Compatibility, Go 1.21, and Go 2

これまでの go の言語処理系の開発の中で非互換な変更について「go 2 で」とプロポーザルだったり、issue の議論で先送りされてきた。最近コア開発者の Russ Cox 氏が (現時点で) go 2 はもう出ないと宣言した。go は既存のプログラムをコンパイルできない状態で新しいバージョンを出すことはしない。この背景の1つとして、誰もがジェネリクスの導入で go の互換性は崩れると思っていたものが互換性を維持して導入できたことが大きいと思う。(現時点で) go 2 はもうリリースされないらしい。

openldap サーバーのデバッグ

1時に寝て3時に起きて5時に起きて6時半に起きた。あとひと踏ん張りなのでこのまま突っ切る。

openldap 2.5 の ldappasswd の振る舞い

openldap サーバーでパスワードを変更時の平文パスワードを連携するために カスタム overlay モジュール を使っている。前回の改修をしたときは openldap 2.4 向けのみの振る舞いを検証していた。今回は開発フェーズでは openldap 2.5 向けにもモジュールをビルドしてパッケージングしていた。その qa テストをしていて ldappasswd だけ、意図したパスワード連携が行われないという。

開発時に私が振る舞いを検証したつもりが ldapadd, ldapmodify は確認済みだったが、ldappasswd の確認をしていなかった。これは完全に私のミスで2つのフックポイントに対してカスタム overlay モジュールが動くのだから ldappasswd も大丈夫だろうと見通していた。しかし、そうではなかった。それぞれにフックポイントのコールバック設定があって、フックポイントもロジックが違うのだから当然ではあるのだけど、ちゃんと動作検証をしないといけないという、初歩的なミスをした。こんなこともあるんやと反省した。

gdb でデバッグしていて原因は 2.5.3 に含まれる次の修正だとわかった。私が検証していた openldap サーバーのバージョンは 2.5.14 だった。

簡潔に言えば、なんらかの不具合対応でもともと設定してあるコールバックを別のものに上書きしていた。カスタム overlay モジュールが設定したコールバックが別のものに上書きされてしまって意図した振る舞いをしないという現象が起きていた。これは明らかに openldap のリグレッションなので 2.5.15 で修正されてた。

たまたまピンポイントにバグを踏んだ形にはなったが、qa テストという別の人がテストをする仕組みでこのバグを検出できたことがうちの開発の品質基準を担保していることの表れでもある。

生きるということは嬉しいこと半分、辛いこと半分なのですよ。 采王

3つめのテックブログ

2時に寝て何度か起きて7時に起きた。朝から弁護士さんにメールの返信をしたりしていた。午前/午後と普通に運用ツールの開発をして夕方に勉強会をして、夜に軽く呑みに行ってきた。

go-ldap テックブログとその勉強会

水曜日から テックブログの執筆 に着手して、木曜日のお昼には下書きを書き上げて社内レビューをお願いしていた。ldap プロトコルの振る舞いについて調べたことを書いた。あまり自信はなかったけど、社内のシニアエンジニアにレビューしてもらって、よく書けているとコメントをもらってシンプルに嬉しかった。いま開発の佳境のしんどい時期に、非開発以外のことに時間をとって記事を書いて報われた気持ちになった。

今日のチーム勉強会は私が担当だったのでこのテックブログの記事を解説した。実際に go-ldap のソースコードを一緒に読みながら進める。書いたり話したりすると、ちゃんと自分が理解できているかどうかを確認ができる。そして、コードを読みながら説明しているときに、記事に書いてある内容が一部間違っていることにも気付けた。こういう体験の繰り返しで私は自分を信頼しないことを学ぶ。私の浅い理解や未熟さを実感する機会があって、また次にがんばろうというモチベーションにもつながる。

自身の理解度の確認やチームへの共有、モチベーションコントロールなど、複数の意味で自分が書いたテックブログの記事を解説する勉強会はいいように思えた。

思い入れのあった内容を書いて公開してしまうと、燃え尽きのような、少しやり切った感を感じていた。しかし、開発はまだ佳境の途中なのでここで立ち止まることはできない。それで呑みに行ってみた。とくに目的なくみつけたお店に初めて入ってみた。普通の居酒屋よりちょっとだけ値が張る感じの、落ち着いたお店で、私よりも少し年配 (にみえる) マスターが1人でやりくりしていた。軽く話しながら晩ご飯を食べれてやや救われた。また行ってみようと思う。

台風の暴風雨にびびった

台風が来るということだったので昨日は18時には家に戻ってきてゆっくりしていた。とくに何をしていたわけでもないけれど、なぜか眠れなくて3時ぐらいまでは起きていた気がする。あまりちゃんと眠れない中、7時に起きた。朝から外の暴風雨がすごくて人が飛ばされそうな勢いだった。さすがにオフィス行けないなと諦めて家でリモートワークしていた。お昼過ぎぐらいまで暴風雨が続いていたと思う。夕方になってから外に出たら普通の雨になっていてそれからオフィスに来た。

課題管理とプロジェクトマネージメントの話を熱く語る

理由があって先日 チェックした音声データ とは違う音声データを使って昨日の夜に公開された。週末働いてバテていたせいか、昨日は余裕なくて聞けなかったものの、深夜に聞き始めた。よいこと言っているなーと自画自賛しつついくつか間違ったことも話してしまっているけれど、私の話しにそこまで注意して聞く人はいないでしょう。

課題管理の話題になると、ついつい熱中して話してしまう。「熱く語る」と書かれてしまうのはこの分野に熱意をもっている人が稀だからかな。私はこの1-2年この分野をずっと調べているから、ここで話した10倍ぐらいのコンテンツをもっている。勉強会の資料も数個はあるし、スライドは200枚ぐらいある。そして、調べれば調べるほど私が分かっていないことも分かってきて、もっともっと調べたいことがある。しかし、いまいまはもう体力と気力がない。

エージェントアプリケーション開発

昨日の続き 。昨日レビューをしっかりしてもらってマージした。windows ad サーバーとの dirsync の通信のところを、一切動かさず、既存のコードをインターフェースにあうように作り直したものの、実際に動かしてみると非同期の制御が意図したデータフローでなかったり、windows ad サーバーの知らない仕様があったり、細かいバグもあったりで半日ほどかけてデバッグしながらバグ修正してた。単体レベルのテストでこのバグ数だと、qa レベルだとさらにバグありそうだなという感触だけ確かめた。その後 dirsync の検索も非同期になった方が嬉しいなと思ってちょっとリファクタリングして検証がてら提案してみた。特別なことをしなくても go-ldap の非同期検索を使ってそのまま動くことも確認できたのでこれはこれで役に立つと思う。

go-ldap の syncrepl 機能のレビュー対応

1時に寝て何度か起きて7時に起きた。今週もよく働いたからバテバテ。

ストレッチ

これまでも慢性的に右足全般は悪かったのだけれども、今日は左足の張りがある (痛い) ところと右足の張りがある (痛い) ところが全然違うことに気付いた。トレーナーさんによるとさらに今日はいつもより上半身の腕も硬かったという話しだった。これから1ヶ月か、1ヶ月半ほどは開発の佳境で忙しくなる (座っている時間が長くなる) ので体調がよくなることはないと思う。今日の開脚幅は開始前155cmで、ストレッチ後158cmだった。普段通りなのでこの調子なら問題ない。

syncrepl のレビュー対応

先日のレビュー対応 の修正。通信プロトコルの処理を実装するには想定したパケット (byte 列) をデコードしないといけない。そのために誤っているとすぐに panic する。開発しているときは既存の処理の振る舞いと競合してあちこちで panic してデバッグが容易ではなかった。そこで既存処理とは分割して先ずは実装した。その後、プロトコルの仕様と対応するパケットを理解してしまえば、どこを直せばよいか把握できているので既存の処理と共存させることはとくに難しくなかった。私の先入観であちこち変更しないといけないのでは?と思い込んでしまっていたのをレビューアの指摘で気付くことができた。感謝。

これでレビュー対応を終えた。pr を送ってから2週間放置されていた。そこでレビューしてくれないかとお願いしたら2人のメンテナーがすぐにレビューしてくれた。これは 非同期検索 でのやり取りを経て私の信頼があがっていたためと思われる。この機能はお仕事の開発にも使う。ちょうどいま開発の佳境に際して花を添えるよいタイミングと言える。

進捗報告の資料作り

気付いたら来週は出張の週になる。月例報告のための資料を作っていないことに今日気付いて慌てて資料を作った。いまは開発の佳境の時期なので、開発方法論に新しいことを試しているわけでもないし、この1ヶ月のやったことの進捗を報告するだけ。内容は固まっていて資料を作るのはそんなに大変ではない。理想的なスケジュールだと9月上旬で開発完了を目指していたが、それは無理そうだと判断した。その次のイテレーションで完了できるように目指す。さらに追加でバッファのイテレーションももう1つある (と私が勝手に思っている) 。2つイテレーションを伸ばすと開発は1ヶ月の遅延となる。このぐらいのブレは私の中では許容範囲だけれども、一般の会社だと認められるのかどうか、開発マネジメントの機微によって分かれるのかもしれない。

go-ldap の syncrepl 対応

2時に寝て2回ぐらい起きて6時半に起きた。お休みとったからいろいろ余裕がないけれど、体力だけはある。

syncrepl 機能の実装

go-ldap の非同期検索 の続き。persistent search という用語 も理解して、満を持して openldap の syncrepl 対応に臨んでいた。syncrepl を用いた persistent search というのは、簡単に言えばメッセージキューで言うところの pubsub のコンシューマーに相当する。その通信のテストやデバッグをしているときに非同期検索のバグもみつけた。余計なことをして返って複雑なバグを混入したなと反省しながら修正の pr を送った。

go-ldap の ldap 通信の処理や設計を理解するのに2日、rfc-4533 を読みながら Control に関するプロトコル実装に1日、テストやデバッグ、その他の調査や設計に2日ぐらい、次の pr を作るのに1週間 (平日5日) ほどは工数を割いた。

ローカル環境で単体レベルの動作は問題ないと思う。あとは私が知らない ldap プロトコルの振る舞いや単体レベルで検証できていない通信、実際の運用レベルの syncrepl の状態やエラーなどに耐えられるかどうかといったところだと思う。おそらく rfc を読みながらプロトコルの一部を私が実装したのは初めてかもしれない。もうサーバーサイドやバックエンドの領域で (スキルの多寡はあれども) 私が作れないものはそうないだろうという自信をもっている。実際に rfc で提案されたプロトコルを実装して、テストして、ちゃんと動いて、そういう日々がすごく楽しくて嬉しかった。根拠のなかった自信を確認できた。またレビューに時間かかるかな?マージのためのやり取りや修正に2週間から1ヶ月ぐらいを見込む。

情報共有とメンバー課金の過ち

1時に寝て4時に起きて5時に起きて7時に起きた。明け方からうまく眠れなくなった。

clang の互換性

openldap 2.5 向けに ldap の overlay モジュールのビルド環境を作っていた。これまでは 2.4 向けのモジュールのみを提供していた。2.5 もそろそろやろうということで先週末からビルド環境の構築に着手していた。rpm のパッケージングの作業をしていて、openldap 2.5 のサーバーのビルドをしていると次のエラーが発生した。

configure:21011: checking for pthread_detach with <pthread.h>
configure:21033: clang -o conftest -O2 -g3 -fstack-protector -fPIE -D_REENTRANT -D_THREAD_SAFE -DOPENLDAP_FD_SETSIZE=16384 -DLDAP_CONNECTIONLESS -DSLAPD_META_CLIENT_PR -D_GNU_SOURCE -Wl,-z,relro -Wl,--as-needed  -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1  conftest.c    >&5
clang-15: warning: argument unused during compilation: '-specs=/usr/lib/rpm/redhat/redhat-hardened-ld' [-Wunused-command-line-argument]
clang-15: warning: argument unused during compilation: '-specs=/usr/lib/rpm/redhat/redhat-annobin-cc1' [-Wunused-command-line-argument]
conftest.c:118:16: error: incompatible pointer to integer conversion passing 'void *' to parameter of type 'pthread_t' (aka 'unsigned long') [-Wint-conversion]
pthread_detach(NULL);
               ^~~~
/usr/lib64/clang/15.0.7/include/stddef.h:89:16: note: expanded from macro 'NULL'
#  define NULL ((void*)0)
               ^~~~~~~~~~
/usr/include/pthread.h:269:38: note: passing argument to parameter '__th' here
extern int pthread_detach (pthread_t __th) __THROW;
                                     ^
1 error generated.

エラーメッセージを調べていると、どうやら clang 15 に pthread_detach がないといったものらしい。clang 14 のときはビルドできたという。他の oss でも clang のバージョン違いでビルドできないといったことは発生しているらしい。有識者によると、次の修正が clang15 対応らしい。

それ以外はとくに問題なく、ビルドできてモジュールそのものの動作も確認した。あとは rpm のパッケージングと gitlab ci/cd でビルドしたモジュールで動くかどうかの検証だけ。

メンバー課金による過ち

昨日 SuperGoodMeetings をさわってみた ときにチーム管理の機能があって、任意のユーザーを招待するのは無制限で課金されないと書いてあった。「なるほどね。」とピンと来てコパイロツトの中の人に次のような所感を共有してみた。

招待可能ユーザー数を無制限にしているのはよい視点だと私は思います。メンバー課金にすると、経費を削減するために共有アカウントを利用したり、あまり使わない人にはアカウントを作らないようになって情報共有の側面から望ましくない状態になる。一昔前のオンプレ時代は業務に使うシステムのアカウントは全社員がもっていて当たり前だったのが、クラウドサービスを使うようになってメンバー課金の経費削減から全社員がもたないようになりつつある (とくに中小企業) のは、情報共有の視点から過去よりも悪化しているという問題意識を私はもっています。

コパイロツトさんもまったく同じ課題意識をもっていてメンバー課金しない料金体系にしているとのこと。鶏と卵みたいな話しだけど、組織には情報共有のためにアカウントのお金をケチんなと言いたいし、クラウドサービスの会社も料金体系を1人ずつじゃなくて、30人、100人、1000人といったある程度の階段でいいんじゃない?とか思ったりする。メンバー課金じゃないクラウドサービスとして basecamp や backlog などがある。

ldap プロトコルの persistent search

0時に寝て5時に起きて6時半に起きた。朝から大鼓方を調べたりしていた。

persistent search あれこれ

ldap プロトコルの文脈でクライアントがサーバーに接続して、エントリーの更新を検出して更新があったエントリーのみを取得することを persistent search (永続検索) と呼ぶ。メッセージキューで言うところの pubsub の consumer に相当する機能。フィルター条件に合致したエントリーのみを取得するという側面では検索と言える。

ietf のワーキンググループに次のような仕様がある。

go-ldap で過去に Add Persistent search control + PersistentSearch() #80 で実装を追加しようとしたのもあったので調べてみた。しかし、この機能に openldap は対応していないようだ。

以前から調べている openldap の syncrepl も persistent search を実現する機能の1つと言える。ldap に詳しくないと用語と機能と実装の切り分けができなくて困惑する。syncrepl はもともとレプリケーションのための仕組みではあるが、pubsub の consumer としても使える。そういうときに syncrepl を使って “persistent search” を行うと言ったりする。このときに先の ietf に提案されている persistent search とはまったく関係ない。だから混乱する。

lopenldap サーバー同士で syncrepl の provider の機能は次の overlay モジュールによって提供される。逆に syncrepl の consumer の機能は openldap の組み込みの機能で提供される。なんらかの歴史的経緯があるのだろう。

overlay syncprov

ldapsearch コマンドで persistent search (syncrepl consumer) を実行するには次のようにする。

$ ldapsearch -x -H "ldap://localhost:389" -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -w "secret" -E '!sync=rp'

ldap プロトコルの文脈で persistent search を行うといった場合、クライアントから pubsub で言うところの consumer を用意するといった意味だけで、その実装や通信方法はいくつか実現方法があるということを学んだ。

サイトデザイン最終レビュー

19時からデザイナーさんとはらさんと打ち合わせ。少し前に用意してくれた サイトデザインのサンプルページ の最終レビューを行った。全体としては気に入っているので概ね ok なのだけど、詳細の気になったところやデザインの機微のようなところをはらさんと一緒にデザイナーさんとやり取りして共有した。

デザインだけをみてこちらで想定していたことも、デザイナーさんの意見や視点を伺ってみると発見があっておもしろかった。逆に言えば、デザインだけでデザイナーさんの意図を伝えるのはとても難しいということもわかった。背景の説明を受けると論理的だったり合理性があったりするものの、なにも情報がない状態でそのことに気付くのは難しい。これはコードリーディングにおいても同じで、作者に意図の説明を受けながらソースコードを読むと簡単に理解できたりする。そして、デザイナーさんもうちらの意見から考え方を見直すこともあった。ウェブデザインのようなものを1人で完全に気付きを得るのは難しそうだ。

はらさんにレビューに入ってもらっていてとても助かる。私は ui/ux については素人なので、要件やレビューする視点の重要なところにツッコミを入れてくれるので気付くことも多い。私がコードレビューで設計やプログラミングについて指摘しているのも、別の人の視点からみるとこういうみえ方をするんだろうなと思いながら聞いていた。「餅は餅屋」とはよく言った言葉だ。自分がよくわからない分野のお仕事を依頼もしくは話すときは、自分たちの立場でそういった外部の専門家を雇うことの重要性も同時に理解できた。私は課題管理の専門家としてそういうポジションを作っていきたい。

リフレクションにはまった半日

23時に寝て5時に起きて6時半に起きた。ストレッチで伸ばしたせいか、いつもよりよく眠れた。先週は主に旅行へ行っていて非日常でリフレッシュした。今朝は朝ご飯に野菜サラダを作って食べて7時半には家を出れた。

非同期の ldap 検索の api

先日送った go-ldap の pr を完了した。送ったときはチャンネル用いた検索 api だったのだけど、それから設計を議論して非同期検索を主とした api として生まれ変わった。レビューに1ヶ月を要したものの2人のメンバーから approve をもらって無事にマージされた。

この一歩は大きくてこの機能を突破口にうちらの要件に足りない機能を実装していく。プロトコル部分の修正が過去の draft 実装から参考にできるのであれば今週中にはまた pr を送りたい。

mongodb の unmarshal 実装

mongodb-driver での bson の marshal/unmarshal を実装する。mongo-driver/bson に unmarshal について2つの interface が紹介されている。

type Unmarshaler interface {
	UnmarshalBSON([]byte) error
}

type ValueUnmarshaler interface {
	UnmarshalBSONValue(bsontype.Type, []byte) error
}

bson の byte 列を unmarshal するにあたり、構造体そのものには UnmarshalBSON() を、構造体のメンバーには UnmarshalBSONValue() を使う。これでうまくいきそうに思えたのだけど、interface を介したデコード処理で意図した振る舞いにならないことに気付いた。mongodb-driver は decode/unmarshal 処理を reflect を使って実装している。要件の詳細は省く (interface を使いたい背景がある) が、再現コードが次になる。

type MyInterface interface {
	MyFunc() error
}

type MyType struct {
}

func (t *MyType) MyFunc() error {
	return nil
}

var tMyInterface = reflect.TypeOf((*MyInterface)(nil)).Elem()

func some(v reflect.Value) {
	f := v.Convert(tMyInterface).MethodByName("MyFunc")
	fmt.Println("got", f)
	fmt.Println("=========")
}

func main() {
	t1 := &MyType{}
	some(reflect.ValueOf(t1))
	// the zero value of an interface is nil
	var t2 MyInterface
	some(reflect.ValueOf(&t2).Elem())
}

このコードを実行すると次のエラーになる。

panic: reflect: Method on nil interface value

ドキュメントにも interface の nil の値を呼び出すと panic するよと書いてある。

Method returns a function value corresponding to v’s i’th method. The arguments to a Call on the returned function should not include a receiver; the returned function will always use v as the receiver. Method panics if i is out of range or if v is a nil interface value.

https://pkg.go.dev/reflect#Value.Method