Journal InTime


2014-06-30 (Mon) [長年日記]

_ InfluxDBの古いデータの削除

InfluxDBを試しているが、InfluxDBには今のところ古いデータを自動で削除する機能がない(対応予定はあるようだ)。

古いshardを丸ごと削除するのが効率がよいらしい(InfluxDBでは一定期間ごとにshardが作成される)ので、以下のようなcron用スクリプトを書いてみた。

require "influxdb"

USER = "root"
PASS = "root"
TTL = "1d"

def parse_ttl(ttl)
  case ttl
  when /\A(\d+)s\z/
    return $1.to_i
  when /\A(\d+)m\z/
    return 60 * $1.to_i
  when /\A(\d+)h\z/
    return 60 * 60 * $1.to_i
  when /\A(\d+)d\z/
    return 24 * 60 * 60 * $1.to_i
  else
    raise "invalid TTL - #{ttl}"
  end
end

secs_to_live = parse_ttl(TTL)
influxdb = InfluxDB::Client.new

for shard in influxdb.get_shard_list["shortTerm"]
  end_time = Time.at(shard["endTime"])
  if Time.now - end_time > secs_to_live
    influxdb.delete_shard(shard["id"], shard["serverIds"], USER, PASS)
  end
end

InfluxDBでは小文字で始まる名前のseriesは短期間(short term)、大文字で始まる名前のseriesは長期間(long term)と決まっているが、上記のスクリプトでは短期間のshardだけ削除するようにしている。

ただ、デフォルトでは各shardの期間が7日間になっているので、それよりも短い期間でshardを削除する場合は/opt/influxdb/shared/config.tomlの設定を以下のように変更しておく必要がある(この例では6時間に設定している)。

[sharding.short-term]
duration = "6h"

/opt/influxdb/current/config.tomlの方を変更しても反映されないので注意。

Tags: Ruby InfluxDB

2014-06-29 (Sun) [長年日記]

_ RedisのPub/Subの遅いsubscriber問題

RedisのPub/Subを試していて、subscriber側の受信が遅いとredis-server上のキューが伸び続けて、メモリ使用量が増大し続けてしまう問題にはまった。

以下のようにひたすらpublisher側でひたすらデータを送るようにしておいて、

require "redis"
require "json"

redis = Redis.new(timeout: 0)

redis.subscribe("foo") do |on|
  on.message do |channel, msg|
    i = msg.slice(/(\d+):/, 1)
    p i
    sleep(1.0)
  end
end

subscriber側で以下のようにsleepすると、どんどんredis-serverが太っていく。

require "redis"
require "json"

redis = Redis.new(timeout: 0)

redis.subscribe("foo") do |on|
  on.message do |channel, msg|
    i = msg.slice(/(\d+):/, 1)
    p i
    sleep(1.0)
  end
end

調べてみると、Redis 2.6以降ではバッファサイズの上限を指定でき、上限に達したクライアントは切断されるようだ。

たまたまUbuntu 14.04に上げたところだったので確認すると、redis-server-2:2.8.4-2では最初からredis.confで以下のように設定されていた。

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

この環境で上記のスクリプトを試すと、たしかに以下のように切断される。

$ ruby sub.rb
"0"
"1"
"2"
"3"
"4"
"5"
"6"
"7"
"8"
"1681"
"1682"
"1683"
"1684"
"1685"
"1686"
"1687"
"1688"
"1689"
/home/shugo/local/lib/ruby/gems/2.2.0/gems/redis-3.1.0/lib/redis/client.rb:233:in `rescue in io': Connection lost (ECONNRESET) (Redis::ConnectionError)

番号が飛んでいるところを見ると、上限に達してもすぐに切断されるわけではないようだ。

結論: ソフトウェアに何か問題があったらとりあえず最新版を確認しましょう。


2014-06-28 (Sat) [長年日記]

_ Ubuntu 14.04へのアップグレード

長らくUbuntu 12.04を使っていたが、重い腰を上げて14.04にアップグレードした(ついでに32bit版から64bit版にしたかったので、正確にはVMのイメージを作り直した)。

インストールは問題なかったが、使い始めるといくつかの問題が…。

  • compizのCPU使用率が高い。→ gnome-session-flashbackを入れてGnome Classicを使用することに。
  • Emacsのバージョンが上がって、T-Codeが動かなくなった。→ 後述の設定で回避。
  • Emacsのバージョンが上がって、insert-signatureがなくなった。 → とりあえずmime-edit-insert-signatureを使用。
  • kb2mb2の起動時にX Error of failed request: BadAccessと言われてこける。 → とりあえず放置。

T-Codeの件については、last-command-charがなくなったせいでgithubの最新のコードでも問題があったので、.emacsに以下のようなコードを入れて凌ぐことにした。

(defmacro advice-last-command-char (commands)
  `(progn
     ,@(mapcar
        (lambda (command)
          `(defadvice ,command (around ,(intern (concat (symbol-name command) "-last-command-char")) activate)
             (let ((last-command-char last-command-event))
               ad-do-it)))
        commands)))

(advice-last-command-char (eelll-key
                           isearch-printing-char
                           isearch-printing-char
                           isearch-printing-char
                           tcode-2byte-alnum-input-method
                           tcode-2byte-alnum-self-insert-command
                           tcode-mazegaki-complete
                           character-to-event
                           isearch-last-command-char
                           tcode-electric-space
                           tcode-insert-ya-outset
                           tcode-katakana-preceding-chars))

defadviceとdefmacro便利。

7/1追記:

insert-signatureについては、.emacsに以下のような設定を追加すれば定義された。

(setq mime-setup-use-signature t)
Tags: Ubuntu

2014-03-15 (Sat) [長年日記]

_ 松江Ruby会議05

松江Ruby会議05に参加してきた。参加されたみなさん、おつかれさまでした。

ライブコーディングがテーマだったので色んな人のコードを見られて楽しかった。まつもとさんも珍しく書き下ろしのコードを紹介してたし。

とくに圧巻だったのはDXRuby作者のmirichiさんのライブコーディングで、15分程度(?)でその場で一からブロック崩しを作られていた。 スプライトの衝突判定にSprite#===を使われていたのが印象的だったけど、たぶんcaseやgrepで使えるようにという設計なんだろう。

自分がライブコーディングで書いたコードは以下のURLに置いておいた。

このコードだと単純なpointcutしか表現できないけど、例えば、

class Foo
  def bar
    Bar.new.baz
  end
end

のようなコードがあった時に、AspectJみたいにwithin(Foo) & call(class: Bar, method: :baz)みたいなpointcutの指定(Fooの中のBar#bazの呼び出しだけにアスペクトを織り込む)ができると使いやすそう。

しかし、Module#prependだとメソッドの呼び出し側でなく呼び出されるメソッドの側を置き換えるので、Fooから呼び出されたかどうかの判定に呼び出し元のクラスの情報が必要になるが、Rubyレベルではその情報を取るAPIがない。 気が向いたらrb_debug_inspector_open()とかを濫用して作るかもしれないけど、呼び出し元のクラスくらいは普通にRubyレベルで取れてもいいように思う。

Tags: Ruby
本日のツッコミ(全3件) [ツッコミを入れる]

_ 宮国 [私は宮崎で木工作家をしている宮国と申します。 現在は仕事の傍ら、以下のウェブサイトを運営しております。 四畳..]

_ shugo [コメントありがとうございます。 レポートを興味深く拝見しましたが、問題意識に共感いただけたことを嬉しく思います..]

_ 宮国 [見ていただいてありがとうございました。コメントもありがとうございます。 卒業論文であのようなレベルのものが書けるの..]


2014-03-04 (Tue) [長年日記]

_ 松江Ruby会議05予告

松江Ruby会議05でライブコーディングをすることになったが、「Module#prependとRefinementsで遊ぶ」というタイトルだけ出して、実は内容を考えてなかった。

以下のようなAspectJ風のコードを書くためのaspect.rbを実装しようと思う。

require "aspect"

class Job
  def run
    raise "subclass must override Command#execute"
  end
end

class PrintJob < Job
  def run
    puts "running PrintJob..."
  end
end

class BackupJob < Job
  def run
    puts "running BackupJob..."
  end
end

module Logging
  extend Aspect

  JOBS = execution(class: Job, method: :run)

  around JOBS do |join_point|
    STDERR.puts("enter #{join_point.signature}")
    begin
      join_point.proceed
    ensure
      STDERR.puts("exit #{join_point.signature}")
    end
  end
end

jobs = [PrintJob.new, BackupJob.new]
jobs.each(&:run)

実質20分くらいの持ち時間で上手く書けるかどうか。お楽しみに。

Tags: Ruby