kikumotoのメモ帳

インフラ・ミドル周りを中心に、興味をもったことを適当な感じで。twitter : @takakiku

Ractorに入門してみた - Consumers/Producer はこんな感じ?編 -

以下での背景と失敗を踏まえて、元々やりたいことを書いてみた、という内容の記事になります。

kikumoto.hatenablog.com

やりたい事

  • ある処理をするためのデータが多数ある
  • それを並列に処理したい。
  • 並列数は固定でOK。
  • 結果は随時処理したいが、処理つの都合で1カ所でやりたい

みたいな感じです。

github.com

Worker pool のサンプルだと

(1..N).each{|i|
  pipe << i
}

で、全てのデータを渡してから、最後に結果をごそっと受け取るという感じで微妙にニーズと一致しませんでした。(結果を随時受け取っていきたい)

実装

そこで、実際のデータとか実処理の内容はのぞいて、並列処理部分はこんな感じ?というのを書いてみました。

def main()
  # 同時実行数
  c = 2

  producer = Ractor.new Ractor.current, c do |parent, c|
    puts "start producer"

    get_data.each do |d|
      Ractor.yield Ractor.make_shareable(d, copy: true)
      # sleep 1
    end

    # consumer に終了通知
    c.times do
      Ractor.yield :term
    end

    parent.send :producer_finished
  end

  consumers = (1..c).map do |i|
    Ractor.new producer, i do |producer, i|
      puts "start consumer #{i}"

      loop do
        d = producer.take
        break if d == :term

        puts "consumer_#{i}: #{d['id']}"
        Ractor.yield d['value']
      end

      # main Ractor への終了報告
      Ractor.yield :consumer_finished
    end
  end

  # consumer からの結果受け取り&終了待ち
  until consumers.empty?
    r, obj = Ractor.select(*consumers)
    if obj == :consumer_finished
      consumers.delete r
      next
    end
    puts "message: #{obj}"
  end

  puts "wait producer"
  Ractor.receive
end

def get_data
  [
    { 'id' => 1, 'value' => 'aaa'},
    { 'id' => 2, 'value' => 'bbb'},
    { 'id' => 3, 'value' => 'ccc'},
    { 'id' => 4, 'value' => 'ddd'},
  ]
end

main

一応、これで期待通りっぽく動いているのだけど、果たして。。。
詳しい人に教えて欲しい〜。

Ractorに入門してみた - 基本的なところでハマった編 -

背景

ruby で書き始めたちょっとしたツールで、処理する対象量の都合で並列実行したいな、となり、できればやはり複数 CPU 使いたいなということで、Ruby って Process 以外で今って何かできるんだっけと見たら Ractor というのがあるんですね。

Ractor 自体は

techlife.cookpad.com

などなど見てもらうのが良いと思います。なんせ、まだ私はわからないことだらけなので。

で experimental ということですが、作ろとしているツールは1回ぽっきり的なので、それなら勉強がてら(最近この手のもので新しいことを使う機会が少ないこともあり)使ってみようと思い手を付けてみました。

この記事は、やってみて、適当な理解なせいでしばらくハマった内容を書いたものです。 ちゃんと考えればわかるでしょ、的な自戒な意味を込めた記事。

なお、rubyのバージョンは以下。

$ ruby --version
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin19]

間違い実装

Consumers - Producer 的な感じなものを実装したくて、プロタイピング的になんとなくで書いてみました。(dame.rb とします)

def main
  producer = Ractor.new do
    get_data.each do |d|
      Ractor.yield Ractor.make_shareable(d, copy: true)
    end
    Ractor.yield :term
  end

  consumer = Ractor.new producer do |producer|
    loop do
      d = producer.take
      break if d == :term
      puts "consumer: #{d['value']}"
    end
  end

  # producer 完了待ち
  producer.take

  # consumer 完了待ち
  consumer.take
end

def get_data
  [
    { 'id' => 1, 'value' => 'aaa'},
    { 'id' => 2, 'value' => 'bbb'},
    { 'id' => 3, 'value' => 'ccc'},
    { 'id' => 4, 'value' => 'ddd'},
  ]
end

main
  • producer からデータ流して、
  • consumer で受け取って何か処理
  • 後は終了待ち

みたいなやつです。

これ実行すると

$ ruby dame.rb
<internal:ractor>:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
consumer: bbb
consumer: ccc
consumer: ddd

みたいな感じで、一個データが consumer に渡っていなんですよ。 これにしばらく悩みました。。。

原因は

  # producer 完了待ち
  producer.take

これ。

当然ですよね。。。
producer.take するってことは producerRactor.yield したやつを受け取るので、ここに1つ producer から流されたデータが渡ってきていた、というだけ。

終了待ちは take だからという適当な理解で書いてしまった、というオチです。

動くようにしたもの

動くようにするには、この実装だと最後の待ちの順番を入れ替えるだけ。

  # consumer 完了待ち
  consumer.take

  # producer 完了待ち
  producer.take

これだと期待通り。

$ ruby ok.rb
<internal:ractor>:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
consumer: aaa
consumer: bbb
consumer: ccc
consumer: ddd

さて、動いていはいるけど、これで本当に良いのか全く自信なし。

:term みたいなメッセージ投げるのが正しいのだろうか、というのも。

make_shareable のところはこの記事的なところだと copy: true 必要はないけど、今後予定のことを考えてそうしてはいます。

まだまだ理解に乏しいけど、実装しながら学んでいくことにします!!
(初めから〇〇言語で書けば、というのはあるが...それは置いとく)

iOSDC Japan 2021のコアスタッフやってきました

もうブログはほぼ放棄ぎみなのですが、iOSDC Japan 2021が終わって書く気分になったので書きます。

感謝

今年も大成功にiOSDCが終わったと思います。
スポンサー、スピーカー、参加者、スタッフ(4Sというワードが爆誕してましたね)の皆さまのおかけです。 とても楽しい 2.5 Days でした!!

参加形態

今年もコアスタッフとして参加させていただきました。
スタッフ自体は1回目からさせていただいており、もはや私にとっては一年で一番大事なタスクになっていますね。
普段の通常の仕事よりは完全に優先事項。内緒です。。。

事前の担当

今年は、スピーカー、サポーター向けのノベルティの1つタンブラーを担当しました。
スタッフの Slack でノベルティ案の会話があったときに、ポロッと、「タンブラーとか?」みたいな発言をしたのがきっかけでした。

デザインはデザイナーの方にしていただきましたが、ベースとするタンブラーのサンプル集めから、最終発注までは2か月くらいかかった力作です!! iOSDC Japan 2021のロゴが、タンブラーと蓋にレーザー彫刻で刻印されています。

自分でも大満足な仕上がりになりました。
是非、スピーカー、サポーターの皆さまに長くご愛用いただければ嬉しいです。
おそらく毎年つくるような定番系のノベルティではないので、今後数年は作られないような気がしますし。

f:id:kikumoto:20210908174335j:plain f:id:kikumoto:20210920185337j:plain

当時の担当

当日はリモートからの参加で、Ask the Speaker の方を担当させていただきました。
拙い司会となりましたが、スピーカー、参加者の皆様のおかげもあり、なんとか無事に終えることができたと思います。 ありがとうございました!

来年にむけて

この2年はオンラインでの開催を通じて、以前から参加いただいている方にはオンラインの良さを認識しつつ、オフライン開催の懐かしさも感じ始めたころではないでしょうか?
iOSDCの良いとこの1つは、常にチャレンジをしているカンファレンスだと思っています。
来年はオフライン開催も見えてくる気配もあるので、きっとオン・オフをうまくミックスしたカンファレンスへのチャレンジとなるのではと思っています(実行委員長次第ではある)。
そして、自分もまたそこになにか貢献したい気持ちはあります。
しかし、オン・オフのミックスとなるとスタッフとしてやることは今まで以上に多いと思うので、是非来年は4Sの1つスタッフをしてみようかな、と少しでも思ったあなた!、是非時期がきましたらスタッフへの参加をお願いします!!!

それでは、また来年 iOSDC という同窓会でお会いしましょう!!!

PHPerKaigi 2020 にスピーカー&当日スタッフで参加してきた

PHPerKaigi 2020にスピーカー兼当日スタッフで参加してきました、エントリーです。

スピーカー

二日目に、PHPシステムをコンテナで動かすための取り組みのすべて by きくもと | トーク | PHPerKaigi 2020 #phperkaigi - fortee.jp として30分枠で話してきました。

資料は↓です。

speakerdeck.com

地味なネタだけに通ると思っていませんでしたが、久しぶりに外で話す機会が持てたことに感謝です。

また聞きにきていただいたかたにも感謝です。ありがとうございました。

何か新しい技術とかはないのですけど、実際にやったことであるので、何かの参考になれば幸いです。

やはり話すのは楽しいですね。また良いネタができたときには話したいです。

当日スタッフ

また、今年もですが、当日スタッフでの参加でした。で、例年通り受付してました。

今年は幸い人が集中することもなく、また慣れている参加者もそれなりにいるのか、受付はほぼスムーズに流れていたように思います。

が、今年の最大のポイントはやはり、これですよ、これ

f:id:kikumoto:20200217212103j:plain:w200

トレカ!

全員にあったわけではないのですがそれでも物量がすごくて、スマートにお渡しできず、ちょっとバタバタしながらのお渡しになってしまったかなぁと思っており、また次回があるなら良い方法を考えてリベンジしたいところです。

お渡しする時の皆さんのリアクションがよくて、受付的にも楽しかったです。

皆さん、ご来場ありがとうございました!

最後に

毎年こうやっていろんな人と交流できるPHPerKaigiに感謝です。また、来年もなんらかの形でかかわっていきたいなぁという気持ちになった 2020 でした。

ありがとうござました!!!

Dygma Raiseがやってきた

7月に申し込んだキーボード Dygma Raise

www.dygma.com

がついに届きました!

申し込んだ時はまだ絶賛開発中だったので、夏の終わりにくれば良いかなと思ってましたが、結局ほぼ半年待った形に。 まぁ、待つことは覚悟してたので、そこはあんまり気にはしてない。

そもそもなぜ Dygma Raise を買ったかですが

  • 分割キーボードにしたかった
  • Mistel Barocco を持っている人のをみて、1体にもできるのは何となく良さそうだった
  • 親指部分に機能を集中させたかった

という感じで調べてたら Dygma Raise を見つけて、これだ!と思いポチりました。

繋いだ状態はこんな感じ(まだまだこれから整えていく) f:id:kikumoto:20191228194749j:plain

ファーストインプレッション

キースイッチは Kailh Speed Silver というやつにしました。

まぁ、特に嫌いという感じではなかった打感なので、これで良かったかな。
Macのキーボードに慣れすぎていたから、キーキャップが発する音(と言えばいいのかな)がまぁまぁするなぁという感じ。

親指部分に、Returnとかバックスペースを使えるのはやはり快適。(あくまで個人的感想ですw)

パームパッドもついてくるのだけど、これは私は使わないかなぁ。

本体については基本大満足ですね。本当は、パームレストが取り外せるともっと最高なんだけど。(なお、一体型というのは理解して買っています)

一方問題なのはキーの設定をするソフトウェア。

Bazecor というアプリをダウンロードして使うのだけど、まだまだ不安定な感じですね。
もっとも、不安定なのはソフトなのかキーボード側のファームなのかは定かではないですが。

レイヤーを任意に一発で切り替えたいのだけど、現状は1つのキーに特定のレイヤーの変更を割当る感じになってしまう。
実質2、3レイヤーしか使わないから、レイヤーのために2つのキーを割り当てる程度でまだ棲むけど。
このあたりは、ソフトウエアのアップデートに期待したい。

それにまだまだよく落ちる。。。
ソフトウェアが落ちても別にキーボードそのものはちゃんと動くから、まぁ早く安定版を作ってくれ、という感じ。

キーの設定をテキストでエキスポート、インポートできるのは良いですね。git 管理することにしました。

まだまだ年末・年始休暇は始まったばかりなので、まずはキー設定を自分好みにしていくぞ!!

ファイルが作成されたら自動追尾するfluent-agent-chimeraを作りました

特定のディレクトリ配下に、任意のタイミングでディレクトリが新規作成されて、その配下にログが出力されているような状況下で、そのログをin_tailで取り込みたいという必要性から、fluent-agent-chimera

github.com

を作りました。

目次

どういうものか

例えば

/path/to/batchdir
  - dir1
    - hoge_20180201.log
    - hoge_20180202.log
    - xxxx_20180201
    - yyyy.log
  - dir2
    - hoge_20180201.log

というディレクトリ、ファイルがあるとします。

これに対して、設定ファイルとして以下のようなものを用意します。

[Server]
Network = "tcp"
Address = "127.0.0.1:24224"

[[Logs]]
Tag = "batch"
Basedir = "/path/to/batchdir"
Recursive = true

TargetFileRegexp = "^.+/batchdir/.*(\\d{8})(?:.*\\.log)?$"
FileTimeFormat = "20060102"

この設定でfluent-agent-chimeraを起動すると、まず

/path/to/batchdir
  - dir1
    - hoge_20180202.log
    - xxxx_20180201
  - dir2
    - hoge_20180201.log

のファイルが監視対象となり、ログが出力されると指定のfluentサーバにログがforwardされます。

ここで

/path/to/batchdir/dir1/hoge_20180203.log

というファイルが作成されると、dir1/hoge_20180202.log は監視対象からはずれ、dir1/hoge_20180203.log が監視対象となります。

さらに

/path/to/batchdir/dir3/hoge_20180202.log

が作成されれば、このファイルも新たに監視対象となります。

このように、監視対象のディレクトリと、正規表現にマッチするファイル(ただし、ファイル名に日付を含むファイル)を新規に作成されるものも含めて自動追尾して、fluentサーバにログをforwardしてくれるのが、fluent-agent-chimeraです。

まぁ、アプリとかバッチのログ出力をちゃんと最初から設計して、普通にfluentやfluent-agent-hydraなどで追えるようにしおけばこんなものは不要なのですが、いろいろ事情もあるわけで(つらい)、こんな闇のようなツールを生み出しました。

実装について

GitHubリポジトリにも書いていますが、fluent-agent-chimeraは、@fujiwara さんの

github.com

です。基本的な構成は同じで、inotifyでの処理部分を作り込みつつ、in_forwardを削ったり、ファイルの中身を処理するところ削ったりしています。

fluent-agent-chimeraは、基本的に新規作成ファイルの検知がメインで、あとはなるべくローカルのfluentに処理を移譲する、という想定でいます。

あとは、fluentのclientとして、@lestrrat さんの

github.com

に変更しまいた。fluent-agent-hydraを大きく書き換えているので、fluentのclient部分をfluent-agent-hydraに組み込まれているfluent clientに依存させると、それに追随するのも手間かなと思ったのと、単にいじってみたかったので、変えました。そのおかげで、unix domain socketでfluentサーバと通信も可能です。

そのほか

今回、初めてGoのソースを公開するってのをしたのですが、一連の公開の手順については、@songmu さんの

github.com

を多いに参考にさせてもらいました。

goxz 含めて、gobump, ghr, ghch などが見事に連携されていて、このやり方が標準になれば良いのでは、と個人的には思ったほどです。多分、今後もこれにならう感じになりそうです。

最後に

個人的には、やっぱりGoは肌にあっているなぁという感じです。もっとGoを書いていきたい。

で、もし感想とかありましたら、@takakiku までいただけると嬉しいかもです。

Nginx+SiteGuard LiteのRPM作成Docker - ngx_mruby を添えて -

さくらインターネットの環境でSiteGuard Liteが無償で使えますが、特に今年の6月にはNginx版も提供されるようになっていますね。

それまではApacheのみの対応であったので、Nginxを使っている身としてはちょっと縁がないかなぁと思っていたわけですが、Nginxが出たので利用し始めています。

ただ、仕方ないのですが、Nginxをソースからビルドしてインストールする、という手順となるのでそこが複数台のサーバにインストールするときに面倒だったり、一応バージョン管理もしたいので、イマイチでした。

なので、CentOS7限定(自分が必要だったので)ですが、SiteGuard Lite付きNginx RPMをビルドするためのDockerfileを作ったので公開します。RPM作成となるベースのNginxは、CentOS7向けに公開されているSRPMを使っています。

ついでに、ngx_mrubyも必要であったので ngx_mruby 付きです。公開リポジトリには、ngx_mruby なしも公開しています。

上記リポジトリのREADMEの通りですが、そもそもさくらインターネットの環境でSiteGuard Liteを利用できる条件を満たしていることが前提です。

その上でリポジトリをcloneして

make

すれば build ディレクトリ配下にRPMが作成されます。

このRPMを使えば、SiteGuard Lite公式のドキュメントにてNginxをビルドする箇所を、RPMインストールに読み替えればOKです。

誰かの参考になれば幸いです。