おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

ginza.rb 第96回を開催してFizzyについて学んだ

Ginza.rb 第96回 - Fizzyソースコードリーディング - connpass

第96回は37signals社が公開したFizzyのソースコードを見ながらワイワイしました。

basecamp/fizzy: Kanban as it should be. Not as it has been.

fizzyというビジネスに対する感想

第96回を開催したタイミングでは「1000カード以上を作ろうとすると20ドル/月」という課金体系でした。ユーザ数などは無制限でだいぶ安い。さらにコードが公開されているのでオンプレでも動かせる。オンプレからクラウド、クラウドからオンプレへの移行もやりやすいようにuuid7で作っていて(それぞれが稼働している状態でデータをマージしてもidが衝突しない)、ユーザとしてみたら最高便利なんだけどサービス提供側からしたらこれで採算取れるのかな?と思っていたら完全無料のサービスになってしまって驚きました。元々どういうビジネスプランだったのか気になるところです。

設計や実装に対する感想

色々気になった点を列挙しています。

  • 大まかなコード体系としてはbasecamp/once-campfireと似ています(同一の会社のプロダクトなので当たり前だけど)。最近のプロダクトなのでAGENTS.mdだったりSTYLE.md)だったりとAIエージェントに読ませるためのファイルがあるのが違い。
  • 大きな特徴としてはsqlite3とmysqlのどちらでも使える構成だということ。元々はsqlite3で実装する予定だったけど途中で厳しいことがわかりSaaS版はmysqlにしたというのをどこかで読みました。
  • 全文検索をDBで実装しているというのも珍しいところです。一昨年のRails WorldでDHHが話していたActive Record Searchの原型はこれなんだろうと思っています。しかし実装としてはMySQLとsqliteの全文検索機能を比較的素朴に使っており、単語はスペースで区切られるのが前提になっているため日本語でFizzyを使うと検索はまともに動きません。残念><
  • seedの定義の仕方も面白いです。まずseed用のDSLを定義して、各テナントでDSLを利用してレコードを作っています。
  • マルチテナントなSaaSとしてテナントを分けるのをRackミドルウェアで実装している、というのも個人的には面白い点だと思いました
  • あとはライセンスが興味深かったです。基本MITと同等だけど競合サービスを作るのは禁止というO’Saasyライセンスを新設しています。
  • koicさんも書いてたけどzip_kitを知れたのは収穫でした。

次回

次回は5月15日(金)に、RubyKaigiの振り返りをやります。興味のある方はぜひご参加ください。

Ginza.rb 第97回 - RubyKaigi 2026を振り返る - connpass

seed-doのv4.0.0をリリースしました

seed-do v3.2.0をリリースしました - おもしろwebサービス開発日記 で書いていた速度改善案を実装してv4.0.0としてリリースしました。

rubygems.org

SeedDo.seed(bulk: true)とするとRailsのupsert_allを使用して一括でレコードをUPSERTできるようにしました。

PRはこちら

github.com

大半の人は、単にSeedDo.seedSeedDo.seed(bulk: true)に置換するだけでseedの実行が速くなるんじゃないかな、と期待しています。ただし upsert_all:unique_byオプションを利用している関係でPostgreSQLとSQLiteでしか利用できません。MySQLユーザの人はごめんなさい。

また、この機能を入れるにあたって内部APIの整理をした関係で非互換が発生したのでメジャーバージョンアップをしています。内部APIを使ってなければ関係ないはず。

利用してみて、何か感想などあれば教えてもらえると嬉しいです。

株式会社ウィルネットは設立9周年を迎えました

2月21日は弊社の設立記念日でした。もう会社運営も10年目。時間が過ぎるのはあっという間ですね…。

去年のエントリはこちら:

blog.willnet.in

法人9年目を振り返る

2025年の弊社(僕)の活動を振り返ると、OSS活動をよくやっていた印象があります。仕事の関係で見ていたライブラリのメンテが滞っていて仕方ないから直すか〜というのを繰り返していたらいつの間にかメンテナになっていたりしました。AI AgentのおかげでコミットメッセージやPRの英語に困らなくなったというのが要因として大きい気がします。

メンテされないgemの向き合い方についてfindyさんに寄稿したりもしました。

findy-code.io

あとはブログのエントリ数が2024年の倍以上あるのでまあまあアウトプットを頑張っていそう。お仕事の一環で僕が新しく知ったり学んだりした情報を共有するというのを継続してやっていて、そこでのアウトプットで満足してパブリックな状態にはせずにいたのですがやっぱりパブリックにもした方がいいよね、ということで時間や気力的に可能な分だけ公にしています。特にRails8.1のマイナーフィーチャーをまとめました みたいなやつがパブリックになっていると自分でも便利なのでもっとやっていきたい(時間があれば…)。

最近はAI Agentのおかげで時間がない中でもちょっとしたタスクを進めることができて楽しいです。仕事もプライベートもなんやかんやで忙しいのですが、顧問先に関しては週1日程度分を募集しています。興味ある方はお声がけください。

10年目も健康に気をつけつつ頑張っていきます (( ⁰⊖⁰)/)

ginza.rb 第95回を開催してRuby4.0について学んだ

Ginza.rb 第95回 - Ruby4.0について学ぶぞ - connpass

第95回はRuby4.0について学びました。ko1さんやmameさん、hsbtさんが書いてくれた資料を参考にみんなでわいわいしました。

感想

個人的にはRuby::Boxがどう自分たちの生活を変えていくのか、というのに興味があります。

例えば boxwerkのように、Ruby::Boxでパッケージを分けることでパッケージ間の疎結合を強制させるであるとか、Ruby::Box: Rethinking Code Reloading with Isolated Namespaces | Ruby Elders で書かれているような、アプリケーションのリロードをRuby::Boxを都度作り直すことで実現するとか。

Ruby::Boxが実際に我々の手元で動くようになったことで夢が広がりつつあるのを感じます。

次回

次回は3月13日(金)開催予定です。fizzyのソースコードリーディングをします。興味のある方はぜひご参加ください。

Ginza.rb 第96回 - Fizzyソースコードリーディング - connpass

SolidQueueがスレッドでも動くようになった

最近個人的にバックグラウンドワーカーとしてSolidQueueを使う機会が少しずつ増えています。そんな折SolidQueueのv1.3.0がリリースされたのですが、その中に個人的に興味深い機能が入ったので紹介しておきます。

前提

SolidQueueには複数の役割が定義されています。

  • Supervisor
  • Dispatcher
  • Scheduler
  • Worker

SolidQueueは開発初期では それぞれの役割がスレッドとして1プロセス内でも動くasync modeが用意されていました

が、実装をシンプルにするためにv0.4.0以降はasync modeが廃止され、各役割ごとに1以上のプロセスを割り当てるようになっています

SolidQueueはメモリ消費量がSidekiqよりも多かった

各役割ごとにプロセスが割り当てられるため、SolidQueueを利用する際には最低限の構成でも3〜4プロセス作られることになります*1。Sidekiqは1プロセスで動くので、Sidekiqだと問題ないサーバでもSolidQueueだとメモリが厳しいことが起こります。

ぼくもHerokuで512MBメモリのdynoを利用しworkerを運用していたのですが、SidekiqからSolidQueueへ移行する際にはメモリが足らず1GBメモリのdynoにアップグレードせざるを得ませんでした。

Railsは個人開発者が安いVPSを借りて簡単にデプロイし運用することができ大変便利なのですが、SolidQueueを利用する場合、メモリに関してはある程度大きめなもの(2GB〜)が必要になっていました。

async modeの復活

このメモリ消費量の問題で、async modeの復活をしたいという人が現れました。メモリ消費量を減らしたい!という要望だけではなかなか物事が前に進まなかったのですが、これまでforkが使えないのでSolidQueueを利用できなかったJRubyやWindows環境の人もSolidQueueを使えるようにしたい、というところで話が前に進んだようです。

そしてついにasync modeを復活させるPR がマージされました。このコミットは先日リリースされたv1.3.0に含まれています。

bin/jobs --asyncで起動するか、SOLID_QUEUE_SUPERVISOR_MODE=asyncとするとasync modeを利用するようになります。

READMEに次のように書かれているので、基本的にはプロセス(fork)を使う方が挙動が安定しそうですが、個人開発など限られた環境で使う分には便利ではないでしょうか。

The recommended and default mode is fork. Only use async if you know what you're doing and have strong reasons to

僕の環境でも600MBを超えていたメモリ消費量を260MBくらいまでに減らすことができました。やったね。

*1:Schedulerはrecurring jobが必要なければ使わない

Gonのv7.0.0をリリースしました

github.com

メジャーバージョンアップであることからもわかるように、非互換な変更が入っています。この日互換な変更は@krororoさんが作ってくれた次のPRによるものです。

Breaking: Make request_store an optional dependency by krororo · Pull Request #296 · gazay/gon

gonは、gon.foo = 'bar'のように値をセットすると、内部でrequest_storeを使って値を格納していました。昔のRailsアプリケーション開発において、リクエストごとに自動でリセットされるグローバルな値を利用したい時はrequest_storeがほぼ唯一の選択肢だったように思います。しかしRails5.2からはRails公式の機能であるActiveSupport::CurrentAttributes を利用できるようになっています。上記のPRはrequest_storeの依存を消してActiveSupport::CurrentAttributesを利用するためのものです。

何が嬉しいのか

安定性が増すと思っています。現時点でのrequest_storeは内部でファイバーローカル変数が使われています。このためリクエスト処理中にFiberを使うコードがあるとgonの挙動はおかしくなる可能性があります。

ファイバーローカル変数って何?という人はこちらを参照してください。

Thread.current[:hoge]はスレッドローカル変数を参照していると思いきや実際はファイバーローカル変数だった - おもしろwebサービス開発日記

ActiveSupport::CurrentAttributesも登場時はファイバーローカル変数を利用していました。しかしRails7.0からは自分が利用しているサーバの種類に合わせてファイバーローカルとスレッドローカルを切り替えられるようになり現在に至っています。

そのため現時点では、Rails7.0以上を利用しているのであればActiveSupport::CurrentAttributesを利用したほうが安定するはず。ということでこのPRをマージしました。

懸念点

gonを利用しているユーザの大半は自分がrequest_storeを使っているのかActiveSupport::CurrentAttributesを使っているのか特に意識することはないと思います。そのためRails5.2以上を利用していてActiveSupport::CurrentAttributesを利用できるのであればそれを利用し、そうでない場合は明示的にgem 'request_store'をGemfileに書いてもらうという仕様にしました。

最近のRailsでrequest_storeを明示的に使いたいケースはほぼないと思いこのようにしたのですが、もし不都合があったらぜひ教えてほしいです。

GitHub Sponsorsを始めました

始めてみました。

github.com

ここ最近はseed-fuをforkしたりgonのメンテナになったりSorceryのメンテナになったりと、メンテが滞っているライブラリの手助けをすることが増えてきました。あとCIが整っていないライブラリを見つけたら直すというのをちょくちょくやっています。

メンテされないgemとどう向き合うか。“普通のOSS開発者” willnetさんの取り組み - Findy Media | IT/Webエンジニアの転職・求人サイトFindy – GitHubからスキル偏差値を算出 にもCI改善の話を書きました。

僕はこれらのgemを直接自分では使っていない事が多いです。お手伝い先が使っていることがモチベーションになっていますが、将来的にお手伝い先がそのgemを使わなくなったらどうでしょう?僕は自分のメンテナンスしているライブラリが最新のRubyやRailsで動かなくなるのは絶対嫌なのでメンテナンスが止まることはない*1と思いますが、モチベーションは少し下がりそうです。

でも僕のメンテナンスしているライブラリを直接使っている他の人や会社が応援してくれたらもっとやる気が出そうだな、と思ったのでGitHub Sponsorsを始めてみました。

OSSの継続可能性について考えている

GitHub Sponsorsを始めたのにはもう一つ理由があって、それは「GitHub Sponsorsが備えている機能を使ってみたかった」です。

メンテが滞っているライブラリの手助けをするなかで、もっとOSSエコシステムをいい感じにできんもんかな、というのを時折考えます。例えば人気のOSSライブラリをメンテナンスしているひとにお金がちょっとでも入ってきたらメンテナンスが滞ることも減るんじゃないかなと思うのですが、現状そうなってはいません。

GitHub Sponsorsは我々開発者にとって一番身近な寄付の手段であるので、スポンサーを受ける側としてどういう機能があって、どのような手法で寄付を募ることができるのかを実際に使ってみることで知りたかったのでした。

実際に始めてみると、寄付してくれた人限定でprivateリポジトリの招待ができたり、ニュースレターを遅れたりする機能があるのを見つけました。なので

  • 毎月10ドル以上のスポンサーに活動報告を送ります
  • 毎月100ドル以上のスポンサーに公開前のOSSライブラリを閲覧できる権利を付与します

みたいなことはできそうです。実際やるかはさておき、いろんな手段があるのは良いですね。

まとめ

もし@willnetを応援してもいいぞ、という人や企業がいたらスポンサーのほどよろしくお願いします!

github.com

*1:僕が健康でいる限り