2023GW振り返り
今年はカレンダー通りなので5/4スタートです。連休を作りすぎるとやることがない人生に絶望して悲しくなるので…
5/3(水)
河原でヴァイオリン練習。カノンを録音してセルフ多重して一人で楽しんでた。河原は音を気にしなくていいけど車が通るのと風が強いのが難点。
その後カレーの材料を買って作った。
5/4(木)
スポーツクラブで体力テストのイベントがあったので参加。
鍛えている足や腹筋は悪くないスコアが出たが、握力は全く鍛えてないし、柔軟はいつも痛いし、有酸素は時間取れてないので宜なるかな。体力年齢60-64歳でガックリ。あと30年くらい維持すれば年齢相応になる。
もうちょっと良いマイクがほしいなとなんとなく思って秋葉原に行ってSONYのやつを買ってみたけど、discordで友達に聴き比べてもらったら大差なかったので意味のない買い物だった。悲しいね。
5/5(金)
ネット上の「ダーツの旅」アプリで渋谷区猿楽町を引いたので、友人と歩いた。謎の寺、超高そうなマンション(後で調べたら月200万〜)、何を売ってるのかよくわからない商品密度が異様に低い店、美容室、大使館、古墳などがあった。
ランチはメキシコ料理屋。マンションの上層階にあり、高級住宅街の中にあるとは思えない大音量大陽気大メキシコミュージックで入店直後にもうウキウキ。『ER』でグリーン先生が死ぬ前日に飲んでいたカクテル「マイタイ」を飲むことができた。
その後のんびりと麻布に向かって散歩。白根記念渋谷区郷土博物館・文学館がこどもの日で無料だった(そもそも普段も100円だし、私はこどもではない)。結構充実してる。
麻布周辺の超高級住宅街を見て、全く違う階層の生活だなあと関心してしまった。労働者では一生あそこには行けないね…。
恵比寿駅に戻って電車で帰宅。足がクタクタ。
5/6(土)
足が筋肉痛だったので休養日…と言いつつ、家系ラーメンを食べたり散髪したりヴァイオリンを練習したりした。渡辺明の坊主頭がカッコいいと思っていて、あれを真似しようかと思ったけど度胸がなくてやめた。あと家系ラーメン食べると尿が白濁してて怖い。翌日には治ります。
5/7(日)
NAS組みたくなって、ラズパイはすでにあったので秋葉原でHDD接続用のUSBハブを買った。その後大学のサークルの先輩たちと酒を飲んだ。
Next.jsのReact EssentialsのServer Componentsの説明をオレオレ翻訳しながら読む
これの話
https://nextjs.org/docs/getting-started/react-essentials#server-components
正確さではなく(それならAIでいいよね)、自分の理解に引き寄せて翻訳してみる。
Server Components
Server and Client Components があるとサーバーとクライアントの両側にまたがるアプリケーションを作れます。クライアントサイドのリッチなインタラクティビティと伝統的なサーバーレンダリングのパフォーマンスを組み合わせることができます。
Thinking in Server Components
ReactはUI設計の考え方を変えました。同じようにReact Server Componentsは、サーバーとクライアントの両方を活かしたハイブリッドアプリケーションを作るための新しい考え方です。
Reactはこれまで全てをクライアントサイドでレンダリングしていました(SPAというやつです)。しかし、Server Componentsがあると目的に応じてどこでレンダリングするかを決められます。
たとえば、アプリケーションのpageを考えます。
ページをコンポーネントに分割すると、実は大多数のコンポーネントはインタラクティブではないです。つまりサーバーサイドでServer Componentとしてレンダリングできます。そしてその中にインタラクティブなクライアントコンポーネントを点在させるのです。これはNext.jsのサーバーファーストなアプローチと相性がいいです。
Why Server Components?
Server Componentsのメリットは何?という疑問が浮かぶでしょう。
Server Componentsはサーバーのインフラを活用しやすいです。たとえば、巨大な依存パッケージをクライアントに送信する必要がありません。こうなるとReactはPHPやRoRのよう(テンプレートエンジンのように?)に扱えます。
Server Componentsは初期ページ読み込みが早いです。バンドルサイズが小さくなります。根幹部分のクライアント側ランタイムはキャッシュ可能かつサイズの予測が可能で、アプリケーションが成長しても増えません。追加されるJavaScriptは、Client Componentsが使われたときだけ増えます(この辺あんまりわかってない)。
Next.jsでrouteが読み込まれたとき、初期HTMLがサーバーでレンダリングされます。このHTMLはブラウザで段階的に成長し、クライアントがアプリケーションを引き継ぎ、インタラクティビティが付加されます。このためのランタイムの読み込みは非同期的です。
Server Componentsに楽に移行できるように、App Router内のコンポーネントは全てデフォルトでServer Componentsにします。special filesやcolocated componentsも同様です。だからあなたは何もしなくてもServer Componentsを採用して優れたパフォーマンスを得られます。ここに use client
directiveを使うことでClient Componentsをオプトインできます。
Client Components
Client Componentsを使うとクライアント側でのインタラクティビティを付加できます。これはNext.jsではサーバーでのpre-renderingとクライアント側でのhydrationで実現しています。Client ComponentsはこれまでのPage Routerと同じ動きです。
The "use client" directive
use client directiveはServer / Client Componentsの境界線を宣言するものです。
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
"use client" はサーバーとクライアントのコードの間に配置します。ファイルの一番上、importsよりも上に書きます。"use client" が宣言されると、そのファイルがimportしている全てのモジュール(子コンポーネントなど)はクライアントバンドルの一部と認識されます。
デフォルトはServer Componentsなので、 "use client" 宣言がない限り全てはServer Component moduleに含まれます。
豆知識
- Server Component moduleに含まれるComponentはサーバーでのみレンダリングされることが保証されます
- Client Componentはクライアントでレンダリングされるものですが、Next.jsは事前にサーバーでレンダリングすることもあります
- "use client" はファイルの一番上で宣言しなければなりません
- "use client" は全てのファイルで宣言する必要はなく、Server Componentsとの境界でだけ宣言すれば十分です(そのファイルがimportしているファイルもクライアントバンドルになるので、"use client" が宣言されたファイルはentry pointとみなすことができます)
おわりに
Server Componentsが何なのか知りたくて調べていたんだけど、なんか概念的な記事しかなくてよくわからなくて、この記事もそうだった。完。でも概念はちょっとわかった。
goでmysqlにDATETIMEを入れるときにgo-mysql-driverとbunでタイムゾーンの扱いが違う
goのTimeは日時とタイムゾーンの情報を持っている。日本標準時のタイムゾーンを持ったTimeを作ってみる。
location, err := time.LoadLocation("Asia/Tokyo")
if err != nil {
log.Fatal(err)
}
t := time.Date(2019, 1, 2, 3, 4, 5, 0, location) // 2019年1月2日3時4分5秒
これをmysqlに突っ込む。mysqlとのコネクションはこんな感じ。
cfg := mysql.Config{
User: "root",
Passwd: "password",
Net: "tcp",
Addr: "127.0.0.1:4306",
DBName: "time",
ParseTime: true,
}
Locは指定していない。その場合UTCになる。
https://github.com/go-sql-driver/mysql/blob/191a7c4c519ef60cf3e8656fde8728eee9194308/dsn.go#L73
// NewConfig creates a new Config and sets default values.
func NewConfig() *Config {
return &Config{
Collation: defaultCollation,
Loc: time.UTC,
MaxAllowedPacket: defaultMaxAllowedPacket,
Logger: defaultLogger,
AllowNativePasswords: true,
CheckConnLiveness: true,
}
}
go-mysql-driverで生のSQLを書いてmysqlにINSERTする
db.Exec("INSERT INTO `time` (`id`, `time`) VALUES (?, ?);", "Asia/Tokyo 2019-01-02T03:04:05", t)
このとき発行されるクエリをgeneral_logで確認すると、この段階でUTCの 2019-01-01 18:04:05
に変換されている。
2023-05-06T14:33:27.581647Z 9 Execute INSERT INTO `time` (`id`, `time`) VALUES ('Asia/Tokyo 2019-01-02T03:04:05', '2019-01-01 18:04:05')
これはgo-mysql-driverがTimeをシリアライズする前にInでタイムゾーンをUTCに変換しているからだ。
b, err = appendDateTime(b, v.In(mc.cfg.Loc))
ではbunでTimeをINSERTするとどうなるか
type BTime struct {
bun.BaseModel `bun:"time2"`
ID string
Time time.Time
}
bundb.NewInsert().Model(&BTime{ID: "BUN Asia/Tokyo 2019-01-02T03:04:05", Time: t}).Exec(ctx)
このとき発行されるクエリでは、Timeに設定されているタイムゾーンを無視して 2019-01-02 03:04:05
とシリアライズしている。
2023-05-06T14:41:39.343913Z 10 Query INSERT INTO `time2` (`id`, `time`) VALUES ('BUN Asia/Tokyo 2019-01-02T03:04:05', '2019-01-02 03:04:05')
どうやらこれは意図的な変更の結果らしい。mysqlやgo-mysql-driverなどと二重変換してしまって正常に動作しないという問題があったようだ(←わかってない)。
https://github.com/uptrace/bun/issues/168
とにかく、じゃあどうやれば安心してDATETIMEを扱えるんだよという話になるんだけど、mysqlとまたがる範囲で暗黙的な変換が入ると状態の把握が難しくなるので、goのアプリケーション側で確実にUTCにしちゃってからORMなりクエリビルダーなりに放り込むことにした。そうすれば少なくともバグったときにfmt.Printfでなんとかなる。
goとmysqlの間で苦しんでる人はたくさんいた(類似記事が多い)がbunの話してる人は全然いなかったのでテキトーに書いてみた。
おまけ dockerのmysqlでgeneral_logを見る
適当なファイルに以下を書いておいて
[mysqld]
general_log=1
そのファイルが入ったディレクトリを、コンテナの/etc/mysql/conf.dにマウントする
-v /path/to/cnf-dir/:/etc/mysql/conf.d
コンテナ起動後にコンテナに入ってファイルの場所を探してtailする
nerdctl exec -it <container_id> /bin/sh
mysql -u root --host 127.0.0.1 -p
> SHOW VARIABLES LIKE '%general_log%';
> exit;
tail /path/to/general.log
なんで general_log_file 使わないの?
なんか general_log_file=/var/log/mysql/general.log
するとmysqlの設定値はそこになるんだけどファイルが作られないんだよね。パーミッションの問題とかあるのかな。
上記の方法でやるとログファイルは /var/lib/mysql/hoge.log
に生える。
Ubuntu 23.04への式年遷宮 トラブル録
普段はLTSしか使わないのだが、なんかやりたい気持ちになったので珍しく奇数系を入れてみた。
https://www.ubuntulinux.jp/News/ubuntu2304-ja-remix
をUSBメモリに焼いて(今はUbuntu公式でもEtcher使えって書いてるんですね、昔はUbuntuにUbuntuのイメージを焼くためのソフトウェアが付属していた気がするが)、PC再起動。
インストールの手順については、特に変化はなさそう。今回はLVM使用、ディスク暗号化をつけてみた。
gnome-text-editorで日本語入力できない
毎度の鬼門(と言いつつ、熱心な日本Ubuntuユーザーが結構情報を残してくれているので言うほどではない)。こちらのページが参考になった。
https://www.kkaneko.jp/tools/server/gnome_ja_input_method.html
注意点として、23.04では(正確には22.10から)はgeditに代わってgnome-text-editorというソフトウェアが入っている。こいつがfcitxとの相性が悪いのか、日本語変換が一切効かない(日本語入力モードに入れない)。シェルから開いてみるとこんなエラーメッセージが残されていた。
(gnome-text-editor:9858): Gtk-WARNING **: 03:13:56.134: No IM module matching GTK_IM_MODULE=fcitx found
意味はよくわからないのだが、fcitxを使えと言われているがgnome-text-editorがそれを理解できていないように見える。実際にはgnome-text-editorのリポジトリにこのようなエラーメッセージはなさそうなので、GTK自体に何らかの問題があるのかもしれない。
20230507追記
fcitxじゃなくてfcitx5を入れると問題が起きなかった。
vscodeで日本語入力時に確定するまで文字が表示されない
確定するまでは変換ボックスを見ないと入力中の文字が確認できず、Enterで確定すると一気にエディタ上に文字が追加される状態になった。これはUbuntu Softwareからデフォルトのsnapで入れていたのが悪いので、消してdebで入れ直したら直った。
bluetoothヘッドホンで音が鳴らない・途切れる・汚い
Linuxでbluetoothヘッドホンを使うのはコツがいる。僕が使ってるのはWH-1000XM4ね。
まず音が途切れるのはドライバのバッファリングが下手くそなせいなので、pavucontrol→出力デバイス→WH-1000XM4→高度な設定でLatency offsetを10msくらいにしてやる。ほんの少しでも設定してやるといい感じになる。
音が汚いのはコーデックがHSPになっているから。bluetoothヘッドホンの通信コーデックにはA2DPとHSPがあり、前者は聞く専用で高音質、後者はヘッドホンの付属マイクからの音声入力も可能だが音質は悪い。なのでA2DPに設定してやることで解決(これはbluemanからでも標準のbluetooth設定からでも、たぶんCLIからでもできる)。
音が鳴らないのは今回初めて遭遇した。HSPでの接続は可能だったがA2DPが選べなかったり、A2DPにすると音が鳴らなくなったりした。これは理由が不明だが、ペアリング直後にbluemanからA2DPに設定してやることで解決した。
ストレージデバイスのマウント
今回はなるべくGUIでやっちゃうのを目標にした。普段はストレージデバイスのマウントは/etc/fstabを手で編集してやっているが、今回は「ディスク」から。デバイス→パーティションを選択して、歯車マークのメニューから「マウントオプションを編集」でGUIからマウントを設定できる。ここで設定した内容がfstabに書き込まれていた。
やはり奇数系のUbuntuを使うのはエキサイティングだ。時間のあるときに限るが、楽しいんだこれが。
保護中: 試して理解 Linuxのしくみ 8 記憶階層
保護中: 試して理解 Linuxのしくみ 7 ファイルシステム
静的サイトをS3にデプロイしてCloudFrontから配信する
世界中で一億回やられてる作業なので僕が言うことはない。これ読む。終わり。いつ書かれたものかよくわからないけどAWSのドキュメント類は割と頑張って最新の状態に追従してるので信頼していい。
https://repost.aws/knowledge-center/cloudfront-serve-static-website
- S3バケット作る
- 静的ウェブサイトホスティングは無効でいい
- CloudFrontディストリビューションを作る
- Origin access control settingsを選ぶ
- ディストリビューションを作った後にS3にコピペするためのポリシーのコピーボタンが表示されるので、そこでコピってS3のアクセス許可→バケットポリシーにペーストする
https://hogehoge.cloudfront.net でindex.htmlにアクセスさせたい
CloudFront側のディストリビューションの設定からデフォルトルートオブジェクトをindex.htmlに設定する
その他のhtmlファイルに.htmlなしでアクセスさせたい
S3側でhtmlファイルの.htmlを削除し、Content-Typeをtext/htmlにする(大抵最初からなってそう)
https://medium.com/@gauravduttkale/static-aws-s3-website-pages-without-html-extensions-12db3e15e153