TokyoTyrant を ActiveRecord 風のAPIで利用するライブラリ

インストール

   1  % gem install miyazakiresistance

セットアップ

適当なポートで TT を起動。(テーブルデータベースを利用するので *.tct)

   1  % ttserver -port 11114 services.tct &

使用例

   1  require 'rubygems'
   2  require 'miyazakiresistance'
   3  
   4  class Service < MiyazakiResistance::Base
   5    set_server "localhost", 11114, :write
   6    set_timeout 1
   7  
   8    set_column :name , :string
   9    set_column :port , :integer
  10    set_column :proto, :string
  11  end
  12  
  13  buf = File.read("/etc/services")
  14  buf.scan(%r{^(\w+)\s+(\d+)/(udp|tcp)}) {
  15    Service.create(:name=>$1, :port=>$2, :proto=>$3)
  16  }
  17  
  18  Service.count
  19  => 373
  20  
  21  Service.first
  22  => #<Service:... @id=1, @port=1, @proto="tcp", @name="tcpmux">
  23  
  24  Service.find_all_by_port(80)
  25  => [#<Service:... @id=40, @port=80, @proto="tcp", @name="www">,
  26      #<Service:... @id=41, @port=80, @proto="udp", @name="www">]

  • データ型は :string, :integer, :date, :datetime
  • master/slave, dual master をサポート
  • ARのdynamic finderもサポート
  • (created|updated)_(on|at) は magic column
  • TTへの保存キーは id の値 (数字の連番が自動付与)

欠点

  • スキーマを途中で変更するとエラー (DB内は同じスキーマのデータが必要)
  • 1モデル毎にTTサーバが1つ必要になる?
  • :date, :datetime を空にできない (Time.at(0) になる)
  • "set_" prefix が冗長

という実装を見る限り、想定されたユースケースは、

  • 任意のドキュメントを格納

ではなく、

  • 固定されたスキーマ定義によってTTをRDB的に利用

のようだ。 ARもASも必要としないので、「TTを手軽に便利にCRUDしたい」 という用途にはピッタリだろう。

参考

posted by Png maiha on Thu 25 Feb 2010 at 08:43

しばらく前から安定性が悪くなってきたのですが、ここに来て頻繁にPassengerのプロセスが暴走するようになってきました。

  • ruby 1.8.7 (2009-04-08 patchlevel 160) [i686-linux]
  • Passengerのバージョンは2.2.8
  • Rails 2.3.5

プログラムやシステム構成はここしばらくほとんど変えておらず、トラフィックも大きな変動はないので、うわさのEC2のサービス劣化の影響でしょうか。

posted by Png genki on Thu 25 Feb 2010 at 02:11

TokyoCabinetとは

  • 高速なKVS
  • mixiの平林さんが作成
  • mixiの高負荷で運用されている性能と実績
  • 永続化機能あり (memcachedに対する利点)
  • 効率的、並列可、単純なAPI
  • 単純なKVS(hash)だけでなく、B+木、テーブル(hashを値に取る)も利用可能
  • 仕様書: Tokyo Cabinet第1版基本仕様書

また親類が多く、用途に応じて使い分けることができる

  • Tokyo Cabinet : KVSライブラリ
  • Tokyo Tyrant : TCのネットワーク対応版
  • Kyoto Cabinet : KVSライブラリ(TCとは別方向の実装)

開発順序も同じで、TCというKVSを作って、TTはそれをネットワークに対応させたもの。

TCとKCの違い (余談)

じゃあ、KCって何?何でまたKVSの作成に戻るの?後継なの?TCより強いの? て気がするが、一言にすると、TCはシングルスレッドでの最速を追求した実装。 (汎用的だが若干速度面で甘さのある)既存のライブラリには一切頼らず、 TCのために最適化された部品を自作し、速度という神の一手を追求した「攻撃的な実装」。 言わば、一瞬の隙を見逃さない久保棋王の将棋。 でもそれは一人でやるには開発以上に保守が大変になってくる。 それに対して、KCは個々の部品レベルでの最善の一手の追求は少し緩めても、 マルチプロセスで性能が出るように再設計し、 既存のライブラリを使ってでも保守性を高めて、 結果的にトータルでの最速を目指した「負けない実装」。 言わば、渡辺竜王の将棋。 したがって、(まだ発展途上なせいもあって)シングルスレッドではTCの方が速いが、 将来を期待させてくれるツールになっている。 ということで、KCは暖かく見守りつつ、今はTC(TT)を使うことになる。 (以上、全て推測)

インストール

   1  % gem install rufus-tokyo

使用例 (TC)

   1  require 'rubygems'
   2  require 'rufus/tokyo'
   3  
   4  t = Rufus::Tokyo::Table.new("foo.tct")
   5  
   6  t['gem1'] = {:name=>'sinatra', :minor=>9}
   7  t['gem2'] = {:name=>'monk', :minor=>0}
   8  
   9  gems = t.query { |q|
  10    q.add_condition 'minor', :numge, '1'
  11  }
  12  # => [{"name"=>"sinatra", :pk=>"gem1", "minor"=>"9"}]
  13  
  14  t.close

直接ハッシュを扱うため、ORMというよりHVM(Hash-Value Mapping)。 というかそもそも tokyocabinet ライブラリを直接使うのと殆ど違いが見えない。 恐らく利点は

  • 全体的に記述が Ruby ぽい (エラー処理とか)
  • transaction サポート (ブロックで記述できる)

あたりだろうか?

   1  t.transaction do
   2    begin
   3      t['gem1']  = {:name=>'sinatra', :minor=>9, :author=>'user1'}
   4      t['user1'] = {:name=>'bmizerany'}
   5    rescue
   6      t.abort
   7    end
   8  end

うーん、なんか微妙かも。 やっぱりObjectに対してCRUDしたいよね。 とか思ってたら、作者(jmettraux)から 「oklahoma_mixerの方がいいよ」 とアドバイスを頂いた。ダメじゃん。 というか、tokyocabinet が撒いた種とは言え、 関連ライブラリの名前の弾け方が凄い>oklahoma_mixer, miyazakiresistance。

posted by Png maiha on Wed 24 Feb 2010 at 17:31

redis とは

インストール

   1  % gem install ohm

セットアップ

   1  require 'ohm'
   2  Ohm.connect(:port=>6379)

モデル

   1  class Video < Ohm::Model
   2    attribute :url
   3    attribute :created_at
   4    set :tags
   5  
   6    index :url    # 検索対象には全てindexを作成する
   7    index :tags   # 検索対象には全てindexを作成する
   8  end

  • pkeyのidが勝手に定義される
  • redisで利用可能なvalueの型は、文字列と集合
  • Ohmでは文字列型を attribute で定義する
  • Ohmでは集合型を set で定義する
  • Ohmではさらに独自の list(順序付き集合), counter(増減のみ操作可能な数値) 型が利用可能

使用例

   1  video = Video.new(:url=>"http://www.you...", :tags=>"愛理")
   2  video.save
   3  video.tags << "cute"
   4  
   5  Video.find(:tags=>"cute")
   6  => #<Index: ["1", "2"]>
   7  
   8  Video.find(:tags=>"cute").last.tags
   9  => #<Set: ["cute", "愛理"]>

低レベルAPI

Object-Hash ではなく、純粋に redis の KVS 用 API として利用する場合。

   1  Ohm.redis.get "Foo"         # => nil
   2  Ohm.redis.set "Foo", "xxx"  # => "OK"
   3  Ohm.redis.get "Foo"         # => "xxx"

長所

  • redisのKVS性能が高いので単純レコードへの参照・記録は超高速
  • 集合型を持つのでタグの扱いなどが凄く便利
  • スキーマレスでとっつきやすい
  • 各操作がアトミックなのでロックがない

短所(redis)

  • 文字列型のみ(数値や日付を入れても範囲検索できないので実用的ではない)
  • 検索機能が貧弱 (文字列の完全一致のみ)
  • データ保存が async
  • もちろんトランザクション処理もなし

保存時間の間隔を設定できるとは言え、基本 async なのでクラッシュによるデータ損失の危険性が常にある。 その意味では、RDBのようなしっかりとしたデータストレージでなく、 前回の状態を(運が良ければ)そのまま再開してくれる memcached という認識(利用)がよさそう。

短所(Ohm)

  • 予約語チェックがなく、id 属性を指定すると謎の挙動でハマる
  • new record 時に集合型を参照するとエラー

など使い辛い

posted by Png maiha on Mon 22 Feb 2010 at 07:40

インストール

   1  % gem install mongo_mapper

mongomapper (古いバージョンのgem)も存在するので注意

セットアップ

   1  require 'mongo_mapper'
   2  MongoMapper.database = "app1"  # DB名

  • 各モデル内で明示されない場合に利用されるデフォルトのDB名

モデル

   1  class Player
   2    include MongoMapper::Document
   3    key :name,   String, :required => true
   4    key :policy, Integer
   5    key :renkei, String
   6    key :note,   String
   7    timestamps!
   8  
   9    validates_uniqueness_of :name
  10  end

  • pkeyのidが勝手に定義される
  • 文字列は長さに制限がないので全てString

検索

   1  Player.count
   2  => 822
   3  
   4  Player.count(:renkei=>'萩原型')
   5  => 135
   6  
   7  Player.first
   8  => #<Player label: "萩原 忠志" ... >
   9  
  10  Player.all(:label=>/^河本/).map{|p| [p.pos, p.label]}
  11  => [["FW", "河本 鬼茂"], ["GK", "河本 龍将"]]

高度な設定

  • ARで言うテーブルをMongoDBではコレクションと呼ぶ
  • table を collection に変えるだけでAR風のAPIが使える

   1  class Pref
   2    include MongoMapper::Document
   3  
   4    set_database_name "usei"  # app1でなく usei DBを利用
   5    set_collection_name "zip" # prefs でなく zip コレクションを利用
   6  ...

長所

  • MongoDB自体の性能がよい
  • インタフェースが AR と DM のよい点を取り込み (CRUDはAR, SearchはDM)
  • バリデーションや関連もサポート
  • 正規表現による検索

短所

  • サーバ(MongoDB)への再接続などのコネクション機能が弱い(mongo_mapper でなく mongo の問題かも)
posted by Png maiha on Sun 21 Feb 2010 at 16:51

Formulaを使って数式の画像を生成するときに、数式のサイズを指定する場合、以下のようにするとうまくいきます。

   1  \mbox{\Huge $\displaystyle
   2  I_j=\int_\Omega f_j(\bar{x})\,d\mu(\bar{x})
   3  $}

出力結果は以下の通り。

posted by Png genki on Fri 19 Feb 2010 at 20:39

下記の続きです。

iPhone, Mac, ISO-2022-JP, 〜, 全角チルダとか波ダッシュについてのメモ
http://blog.s21g.com/articles/1693

あと、この記事も関係するかも:[iPhone] メール送信アプリでのツボ

結局何が書きたかったかというと、

「全角チルダ」がdataUsingEncoding:NSISO2022JPStringEncodingで「?」に置き換えられるので、何らかの回避策を探そう!…探しました!

という報告です。

〜の入力方法二つ

以前に書いた記事で、Macでは全角チルダと波ダッシュは同じに見えるらしいことがわかりましたが、iPhoneでも同様のようです。 で、日本語キーボードで「〜」を入力する方法は二通りあります:

  1. あいうキーボードで、わの所を右にフリック、次候補中にある〜を選択
    → 全角チルダ

  2. 数字キーボードで、0の所を左にフリック
    → 波ダッシュ

dataUsingEncoding:NSISO2022JPStringEncodingで波ダッシュだと?にされない、〜のまま

全角チルダ(〜) → ?
波ダッシュ(〜) → 〜

という風に置換されることがわかりました。

大方のユーザにとっては全角チルダを波ダッシュに変換しても問題がなさそうなので、アプリ内では下記のように変換することにしました。

   1  	unichar wave_dash_char = 0x301C;
   2  	unichar fullwidth_tilde_char = 0xFF5E;	
   3  	NSString *wave_dash = [NSString stringWithFormat:@"%C", wave_dash_char];
   4  	NSString *fullwidth_tilde = [NSString stringWithFormat:@"%C", fullwidth_tilde_char];
   5  
   6  	NSString *replacedStr = [str stringByReplacingOccurrencesOfString:fullwidth_tilde withString:wave_dash];

またポンドとセントでも同じような状況が発生するので、同様の処理を行うようにしました。

   1  	unichar pond_char = 0x00A3;
   2  	unichar cent_char = 0x00A2; 
   3  	unichar pond_char2 = 0xFFE1; //?になるポンド
   4  	unichar cent_char2 = 0xFFE0; //?になるセント

posted by Png satoko on Wed 10 Feb 2010 at 07:18

答え

"!=", "!==" を使う

詳細

文字列のHTMLエスケープはRailsやRack::Utilによって hヘルパメソッドとして提供されており、 ユーザはエスケープ処理を「hの有無」によって調整します。

例: hamlでのHTMLエスケープ
コード結果
= link_to(...)&lt;a href=...
=h link_to(...)&amp;lt;a href=...

自動エスケープモード

上記のようなユーザ駆動のエスケープ処理では100%の安全性を保証できないため、 hamlでは「デフォルトでエスケープする」というオプションが存在します。

   1  Haml::Template.options[:escape_html] = true

これにより常時エスケープされるようになるのですが、 今度はh の逆とも言うべき「エスケープしない」方法が必要になります。 これがずっとわからなくて困っていたのですが、 「ドキュメントを見る」という手段を思い出して解決しました。 "!=" を使えばよいようです。

例: escape_html: true
コード結果
!= link_to(...)&lt;a href=...
= link_to(...)&amp;lt;a href=...
=h link_to(...)&amp;amp;lt;a href=...

posted by Png maiha on Mon 8 Feb 2010 at 08:34 with 2 comments

備忘録。ezPhotoMail、地味にバージョンアップしているのですが、Verion Numberではまってしまいました。

@takayamaさんや@sumihiroさんに有益なアドバイス頂きました。感謝!

バージョンは1桁ずつ「.」で区切るのが吉:1.5.5

1.5.5

じゃないかという@sumihiroさんのアドバイス。1.55とかにすると、1.6に上げようとすると、「前のバージョンより高いバージョンをNSBundleに記述してください」という旨のエラーメッセージが出てしまうのです。

または、下記のように60と0を足すのも多分大丈夫じゃないかという@takayamaさんのアドバイス。確かに運用で「下位桁は必ず二桁で」などとするのもいいかもしれません。

1.60

1.5.5aなどの変則バージョンもOK

1.5.5aを公開しておいて、その後1.5.5bというのも可能だそうで、このパターン、@takayamaさんは実際に使われたことがあるそうです。

1.5.5a -> 1.5.5b

というわけで、次のezPhotoMailのバージョンは1.6にしたかったのですが、混乱してしまったので1.56で申請しています…

posted by Png satoko on Mon 8 Feb 2010 at 08:12

CUresultの値一覧表です。

   1  CUDA_SUCCESS                = 0,    ///< No errors
   2  CUDA_ERROR_INVALID_VALUE    = 1,    ///< Invalid value
   3  CUDA_ERROR_OUT_OF_MEMORY    = 2,    ///< Out of memory    
   4  CUDA_ERROR_NOT_INITIALIZED  = 3,    ///< Driver not initialized
   5  CUDA_ERROR_DEINITIALIZED    = 4,    ///< Driver deinitialized
   6  
   7  CUDA_ERROR_NO_DEVICE        = 100,  ///< No CUDA-capable device available
   8  CUDA_ERROR_INVALID_DEVICE   = 101,  ///< Invalid device
   9  CUDA_ERROR_INVALID_IMAGE    = 200,  ///< Invalid kernel image
  10  CUDA_ERROR_INVALID_CONTEXT  = 201,  ///< Invalid context
  11  CUDA_ERROR_CONTEXT_ALREADY_CURRENT = 202,   ///< Context already current
  12  CUDA_ERROR_MAP_FAILED       = 205,  ///< Map failed
  13  CUDA_ERROR_UNMAP_FAILED     = 206,  ///< Unmap failed
  14  CUDA_ERROR_ARRAY_IS_MAPPED  = 207,  ///< Array is mapped
  15  CUDA_ERROR_ALREADY_MAPPED   = 208,  ///< Already mapped
  16  CUDA_ERROR_NO_BINARY_FOR_GPU = 209, ///< No binary for GPU
  17  CUDA_ERROR_ALREADY_ACQUIRED = 210,  ///< Already acquired
  18  CUDA_ERROR_NOT_MAPPED       = 211,  ///< Not mapped
  19  CUDA_ERROR_INVALID_SOURCE   = 300,  ///< Invalid source
  20  CUDA_ERROR_FILE_NOT_FOUND   = 301,  ///< File not found
  21  
  22  CUDA_ERROR_INVALID_HANDLE   = 400,  ///< Invalid handle
  23  
  24  CUDA_ERROR_NOT_FOUND        = 500,  ///< Not found
  25  
  26  CUDA_ERROR_NOT_READY        = 600,  ///< CUDA not ready
  27  
  28  CUDA_ERROR_LAUNCH_FAILED    = 700,  ///< Launch failed
  29  CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES = 701, ///< Launch exceeded resources
  30  CUDA_ERROR_LAUNCH_TIMEOUT   = 702,  ///< Launch exceeded timeout
  31  CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING = 703, ///< Launch with incompatible texturing
  32  
  33  CUDA_ERROR_UNKNOWN          = 999   ///< Unknown error

posted by Png genki on Sun 7 Feb 2010 at 22:10