git v2.29 で追加された git-maintenance について

git-maintenance とは

git maintenance は、Git 2.29 で導入されたリポジトリのメンテナンス機構です。

porcelain command (高レベル API) *1 ですね。

従来の git gc と似ていますが、ユーザー入力をブロックしないし、適切にロック処理も行っているので、より安全で継続的な運用を意識した設計になっています。

github.com

git gc は今すぐ git maintenance run --task gc に置き換えられる。二重に動いてもロックされているので安全。っぽい。

git-maintenance の基本

  • git maintenance register すると .gitconfig に記録される
  • git maintenance start すると cron に登録される
    • Mac の場合は launchctl の plist
    • 登録された repository について、 git maintenance run --schedule=xxxly する
  • git config maintenance.strategy によって動作が変わる

hourly で prefetch するの面白い。変化が大きい && 自分がよく見るリポジトリだと待ち時間が減って便利かも。

strategy は v2.52 から geometric が増えるっぽいです。これは incremental-repack じゃなく git repack --geometric する戦略。Scaling monorepo maintenance - The GitHub Blog にあるように、大きな monorepo だと geometric の方が効果があるのかもしれない

git maintenance は普段から動いている

tech.mobilefactory.jp

git maintenance は、リポジトリへの書き込み操作(fetch、commit、merge、rebase など)の後に自動的に実行される最適化処理です。リポジトリのパフォーマンスを維持するために、不要なオブジェクトの削除やパックファイルの再編成を行います。

.git を更新する処理の前後で自動で走っています。

git-maintenance を有効にする (git maintenance register) と、cron で更新するようになるので git fetch 時に動かす必要が無いとして maintenance.auto = false になります。

感想

prefetch は使いどころがあるのかもしれない。

いやー、どうだろうな。ラップトップ PC で git maintenance register する意味があるかというと微妙な気がする。テザリングしている最中に .git 更新されても嬉しくはなさそうだし。でも開発用途なら prefetch は十分に美味しそうなので有効にしそう。

僕は git gc を時々手で打っていたけど、今後は git maintenance run --task gc にしようという気持ちになりました。(いや gc する必要はなくて incremental-repack で十分そうなので引数なしで打つと思う)

というか auto で動くので普段は意識しないか。

全般に「ユーザー操作の邪魔をしない」ように設計されているのかなという感想でした。

なんで気になったの

git の release note を Slack に流していて、v2.52.rc0 のタグが付いたのを見たので。

v2.52.rc0 を打ったコミット を見て、git maintenancestrategy: geometric って何だろう?って疑問からドキュメントを見てみた。という経緯でした。

*1:porcelain については Git - Plumbing and Porcelain を参照

Lambda + SQLite3 + Litestream + S3 でデータを保持してみた

できるかぎり AWS で Web アプリケーションを安く作れないかな、という試みの一環。アプリケーションの内容は特に重要ではなく、適当な Rails アプリです。

Litestream が Kaigi on Rails 2025 でも言及されていた ので、一回使ってみたくてやった、という内容です。

Litestream とは。SQLite の WAL を外部ストレージにストリーミングするツールです。概ねオブジェクトストレージ (今回は S3) へのレプリケーション (かつ PITR) が実現できると思えばヨサソウ。バイナリをポン置きで動くのも魅力。

litestream.io

背景と目的

Rails (に限らないかな。SSR するアプリ) を提供しようと思うと、少なくともアプリケーションサーバとデータストアが必要になる。

アプリケーションサーバについては、Lambda を使うと無料枠もあり、非常に低コストで運用できる。 データストアは RDS を使うと確実だが、継続して立ち上げ続けることになるのでコストが跳ね上がる。SQLite をローカルで使うと無料になるし、SQL から離れることになるが DynamoDB を使うと無料枠があるので安く運用可能である。

つまり Lambda + SQLite で構成すると、最小コストで運用できるはず、となるが、Lambda は実行環境が消えるという問題がある。

Lambda や ECS のようなサーバーレスな環境だと、タスク終了時にローカルストレージも破棄される。このせいでタスクが変わるたびにデータが消えてしまう。

対策としては

  • Lambda + EFS マウント
  • Lambda + S3 + Litestream (今回の構成)

になるんじゃないかな。ローカルのデータをリアルタイムにリモートにも送っておけば、生まれ変わっても引き続き使える、という対策です。EFS もまぁやったら動くと思う。

SQLite にはもう一つ課題があって、Lambda をスケールアウトできない。EFS のように複数 Task からマウント可能でも、SQLite への同時書き込みでデータが壊れる (はず)。 今回は Lambda の同時実行数 (reserved_concurrent_executions) を 1 に設定することで、単一の Task のみが SQLite にアクセスする構成にした。

アーキテクチャ概要

  1. Lambda 起動時に、S3 からリストアする
  2. アプリケーション起動前に Litestream でレプリ開始
  3. Rails アプリがリクエストを処理し、データを読み書きする
  4. SQLite への書き込みが発生したら、Litestream が随時 S3 にストリーミングする
  5. Lambda が終了しても S3 上に最新の状態が残っている

Lambda のコールドスタート時に毎回 restore が走るので起動が遅くなるが、まぁそもそもコールドスタートだからな。

Rails からは普通に sqlite3 として使うだけ。Litestream でのリストアやレプリケーションは Docker の entrypoint でいい感じにやります。

# Restore database replica if available
if litestream restore -if-db-not-exists -if-replica-exists -config /rails/config/litestream.yml /tmp/storage/production.sqlite3; then
  echo "Database restored from S3"
fi

# Start Litestream replication && exec puma
litestream replicate -config /rails/config/litestream.yml \
  -exec 'puma -b "tcp://0.0.0.0:8080" -e production'

db:migrate

reserved_concurrent_executions = 1 で運用しているため、db:migrate が実行できない。まぁ 2 個立ち上がるとデータ壊れるので正しいんだけど。

なので、以下の手順で migrate を実行している。(大変めんどくさいので migrate は Lambda 起動時にやっちゃうかも……)

  1. Lambda の前段 (CloudFront) でメンテに入れる
  2. Lambda の reserved_concurrent_executions を一時的に 0 に設定。今来ているリクエストを処理しきるまで待つ
  3. reserved_concurrent_executions を 1 に戻して、bin/rails-remote db:migratebin/rails-remote runner '<スクリプト>' を実行
    • bin/rails-remote は Lambda の環境変数RAILS_COMMAND を設定して invoke するスクリプトです
    • entrypoint では、litestream restore 後 puma 起動前に、RAILS_COMMAND が設定されている場合は litestream replicate -execrails コマンドを実行して終了する
  4. メンテを明け、HTTP リクエストを受け付ける

コスト感

全体構成としては CloudFront + Lambda FunctionUrl + SQLite3 + Litestream + S3。

CloudFront も Lambda も S3 もほぼ無料なのでほぼ無料で運用できている。データが増えてきたときに restore 時間がどう延びていくかはここから眺めていこうかな。まぁいい構成じゃないかしら。

その他

Litestream v0.5.0

ついこのあいだ (2025-10-02) v0.5.0 が出て、v0.3 系から migrate する必要がありました。

fly.io

litestream gem

litestream gem については特に使っていません。起動は Docker の entrypoint で管理していて苦労していないし、プロセスの親子関係としては Litestream が親の方がデータが正しくなりそうだし、これでいいかなーと思って。

非同期処理

ActiveJob は今のところ使ってないんだけど、使うとしたら solid queue の puma plugin でやるんだと思う。

バックアップ

まだ数日しか経ってないのでちゃんと見てないけど、S3 に snapshot が残っていそうなので、これで運用できるんじゃない……か?

redirect_to

CloudFront の裏に FunctionUrl で Lambda を置いていると、Host ヘッダが FunctionUrl のものになる。 redirect 時に CloudFront のドメインじゃなく FunctionUrl のドメインに飛んでしまって困った。

config.action_controller.default_url_optionshost を設定して、redirect_to foo_path じゃなく foo_url を使うようにして処理した。

ここで Rails 8.1 (最近出ましたね!) にちょうどいい機能が入っているので、有効に使うと良いんじゃないかな。

Allow hosts redirects from `hosts` Rails configuration by byroot · Pull Request #55420 · rails/rails · GitHub

感想

データストアを意識する必要がない、アプリケーションの Task だけ考えれば良いって環境、異常に気楽ですごい。これからは Solid シリーズが流行るだろう。

kinoppyd のトーク をぜひ聞いてみたい。

tech.smarthr.jp

参考URL

Rails in 1.44MB Challenge #kaigionrails_fd0 に参加した

osyoyu さんが企画したイベント ですね。

Rails in 1.44MB Challenge とは

Rails一式をフロッピーディスク(1.44MB)に収めてください。会場でフロッピーディスクに書き込んで動かしてプレゼントします!

Railsは結構でっかいフレームワークです。依存しているGemを全部足しあげると50MBぐらいになります。機能削減・圧縮・ズル、あらゆるテクを駆使して1/30のサイズにしてください!

ということで、50MB を何とかして 1.44MB に削るコンテストです。Kaigi on Rails 2025 のブース企画の中?いやほとんど個人企画だな?で開催されていました。

達成できたのか

$ du -sh .
1.3M    .
$ ./bin/rails s
=> Booting WEBrick
=> Rails 8.0.3 application starting in development http://localhost:3000
=> Run `bin/rails server --help` for more startup options
[2025-09-29 14:04:17] INFO  WEBrick 1.9.1
[2025-09-29 14:04:17] INFO  ruby 3.4.5 (2025-07-16) [arm64-darwin24]
[2025-09-29 14:04:17] INFO  WEBrick::HTTPServer#start: pid=27522 port=3000
Started GET "/?name=onk" for 127.0.0.1 at 2025-09-29 14:04:25 +0900
Processing by HomeController#index as HTML
  Parameters: {"name" => "onk"}
Completed 200 OK in 0ms (Views: 0.0ms | GC: 0.0ms)


127.0.0.1 - - [29/Sep/2025:14:04:24 JST] "GET /?name=onk HTTP/1.1" 304 0
- -> /?name=onk

で、達成としてもヨサソウ。

改めてレギュ読んでたら「Ubuntu で動く」とあって、やっべー忘れてた、となっているのが今です。試しにビルドしたらダメっぽかったので重い C 拡張 gem を殺さなきゃ……。

Rails が動く」の私の定義

普通の controller が動く、をゴールにしたい。

# config/routes.rb
Rails.application.routes.draw do
  root "home#index"
end
# app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
    render plain: "Hello, #{params[:name] || "World"}!"
  end
end
$ rails s

で、http://localhost:3000/ にアクセスして render されること。

Started GET "/" for 127.0.0.1 at 2025-09-29 13:47:39 +0900
Processing by HomeController#index as HTML
Completed 200 OK in 0ms (Views: 0.1ms | GC: 0.0ms)

理想的には scaffold の generator が動いて、その機能を全部網羅する、がやりたいけど、いつも通りの場所にアプリケーションコードを置けて、routing と render :plain があれば許せる。

rails s で起動させることを考えると railties も要るし、app/* を読み込むことを考えると zeitwerk も要るだろう。

状況確認

rails gem は依存多すぎるので入れない、actionpackrailties は必要として、これだけに削って、vendor/bundle を tgz で固めると達成できるのか。

source "https://rubygems.org"
gem "actionpack"
gem "railties"
$ bundle config set path "vendor/bundle" && bundle install
$ du -sh vendor/bundle/ruby/3.4.0/*
 40K    vendor/bundle/ruby/3.4.0/bin
  0B    vendor/bundle/ruby/3.4.0/build_info
7.4M    vendor/bundle/ruby/3.4.0/cache
  0B    vendor/bundle/ruby/3.4.0/doc
3.7M    vendor/bundle/ruby/3.4.0/extensions
 24M    vendor/bundle/ruby/3.4.0/gems
4.0K    vendor/bundle/ruby/3.4.0/plugins
184K    vendor/bundle/ruby/3.4.0/specifications

vendor/bundle 以下には cache/ に gem ファイルがそのまま置いてあったりするので固めるのが面倒だな。とりあえず extensions が 3.7M、gems が 24M あることが分かる。

$ rm -rf vendor/bundle/ruby/3.4.0/cache
$ tar czf vendor_bundle.tgz vendor/bundle
$ ls -hl vendor_bundle.tgz
-rw-r--r--  1 onaka  staff   6.1M  9 29 13:28 vendor_bundle.tgz

と最小の Gemfile で入ったものを tgz で固めても 6.1MB あるので、依存をもりもり削らないと達成できないことが分かる。なお試しに zstd や brotli にしても大差なかった。

この時点で C 拡張の重いヤツ:

$ du -sh vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/*
172K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/bigdecimal-3.2.3
292K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/date-3.4.1
 60K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/erb-5.0.2
 88K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/io-console-0.8.1
2.9M    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/nokogiri-1.18.10
 80K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/psych-5.2.6
 60K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/racc-1.8.1
 88K    vendor/bundle/ruby/3.4.0/extensions/arm64-darwin-24/3.4.0/stringio-3.1.7

bigdecimal, date が重いのは、いやー外して動くのか?

ruby コードとして重い gem トップ 10 はこう。

$ du -s vendor/bundle/ruby/3.4.0/gems/* | sort -rn | head
14296   vendor/bundle/ruby/3.4.0/gems/nokogiri-1.18.10
5168    vendor/bundle/ruby/3.4.0/gems/rdoc-6.14.2
3440    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3
3368    vendor/bundle/ruby/3.4.0/gems/activesupport-8.0.3
2840    vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5
2792    vendor/bundle/ruby/3.4.0/gems/actionpack-8.0.3
2272    vendor/bundle/ruby/3.4.0/gems/actionview-8.0.3
1392    vendor/bundle/ruby/3.4.0/gems/date-3.4.1
1040    vendor/bundle/ruby/3.4.0/gems/irb-1.15.2
1024    vendor/bundle/ruby/3.4.0/gems/rack-3.2.1

nokogiri や rdoc はまぁ削れるだろう (勘) けど、railtiesactivesupport も削る必要がありそうな予感がありますね。

依存 gem を削減したい

Bundler には以下のように fork を指定する機能がある。

# GitHub 上の fork を指定したいとき
gem "foo", github: "user/name", ref: "commit"
# ローカルの任意の path を指定したいとき
gem "bar", path: "path/to/gem"

ので、各 gem の gemspec を真面目に書き換えながらやるかなーと考えたけど、publish されている gem ってテストコード削っていたり、割と丁寧目に gem 自体が軽くなるような処理がされているンですよね。

# actionpack.gemspec
Gem::Specification.new do |s|
  ...
  s.files        = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*"]
  ...
end

lib だけにするとかを再現するのが面倒そうなので、

  • bundle install したあとのコードを直接編集していく
  • bundle を介さずに gem を読み込む

という作戦にしました。

bundle を介さずに gem を読み込む

$LOAD_PATH を弄って require するのは知っていたので、claude code にに丸投げした。(Kaigi on Rails の発表も聞きたいからね……)

コードとしては Bundler 以前の世界というか、普段 rubygems 作るときも exe とか spec_helper に同じコードは書くよねーって感じですね。

# config/boot.rb
gem_path = File.expand_path("../vendor/bundle/ruby/3.4.0/gems", __dir__)
Dir.glob("#{gem_path}/*").sort.each do |gem_dir|
  lib_path = "#{gem_dir}/lib"
  $LOAD_PATH.unshift(lib_path) if File.directory?(lib_path)
end

実際の gem 削減

vendor/bundle/ruby/3.4.0/gems/foo-x.y.z/ を消して動けばヨシ、動かなかったら require を見つけては殺す、みたいな仕事です。これも claude code にどんどん投げてる。

例えば actionview を外したかったら、https://github.com/rails/rails/blob/v8.0.3/actionpack/lib/abstract_controller/rendering.rb#L6-L7 とか https://github.com/rails/rails/blob/v8.0.3/actionpack/lib/abstract_controller/rendering.rb#L20 とかを消していく感じです。

diff --git a/vendor/bundle/ruby/3.4.0/gems/actionpack-8.0.3/lib/abstract_controller/rendering.rb b/vendor/bundle/ruby/3.4.0/gems/actionpack-8.0.3/lib/abstract_controller/rendering.rb
index 346bb4e..e9ebcc7 100644
--- a/vendor/bundle/ruby/3.4.0/gems/actionpack-8.0.3/lib/abstract_controller/rendering.rb
+++ b/vendor/bundle/ruby/3.4.0/gems/actionpack-8.0.3/lib/abstract_controller/rendering.rb
@@ -3,8 +3,8 @@
 # :markup: markdown
 
 require "abstract_controller/error"
-require "action_view"
-require "action_view/view_paths"
+# require "action_view"
+# require "action_view/view_paths"
 
 module AbstractController
   class DoubleRenderError < Error
@@ -17,7 +17,7 @@ module AbstractController
 
   module Rendering
     extend ActiveSupport::Concern
-    include ActionView::ViewPaths
+    # include ActionView::ViewPaths
 
     # Normalizes arguments and options, and then delegates to render_to_body and
     # sticks the result in `self.response_body`.

railties がデフォルトで入れてくる Rack middleware が邪魔だなーって消したのもある。

https://github.com/rails/rails/blob/v8.0.3/railties/lib/rails/application/default_middleware_stack.rb#L14-L110

あと、このコード見たら分かるけど、結構 if config.respond_to?(:active_record) 等がある。下手に config.acitve_record = ActiveSupport::OrderedOptions.new 等で起動時のエラーを回避しようとすると面倒なことになるので気をつけて。なので config/environments/*.rb の各行が何をやっているのかは知っておいた方が便利と思う。

concurrent-ruby 外し

nokogiri や rdoc、actionview 辺りはどこで使っているのか明確だし外せる自信があったのでサクッと作業したけど、concurrent-ruby はちょっと悩んだ。300KB 以上あるので外すのがいいに決まっているが、外せるのか。rails 全体の中で 34 件しか require されてないならまぁ行けるのかなーと思って取りかかった。

$ rg 'require "concurrent/' -l | wc -l
      34

だいたい

-Concurrent::Map.new
+Hash.new

の書き換えでいけるやろと思って作業させました。概ね行けたけど、僕まだ Concurrent::Map の実装読んだこと無いンだよな。そもそも git clone すらしてなかったと思って今回は反省した。

minifyrb

koic/minifyrb の存在を知っていたので、コレで適当にガーッと。

$ fd --type f "\.rb$" | xargs -I{} sh -c "minifyrb {} -o {}" 

koic.hatenablog.com

gzip する前にコメント行を削除するのが効くといいなと思ってやりました。コメントを削るだけなら他にやりようはあったと思うけど、目の前に落ちてたのでお手軽だった。

とりあえず ActiveSupportActionPack 全部に掛けたけど、何ファイルかは壊れたので戻してます。(フィードバックチャンス)

railties から generator 削除

railties でっけーなー、減らせるかな? と思って眺めたら、generators ディレクトリが 1MB って出たので。

$ du -hd 3 vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/
8.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/minitest
 32K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/tasks
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/plugin
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/rackup
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/testing
 28K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/test_unit
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/autoloaders
8.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/rack
 12K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/api
 44K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/templates
164K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/commands
 24K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/command
1.0M    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/generators
 56K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/application
8.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/railtie
 24K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/engine
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails/console
1.6M    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib/rails
1.6M    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/lib
4.0K    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/exe
1.7M    vendor/bundle/ruby/3.4.0/gems/railties-8.0.3/

もう 60% ぐらい generator じゃん。いやー、generator が rails の肝なんだなーって実感しますね。

tgz から起動

ソラで書ける自信無かったのでまるっと claude code に任せた。

tar czf vendor_bundle.tgz vendor/bundle した vendor_bundle.tgz があります。起動時に伸張するように config/boot.rb を書き換えてください

一発目は system("tar zxf ...") するコードが出てきたので、「pure ruby でできる?」って聞いたら zlib は予想通りとして、Gem::Package::TarReader が出てきた。これは初知りライブラリでした。

github.com

感想

数時間で遊べる、ちょうどいい難易度のクイズだった。osyoyu さんありがとうございます。

Gemfile.lock は普段から眺めているのでまぁまぁ何に依存しているか知っている、という前提を置けるのが良かったね。レギュB の Ruby 自体を最小化する方は普段から (ruby-build に任せずに) ビルドしまくってないと勘所が掴めなそう。

Bundler 使わずに gem 読むの 10 年ぶりに手書きしてるなーとか、actionview とこう密結合してるのかーとか、concurrent-ruby こんなでっかいの!とか、イマドキの gem は test 系の不要ファイルを本当に含んでないなとか、date や bigdecimal が gemify される前ならこんな苦労はとか、トークを聞きながら AI Agent に任せる粒度としてちょうどいいとか、面白かった点がいくつもあった。

皆さんもやってみてください :)

しかし、継続的に頭を使う必要があるとトークを聞けなくなるので、裏企画はあんまり増えないで欲しい!(>_<) これは途中で「やばっ、話している内容が頭に残ってねぇ」と気づいて焦った顔で発言しています。 アルコールを多少控えてホテルに帰ってから手を動かせば良いのはそうなんだが……。

京都で私が好きな場所

こんにちは。関西Ruby会議08オーガナイザー、Kyoto.rb 所属の id:onk です。 健康診断の再検査会場で待ち時間にヒマなのでスマホから書いています。みんなも予選突破したら検査行こうね。私は何年か放置していました。

日本一雅な松屋

このツイートで有名になったヤツ。

ただの松屋ですが、ホテルの1階なので庭が良い。ついったー映え写真にどうぞ。

会場からは徒歩15分ぐらい。烏丸線に乗りたいときとかに移動中に撮るといいんじゃないかな。

tabelog.com

ふる里

普通の居酒屋だけど、生搾りサワーがすごいで有名な店。アルコールの入ったスムージーです。

デイリーポータルZにも取り上げられていたね。僕もこれで知って行きました。

dailyportalz.jp

当日は大宮まで移動することまず無いだろう (会場からは徒歩30-40分) から、前後の日 (今日とか) にどうぞ。

琴ケ瀬茶屋

嵐山の穴場的な飲み屋。川のほとりで酒が飲めます。めっちゃいいので適当に画像検索してみて。

渡月橋を渡ったら人の居ない方に数分歩いて行くとあります。 橋を渡らずにボートで行くのも観光っぽくていいんじゃないかなと思うけど僕はボート未経験。

営業日分かってなくて、Day2 に営業してるかは自信ない。たぶんやってるはず……。

tabelog.com

キッチンゴン

知る人ぞ知る京都名物ピネライス。 チャーハンにトンカツを乗せてカレーをかけてある、小学生の考えた最強料理みたいなヤツです。 量は普通なのでビビんなくて大丈夫。

六角店は行きやすいと思う。徒歩7分ぐらい?

tabelog.com

天下一品 総本店

僕もまだ行ったこと無いので興味ある人いたら一緒に行きましょう

tabelog.com

限定の牛すじラーメンがオススメトノコト

www.tenkaippin.co.jp

餃子の王将 1号店

ただの王将ですが発祥の地の石碑はあります。 大宮駅前。

tabelog.com

オシャレ王将

王将で思い出した。日本に4軒しかないオシャレ王将が京都にあります。

www.ohsho.co.jp

ワイングラスが逆さまになって天井からぶら下がってるレベルのオシャレ度。 湯葉天津飯とか、せいろ蒸しとか、ごま団子とかがオシャレメニューかなぁ。

会場からは 20 分ぐらい。これも烏丸御池駅に行きたいときにどうぞ。

tabelog.com

池田屋 はなの舞

新撰組のあの池田屋騒動跡地が、はなの舞 (大衆居酒屋ですね) になってます。 どの辺が池田屋かと言うと、大階段があって、店員さんがダンダラ羽織着てるぐらい。

会場からは徒歩 3 分ぐらい?

池田屋騒動之址って石碑もある。

tabelog.com

すがり

一番京都感というか一見さんお断り感(?)を感じられるラーメン屋だと思う。

まず店の外観に看板やら暖簾やらのお店要素が無い。知ってなきゃドア開けられない系。 中は京町家の雰囲気を存分に楽しめると思う。 一方通行の構造で、他の人とすれ違わないで出られるようになっているのも「京都」っぽさのポイント高い。(入口と出口が別。)

tabelog.com

すがり近所に2店あって、どっちも京都感あると思うけど、会場に近い六角の方のすがりが僕はオススメです。キャッシュレスオンリーなのに注意。

貴船神社Wi-Fi

貴船神社のフリーWi-Fiが繋ぎたくなるいい名前なので見て行ってくれ。もし行くならぜひ小ネタにどうぞ。

貴船口との間にプログラマーが好きそうな名前のカフェもあります

tabelog.com

NINTENDO KYOTO

ニンテンドーショップです。近いし京都っぽい (任天堂さん、京都の会社ですね) し、いいんじゃないかな。

限定品もあるよ

www.nintendo.com

本能寺跡

街中に石碑があるぐらい。「ああそれも京都だっけ」って思い出して貰おうと思って書きました。

本能寺は本能寺の変のあとに移転しているので、現本能寺と本能寺跡は場所が違うことに注意。二条城との位置関係が全然違う。

羅生門

これもガッカリポイントとして有名。

にしんそば

京都以外で見ないし、京都名物だろう。魚がまるっとそばの上にのってるビジュアル見たことない人はぜひ。

純喫茶

本当にいっぱいあるので好きなところにどうぞ

鴨川

等間隔のカップル見たり、トンビにサンドイッチかっ攫われたりしましょう

割と普段から普通に鴨川ビールしてます。「川」Origin だし、当日夜もやるんじゃないかな。

(余談)デルタの勢力図はとても好き。

今日めっちゃ雨だったので、流れてきたオオサンショウウオ見られるかもしれないですね。

クラフトビール

エントリあるので見てください!

note.com

補足もどうぞ。(長文なので開いて見てね)

いくつかは前夜祭に持っていきます。

ランチ情報

エントリあるので見てください!

note.com

それでは楽しい関西Ruby会議08を~

macOS Sequoia (15.4 以降) で cal や date を打つと出力がおかしい

2025-07-30 追記
macOS 15.6 で ja_JP が直っているのを確認しました。助かる!!!
(ko_KR も同じ原因で同じ現象なんだけど、そっちはそのままなんだ……。別途報告しよう……)

$ cal
      3月 2025
日 月 火 水 木 金 土
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

5 月なのに 3 月と言われる。

$ date
#午後

date#午後 という文字列が返ってくる。

状況は cal -y するとある程度理解できて、

$ cal -y | grep "[0-9]月"
         PM            %Y年 %B%e日 %A %X %Z            1月
         2月                    3月                    4月
         5月                    6月                    7月
         8月                    9月                   10月

と、本来「1月」「2月」となるべきところに「PM」「%Y年 %B%e日 %A %X %Z」が入り込んでいる。どこかで何らかのズレが生じているのだろう。

どうやら日本語だけの問題っぽく、LANG を変えると正常に出力される。

$ LANG=C cal
      May 2025
Su Mo Tu We Th Fr Sa
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
$ LANG=C date
Thu May 22 02:01:25 JST 2025

これはもう LOCALE の問題であろう。と /usr/share/locale/ 以下を眺めると、ja_JP.UTF-8 を含むいくつかの言語ファイルで行数が違うのが見える。

$ wc -l /usr/share/locale/*.UTF-8/LC_TIME
...
      58 /usr/share/locale/is_IS.UTF-8/LC_TIME
      58 /usr/share/locale/it_CH.UTF-8/LC_TIME
      58 /usr/share/locale/it_IT.UTF-8/LC_TIME
      60 /usr/share/locale/ja_JP.UTF-8/LC_TIME
      58 /usr/share/locale/kk_KZ.UTF-8/LC_TIME
      60 /usr/share/locale/ko_KR.UTF-8/LC_TIME
      58 /usr/share/locale/lt_LT.UTF-8/LC_TIME
      58 /usr/share/locale/lv_LV.UTF-8/LC_TIME
      58 /usr/share/locale/mn_MN.UTF-8/LC_TIME
...

en_US.UTF-8ja_JP.UTF-8 を見比べると、確かにおかしい。

$ cat -n /usr/share/locale/en_US.UTF-8/LC_TIME
...
    38  Saturday
    39  %H:%M:%S
    40  %m/%d/%Y
    41  %a %b %e %X %Y
    42  AM
    43  PM
    44  %a %b %e %X %Z %Y
    45  January
    46  February
    47  March
...
$ cat -n /usr/share/locale/ja_JP.UTF-8/LC_TIME
...
    38  土曜日
    39  %H時%M分%S秒
    40  %Y/%m/%d
    41  %a %b/%e %T %Y
    42  #午前
    43  AM
    44  #午後
    45  PM
    46  %Y年 %B%e日 %A %X %Z
    47  1月
...

先ほど cal -y で出力されていたのもこの 45 行目、46 行目ですね。

じゃあ /usr/share/locale/ja_JP.UTF-8/LC_TIME を書き換えたら直るんじゃないかと思ったら sudo でも編集できない。SIP (System Integrity Protection) によって守られてるんだった。

ここまで見て「面白い〜」と社の Slack で共有したところ、id:KashEight から「man setlocale が参考になりそう?」という反応を貰った。

$ man setlocale
...
FILES
     $PATH_LOCALE/locale/category
     /usr/share/locale/locale/category  locale file for the locale locale and the category category.
     /usr/local/share/locale/locale/category
                                        locale file for the locale locale and the category category.
...

環境変数 $PATH_LOCALE を見ているので、リカバリーモードで無理矢理 /usr/share/locale/ 以下を書き換えなくてもどうにかなりそう。

$HOME/.config/locale/ 以下に ja_JP.UTF-8/ をまるっとコピーして、LC_TIME を弄る。

$ diff -u {/usr/share,$HOME/.config}/locale/ja_JP.UTF-8/LC_TIME
--- /usr/share/locale/ja_JP.UTF-8/LC_TIME       2025-04-12 14:16:09
+++ /Users/onaka/.config/locale/ja_JP.UTF-8/LC_TIME   2025-05-17 13:51:58
@@ -39,10 +39,8 @@
 %H時%M分%S秒
 %Y/%m/%d
 %a %b/%e %T %Y
-#午前
-AM
-#午後
-PM
+午前
+午後
 %Y年 %B%e日 %A %X %Z
 1月
 2月

環境変数を設定して、caldate を打つと……

$ export PATH_LOCALE=$HOME/.config/locale
$ cal
      5月 2025         
日 月 火 水 木 金 土  
             1  2  3  
 4  5  6  7  8  9 10  
11 12 13 14 15 16 17  
18 19 20 21 22 23 24  
25 26 27 28 29 30 31  
$ date
2025年 5月22日 木曜日 02時19分11秒 JST

やったぜ!

以下のディスカッションも見つけました。Sequoia 15.4 以降のバグっぽいのでフィードバックしておきましょう。先日出た 15.5 でも直ってない!

Mac - ターミナルにて2ヶ月前のカレンダーが表示されます - Apple コミュニティ

株式会社はてなに入社しました

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記

8 年目。コード書かないマネージャーを 2 年やってるので、そろそろ戻る算段付けないとな

irumo のギガを Mackerel で監視する

この記事は Mackerel Advent Calendar 2024 の 9 日目の記事です。

普段使いのモバイル回線は docomo というか irumo なんですが、自宅から離れて生活するとギガを使い切って買い足す日々を送っています。

11 月は 3 GB 買い足した

データ通信量ってなんか上手いことアラート出せないですよね。2GB 使った後に「使ってるよ」って言われても困る。というわけで Mackerel で監視するようにしました。

まずはブラウザから https://irumo.docomo.ne.jp/ を触って Developer Tools の Network タブを眺める。

日ごとの数字が入っていそうな JSON 発見

https://irumo.docomo.ne.jp/api/uw/tra/v1.0/getdatareferenceinf への POST で JSON が返ってきているっぽいのが分かったので、リクエストヘッダをどこまで削っても取れるかを試す。

今回は spsp Cookie だけ渡せば動くっぽかったです。

curl 'https://irumo.docomo.ne.jp/api/uw/tra/v1.0/getdatareferenceinf' \
-H 'User-Agent: DataUsageCheckerBot/1.0 (@onk)' \
-H 'Cookie: spsp=xxxxxxxx' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'requestpatterncode=0009&getuser_div=3'
{
  ...
  "data": {
    ...
    "daydatainfo": {
      ...
      "dayusedatainfo_list": [
        {
          "targetdate": "20241201",
          "daydatadetailinfo": {
            "databypacket": "973737",
            "databygb": "0.93",
            "speedlimitreleasecount": "0"
          },
          "speedlimitreleasecountinfo": {
            "databypacket": "0",
            "databygb": "0.00"
          },
          "highspeeddatainfo": {
            "databypacket": "973737",
            "databygb": "0.93"
          }
        },
        ...

各日のデータに speedlimitreleasecount として買い足した回数が入ってるのがちょっと面白い。highspeeddatainfospeedlimitreleasecountinfo はそれぞれ通常時と速度制限時で使った通信量ですね。

JSON が取れたらあとは加工して Mackerel に送れば完成しそう!

値を取るのは jq でエイってやります。

... | jq -r '
.data.daydatainfo.dayusedatainfo_list[] |
 select(.targetdate | test("20241201")) |
 .daydatadetailinfo.databypacket'
973737

それっぽい数字が取れたので送信用にちょっと加工。

まずハードコードして試していた test("20241201") の部分は外から --arg today $(date '+%Y%m%d') と渡します。

また、Mackerel 向けに投げるならタブ区切りの文字列にしたいですよね。\(...) で String interpolation できるし、now で現在時刻を錬成できるので、全体はこうなります。

... | jq -r --arg today $(date '+%Y%m%d') '
.data.daydatainfo.dayusedatainfo_list[] |
 select(.targetdate | test($today)) |
 "custom.irumo.data-usage\t\(.daydatadetailinfo.databypacket)\t\(now)"'
custom.irumo.data-usage 908991  1733754915

あとは mkr throw で投げて完成です。

... | mkr throw --service irumo

グラフができました

データが溜まったら線形回帰関数を使ってアラートを投げたりして遊びましょう。身の回りのメトリックっぽいデータはとりあえず Mackerel に投げておくと、そのうち使い道を思いつくものです。

Mackerel Advent Calendar 2024、明日は id:taxintt さんです。

追記

1 日のデータ取り出しても毎日 0 から始まってしまうので、今月のデータを合計する必要がありました。

jq -r --arg month $(date '+%Y%m') '
  (.data.daydatainfo.dayusedatainfo_list |
  map(
    select(.targetdate | startswith($month)) |
    .daydatadetailinfo.databypacket | tonumber
  ) | add) as $total |
  "custom.irumo.data-usage\t\($total)\t\(now)"
'