23時に寝て4時半頃に起きてそのまま6時半までだらだらして起きた。早寝早起き。

今日の筋トレは腹筋:20x1,腕立て:15x1,スクワット20x1をした。

隔週の雑談

顧問のはらさんと隔週の打ち合わせ。今日の議題はこれら。内容が多くて1時間超えた。

  • 電子帳簿保存法対応の事務処理規定の共有
    • まだ始まったばかりで税理士さんの温度感も低い
    • 事務処理規定が省令に沿っているかどうかの判断はプロセス監査で行われる
    • 電子帳簿保存法には規定されていないため、事務処理規定の妥当性の検証は行われない
  • 融資を受ける構想作り
    • 日本政策金融公庫のみで検討していたが、融資実績を作りたいなら信用金庫も加えた方がよい
    • 借金のメンタリティ、担当者との折衝や審査など余裕のあるときに経験を積んでおくことはよいように思えた
  • ファイナンシャルプランナーさんとのやり取り
    • 会社の経費で役員のための保険に入ろうと考えている
    • 個人の保険控除は8万円らしい
  • ローカル複業化プロジェクトの考察
    • IT コミュニティに老人や子どもたちは入りにくい。農業なら老若男女誰でも入れる気がする
    • 農業や地元の特産品を切り口にコワーキング (コミュニティ) で街の人たちと田舎の人たちをつなげるのはすごいことだと思う (関係人口の創出)
    • 地元の有力者と仲良くなると、行政の口利きもしてくれて活動しやすくなる

はらさんが よりひろいフロントエンド を始めたそうでその話しも聞いていた。個人のブログサイトにするのか、複数人で記事を共有するサイトにするのかもまだ曖昧だという。Contentful + Next.js + Cloudflare Pages という構成らしい。Contentful というツールを私が知らなかったのでまた時間のあるときに調べてみようと思う。

母が一人暮らしをしていて体調もよくないことから要介護状態になるリスクがそこそこあると考えている。最悪の場合、会社を休眠させてしばらく介護をするかもしれない。はらさんが仰るには休眠はよいアイディアだという。会社員に例えると退職した日の帰り道を想像するとよいと話されていたのだけど、私は過去の記憶があまりないというのもあるが、これまで6回も退職してきたのに退職日を特別に思ったことはあまりない。退職日と他の日に大きな違いはなくて、次のお仕事の準備や調査をしていることが私は多かったと思う。それでも退職にあわせて有休を1-2ヶ月とってゆっくり過ごしていたことには変わりない。私もそういう、メリハリのある働き方が好きだ。

smtp 接続のタイムアウト

たまたまメンバーが誤った設定で smtp サーバーに接続したときにタイムアウトするまで5分ほどかかるという現象を発見した。タイムアウトの設定をせずに接続しようとするからそんなことが起きるのかな?と考えて smtp クライアントのタイムアウト設定を調べてみると net.DialTimeout を使えばよいという。

多めに見積もって30秒のタイムアウトを設定して接続するようにして再度メンバーに再現検証してもらったら直っていないという。接続そのものは出来ていたのだ。ソースを読んでみると、smtp クライアントを生成するときに 220 というレスポンスを読むことがわかる。誤った接続設定でもコネクションは確立するが、レスポンスが返ってこなくて待ち状態になっていた。

func NewClient(conn net.Conn, host string) (*Client, error) {
	text := textproto.NewConn(conn)
	_, _, err := text.ReadResponse(220)
	if err != nil {
		text.Close()
		return nil, err
	}
	c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
	_, c.tls = conn.(*tls.Conn)
	return c, nil
}

調べてみると Conn インターフェースにデッドラインを設定する API が提供されている。注意事項としては接続した後にデッドラインをリセットしないといけない。

デッドラインとは、I/O操作がブロックされずに失敗する絶対時間のことである。デッドラインは、ReadやWriteを呼び出した直後のI/Oだけでなく、将来の保留中の I/O すべてに適用される。デッドラインを超過した後は、未来にデッドラインを設定することで、接続をリフレッシュすることができる。

デッドラインを超えた場合、ReadやWrite、その他のI/Oメソッドの呼び出しは、os.ErrDeadlineExceeded をラップしたエラーを返します。これは、errors.Is(err, os.ErrDeadlineExceeded)を使用してテストすることができます。errorのTimeoutメソッドはtrueを返しますが、期限を超過していなくてもTimeoutメソッドがtrueを返すエラーが他にもあることに注意してください。

アイドルタイムアウトは、ReadまたはWrite呼び出しに成功した後、デッドラインを繰り返し延長することで実装できる。tの値が0であれば、I/O操作はタイムアウトしない。

https://pkg.go.dev/net#Conn

次のように SetReadDeadline() を使ってタイムアウトを5分から30秒に短縮できた。

func (s *Clinet) connectWithReadDeadline(conn net.Conn) (*smtp.Client, error) {
	if err := conn.SetReadDeadline(time.Now().Add(dialTimeout)); err != nil {
		return nil, fmt.Errorf("failed to set read deadline: %w", err)
	}
	c, err := smtp.NewClient(conn, s.config.Host)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to the smtp server: %w", err)
	}
	// clear read deadline
	if err := conn.SetReadDeadline(time.Time{}); err != nil {
		return nil, fmt.Errorf("failed to reset read deadline: %w", err)
	}
	return c, nil
}