thread id の件

とりあえずここまでできた (ちょっと飽きてきた) メモ。

THD->thread_id は書いたら書きっぱなしだし、 pthread_self() が一緒に呼ばれるタイミングがうまく見つかれば行けそうだけど、 mysqld_list_processes() でもダメなんだよね。

#!/usr/sbin/dtrace -s

pid$target:*:*mysql_execute_command*:entry
{
    printf("pid=%d, tid=%d, arg0=%d, arg1=%d", pid, tid, arg0, arg1);
}

これで arg1 に入るぽい。

% sudo dtrace -s ./hoge.d -p `pgrep -nx mysqld` | uniq
dtrace: script './hoge.d' matched 1 probes
CPU     ID                    FUNCTION:NAME
  8  56978 __1cVmysql_execute_command6FpnDTHD__i_:entry pid=1006, tid=17620, arg0=11920071504, arg1=2424494
  8  56978 __1cVmysql_execute_command6FpnDTHD__i_:entry pid=1006, tid=17620, arg0=11920071504, arg1=2424494
  9  56978 __1cVmysql_execute_command6FpnDTHD__i_:entry pid=1006, tid=17620, arg0=11920071504, arg1=2424494
 10  56978 __1cVmysql_execute_command6FpnDTHD__i_:entry pid=1006, tid=17620, arg0=11920071504, arg1=2424494

長いこと動きっぱのスレッドならやってる作業のめどを付けて truss から探す、サクサクやることが変わるものならこのリストにいつか出てくる、てな感じでしょうか。

InnoDB の圧縮を使うときの運用

.@nippondanji さんにブログにまとめろと言われた気がするのだけど、あんま大したネタではないです。しかも、この作業は失敗する可能性を見越していたのであまり作業ログを取ってなかった...。

ので、ちょっと疑問に思った点を幾つか書いておこうかと思います。

でかい InnoDB なテーブル

とあるテーブルが大きくなってしまい、運用がめんどくさくて困ってました。

mysql> show create table hoge\G
*************************** 1. row ***************************
       Table: hoge
Create Table: CREATE TABLE `hoge` (
  `date` date NOT NULL,
  `key` int(10) unsigned NOT NULL,
  `value` double NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`date`,`key`),
  KEY `key` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin

pk に突っ込み入りそうな気もするけど、アプリごとテーブル構造を作り直すのは別件で考えるとして、たったの 12 億レコードくらいで 120GB くらい行っちゃうんですね。いや、だいたい計算どおりではあるんだけど。

べつに text があるわけでもないので、これを圧縮したところで改善するとは思えなかったんだけど、頑張って Antelope から Barracuda に変えました。 KEY_BLOCK_SIZE は 8 です。

alter table

すなおに

ALTER TABLE hoge ENGINE=InnoDB ROW_FORMAT=compressed KEY_BLOCK_SIZE=8;

とやってあげました。しかし、終わんない。とにかく終わんない。

結果、ひたすら待って 120 時間くらいかかりました。 ALTER は 1 CPU (スレッド) しか使ってくれないので、他の人が暇そうにしてるのが残念です。

ZFS で hybrid storage の威力を実感する

このデータベースはふだん Linux 上のそんなにディスク容量がないマシンに置いてあるものなんですが、そこの上で ALTER するのは無理があると思ったので、別のサーバに移して作業しました。

環境は Solaris11, 3 面 mirror な udev の 14 stripe に Sun Flash F20 を log x3, cache x1 と振り分けてました。 MySQLSolaris Studio 12.3 で -xtarget=native でこんぱいるした 5.5.10 。メモリ 128GB 中 64 GB を InnoDB に、のこり 64 GB を ZFS に渡してあげたかんじ。

ということで iopattern (Solaris11 では DTrace Toolkit は /usr/dtrace/DTT に移動しましたよ) とかで見てるやると、無難に Random I/O が大変そう。

そこで、始めちゃった ALTER を止めるわけではなく、すこしでも I/O 負荷を下げてあげるべく ZFS の cache に入れていたフラッシュディスクをログデバイスに移動します。これは MySQL の知る世界ではないので、動かしたままやればいける。

% sudo zpool remove tank c7t1d0; sudo zpool remove tank c7t2d0
% sudo zpool add tank log c7t1d0 c7t2d0

zpool status -v を見てると、3つの log device に 50-60MB/s くらい書いて、適当なタイミングで 1GB 弱をディスクに書いているようです。 1 つのときより log に書いてる量が増えてるので、ちゃんと調査してないですが速くはなっているはず。

結果

120GB くらいあったテーブルが 57GB くらいに減ってくれました。カラム名から想像できるとおり、圧縮しても日常の SELECT の負荷はそんな気にしなくて良いです。 bulk で insert したとき遅いのは気にすべきだけど、まあなんとか。

気になるところ (1)

私が大好きな MyISAM では、こうやって頑張って圧縮したテーブルをファイルベースでコピーしてあげれば、圧縮コストは1回で済みます。 ZFS なら snapshot とって send するとかすればいいんです。しかし InnoDB だとそうは行きません。 innodb_file_per_table=1 にしたところで、 frm と ibd をコピっただけじゃ動かないわけです。

なので、元のサーバに移すために mysqldump なんてやってしまったら台無しです。なので、元のサーバにはデータディレクトリまるごと戻せるような状態にまでしないといけません。

そんなとき役に立つはずなのが、有償の InnoDB Hot Backup のはずです。 .@meijik さんによると、 MyISAM のとき同様にファイルベースでコピーしてうまいことやってくれるもののようです。

が、ファイルごとコピーするにしても backup 以外にもいろいろやりたいことはあるので、実際のところどういう運用が可能になるのかわからないんですよね。良いツールだと思うので、どっかにいい事例紹介とかないのかな。

気になるところ (2)

実際のところ、これで「サイズが大きい」問題が解決する理由は ibd ファイルを gzip したのと同じようなもんです。なので、たとえば standby 的な slave で定期的に mysqld とめて datadir を tar.gz して保存しておく場合、 tar.gz のサイズがほとんど小さくならないことに気をつける必要があります。

ALTER 直後は覚えてるからいいんですけど、今後またデータが増えて行ってまた分割なりなんなりを考えなきゃいけなくなったときに、(空きスペース的に) まったく身動き取れない状況に陥るリスクが有ることを見越しておく必要があります。

気になるところ (3)

ちょっと話はそれますが、 ALTER 中に他の CPU が暇そうにしてると、思わず pbind してやりたくなるのが人情ってものです。しかし、 MySQL の thread id と LWP の id の対応がよく分からない。開いてるファイルとか truss してやれば「だいたいこれだな」と目星はつきますが、正しく知る方法っていいのないのでしょうか。

まとめ

I/O サイズを減らす目的ではなく、純粋にテーブルサイズが大きくなった時の逃げ道として InnoDB の圧縮を考える場合、無駄なデータが沢山入ってるなどの「本質的に圧縮が効く」場合で無い限り、けっこう運用負荷が上がるだけなんじゃないかなー、と思ったのでした。

device path が変わった pool を import

Solaris 11 EA (Early Adopter) というのが出てました。ほぼ snv_173 そのもののようです。

先日、自宅のサーバ (Solaris11 Express, snv_151a) の BIOS とかを firmware してたら突然死亡して、せっかくだし EA release にしようと新規インストールしました。新しめの Intel on-board NIC である 82579V (pci8086,201b) にもちゃんと e1000g で動いていました。

ただ、 LSI MegaRaid SAS 9240-i8 が imraid_sas で認識されているもののディスクが見えない。 snv_151a のときに LSI から落としてきた imr_sas もダメでした。 JBOD じゃなくて virtual disk 作んないとダメかな? あとで余裕あるときに調べようと思っています。

で、ちょっとハマったメモ。

import

まともに export もせずに死んで OS だけ入れ替えたので、import しようとすると以下のとおり。

ichii386@oscar:~$ sudo zpool import 
  pool: tank
    id: 4765092275615549508
 state: UNAVAIL
status: The pool was last accessed by another system.
action: The pool cannot be imported due to damaged devices or data.
   see: http://www.sun.com/msg/ZFS-8000-EY
config:

        tank         UNAVAIL  missing device
          raidz1-0   ONLINE
            c1t0d0   ONLINE
            c1t3d0   ONLINE
            c1t1d0   ONLINE
            c1t2d0   ONLINE
        cache
          c1t1d0
ichii386@oscar:~$ sudo zpool import tank
cannot import 'tank': pool may be in use from other system, it was last accessed by oscar (hostid: 0x894ca0) on Wed Sep 28 12:24:59 2011
use '-f' to import anyway
ichii386@oscar:~$ sudo zpool import -f tank
The devices below are missing, use '-m' to import the pool anyway:
            16845364573619796447 [log]

cannot import 'tank': one or more devices is currently unavailable

device path が変わった

何が起きているかというと、OS 入れ替えに伴いケーブル差し替えをしたので、以下のように device path (の numbering) が変わったのでした。

device snv_151a 時代 snv_173 入れた後
hdd1 c2t0d0 c1t0d0
hdd2 c2t1d0 c1t3d0
hdd3 c2t2d0 c1t1d0
hdd4 c2t3d0 c1t2d0
log c1t0d0 なし
cache c1t2d0 なし

log, cache がなしになってるのは、 imraid_sas がうまくいかない都合です。

import -m (retry)

書いてあるとおりに -m を使って import すると、次のようになりました。

ichii386@oscar:~$ zpool status tank
  pool: tank
 state: DEGRADED
status: One or more devices could not be used because the label is missing or
        invalid.  Sufficient replicas exist for the pool to continue
        functioning in a degraded state.
action: Replace the device using 'zpool replace'.
   see: http://www.sun.com/msg/ZFS-8000-4J
  scan: none requested
config:

        NAME                    STATE     READ WRITE CKSUM
        tank                    DEGRADED     0     0     0
          raidz1-0              ONLINE       0     0     0
            c1t0d0              ONLINE       -     -     -
            c1t3d0              ONLINE       -     -     -
            c1t1d0              ONLINE       -     -     -
            c1t2d0              ONLINE       -     -     -
        logs
          16845364573619796447  FAULTED      0     0     0  was /dev/dsk/c1t0d0s0
        cache
          c1t1d0                FAULTED      -     -     -  corrupted data

errors: No known data errors

logs も cache もないので、これをなんとか消してやりたい。 logs は 1684... という数字を使って

ichii386@oscar:~$ sudo zpool remove tank 16845364573619796447

とすれば消えてくれるんですけど、 cache のほうは c1t1d0 が raidz1-0 に入ってると認識されてうまくいきません。

ichii386@oscar:~$ sudo zpool remove tank c1t1d0
cannot remove c1t1d0: only inactive hot spares, cache, top-level, or log devices can be removed

zdb

logs とおなじように device path じゃなくて数字 (guid) を指定したらうまくいきそうなんですけど、これはどうやって調べればいいんだ? ということで、 zdb の出番です。

しかし、たんに zdb tank しても、 cache の情報は出てくれませんでした。

ichii386@oscar:~$ sudo zdb tank

MOS Configuration:
        version: 31
        name: 'tank'
        state: 0
        txg: 1382933
        pool_guid: 4765092275615549508
        timestamp: 1317327582
        hostid: 4887412
        hostname: 'oscar'
        vdev_children: 1
        vdev_tree:
            type: 'root'
            id: 0
            guid: 4765092275615549508
            children[0]:
                type: 'raidz'
                id: 0
                guid: 3650509394302283806
                nparity: 1
                metaslab_array: 29
                metaslab_shift: 35
                ashift: 9
                asize: 4000766230528
                is_log: 0
                create_txg: 4
                children[0]:
                    type: 'disk'
                    id: 0
                    guid: 9100254656495358846
                    path: '/dev/dsk/c1t0d0s0'
                    devid: 'id1,sd@SATA_____WDC_WD10TPVT-00U_____WD-WX21A31P4321/a'
                    phys_path: '/pci@0,0/pci8086,201b@1f,2/disk@0,0:a'
                    whole_disk: 1
                    create_txg: 4
                children[1]:
                    type: 'disk'
                    id: 1
                    guid: 1408779724253808096
                    path: '/dev/dsk/c1t3d0s0'
                    devid: 'id1,sd@SATA_____WDC_WD10TPVT-00U_____WD-WX51A31U8925/a'
                    phys_path: '/pci@0,0/pci8086,201b@1f,2/disk@3,0:a'
                    whole_disk: 1
                    create_txg: 4
                children[2]:
                    type: 'disk'
                    id: 2
                    guid: 11558871919403241941
                    path: '/dev/dsk/c1t1d0s0'
                    devid: 'id1,sd@SATA_____WDC_WD10TPVT-00U_____WD-WXE1A21S3853/a'
                    phys_path: '/pci@0,0/pci8086,201b@1f,2/disk@1,0:a'
                    whole_disk: 1
                    create_txg: 4
                children[3]:
                    type: 'disk'
                    id: 3
                    guid: 17021081150085051024
                    path: '/dev/dsk/c1t2d0s0'
                    devid: 'id1,sd@SATA_____WDC_WD10TPVT-00U_____WD-WX41A31J8285/a'
                    phys_path: '/pci@0,0/pci8086,201b@1f,2/disk@2,0:a'
                    whole_disk: 1
                    create_txg: 4
(以下略)

man zdb すればわかりますが、これはふつうのユーザは使っちゃダメよらしいので、 http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/zdb/zdb.c を眺めて何とかならんかなー、と考えると、 "l2cache" という文字列がみえる。

そこで以下を試したところ、上手いこと行きました。

ichii386@oscar:$ sudo zdb -M l2cache tank

MOS L2 Caches:
        l2cache[0]:
            type: 'disk'
            guid: 10032807697961866673
            path: '/dev/dsk/c1t1d0s0'
            devid: 'id1,sd@SATA_____INTEL_SSDSC2MH25__LNEL119500KC250DGN/a'
            phys_path: '/pci@0,0/pci8086,201b@1f,2/disk@1,0:a'
            whole_disk: 1
ichii386@oscar:$ sudo zpool remove tank 10032807697961866673
ichii386@oscar:$ zpool status tank
  pool: tank
 state: ONLINE
status: The pool is formatted using an older on-disk format.  The pool can
        still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
        pool will no longer be accessible on older software versions.
  scan: resilvered 54.5K in 0h0m with 0 errors on Fri Sep 30 07:02:18 2011
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz1-0  ONLINE       0     0     0
            c1t0d0  ONLINE       -     -     -
            c1t3d0  ONLINE       -     -     -
            c1t1d0  ONLINE       -     -     -
            c1t2d0  ONLINE       -     -     -

errors: No known data errors

感想

なんてとこで、snv_173 の話はちょこちょこ書いていいのかなー。

volsize を変えたらディスクが読めなくなった件

ただのメモ。

自宅の Solaris 11 (snv_151a) の zvol を target として iMac の globalSAN の iSCSI initiator から使っていたディスクが、volsize を変えたら「パーティションが壊れている」と言われる用になってしまった件が解決した。

zpool の構成を変えてテストしようと思い、zvol を zfs send でいったん別のディスクに退避させたい。実際に書き込んだデータのサイズに対して zvol 自体がすごい容量になっていたので、 volsize いじって小さくしようとしました。ところが、 initiator 側ではこの LU に HFS+ を書いていたので、 zfs と違って容量の変化なんて理解できないのでした。

状況

  • さいしょ
% sudo zfs create -V 1T tank/test
% sudo stmfadm create-lu /dev/zvol/rdsk/tank/test
  • volsize いじって退避 (書き込んだデータは 100G もない)
% sudo zfs set volsize=100G tank/test
% sudo zfs set refreservations=100G tank/test
% sudo zfs snapshot tank/test@aho
% sudo zfs send tank/test@aho > どっか
  • tank を作りなおして書き戻す
% sudo zfs receive -v -d tank < どっか
% sudo stmfadm create-lu /dev/zvol/rdsk/tank/test

ところが mac が認識できないよ、消すか eject するかどうする? みたく聞いてくるんですね。

対処法

で、どうすればいいのかというと、単に volsize を戻すだけです。

% sudo zfs set volsize=1T tank/test

メモとしては以下。

  • 違いよくわかってないけど、lu を戻すときは stmfadm create-lu で新規作成っぽいのじゃなくて import-lu しておくと気持ち安心かも
  • そもそもろくに考えずに大きな zvol つくるんなら zfs create -s -V 1T しておけ
  • ちなみに Mac からフォーマットすると GPT になってるように見えるが、 GNU Parted 2.3 で適当に print したらこけました。
    • parted が悪いのかどうかは調べてないです。
ichii386@elmo-debian% sudo parted /dev/sdb
GNU Parted 2.3
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Error: The backup GPT table is corrupt, but the primary appears OK, so that will be used.
OK/Cancel? o
Warning: Not all of the space available to /dev/sdb appears to be used, you can fix the GPT to
use all of the space (an extra 1098907648 blocks) or continue with the current setting?
Fix/Ignore? i
Backtrace has 12 calls on stack:
  12: /lib/libparted.so.0(ped_assert+0x31) [0x7f92e3e50351]
  11: /lib/libparted.so.0(ped_geometry_read+0x80) [0x7f92e3e57d70]
  10: /lib/libparted.so.0(hfsplus_probe+0x247) [0x7f92e3e6eb47]
  9: /lib/libparted.so.0(ped_file_system_probe_specific+0x5c) [0x7f92e3e5192c]
  8: /lib/libparted.so.0(ped_file_system_probe+0xab) [0x7f92e3e51edb]
  7: /lib/libparted.so.0(+0x442cf) [0x7f92e3e812cf]
  6: /lib/libparted.so.0(ped_disk_new+0x75) [0x7f92e3e57505]
  5: parted() [0x4070de]
  4: parted(interactive_mode+0xf3) [0x40e183]
  3: parted(main+0x8f) [0x40b1df]
  2: /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xfd) [0x7f92e364bead]
  1: parted() [0x405709]

groonga 試してみた、のその後

ちょっと感想をメモ。

  • 試してみた状況
    • すでに動いている master (fulltext key なし) に、どでかいテーブル 1 つを ENGINE=gronnga にした slave をいれた
    • カラムは id INT NOT NULL auto_increment, foo text, PRIMARY KEY (id), FULLTEXT KEY (foo) みたいな感じ
    • auto_increment は現在 5 億数千万くらいで、 MYD で言うと 70GB くらい。
  • 全般的な感想
    • ふつうに使うぶんにはだいたい動いてる
    • 慣れてない (or 枯れてない) に由来するオペミスの意味で、データロストの危険はあるとおもう
    • まだ本格投入 (正常に動いてる前提なサービス) は厳しい気がする
  • auto_increment はちゃんと動いてる
    • 去年ばっと話題になった時点では auto_increment 未対応だったような気がしますが、いまんとこちゃんと動いてます。
    • これを理由に「ねーな」と思った人は試してみるべき
  • multi column index は未対応
    • まあ、絶対必要な場面はよくわからん
  • fulltext key を使わないクエリはダメな場合がある
    • explain して pushed condition に行った場合に、残念な結果になることがあった。まだ調査中。
    • slave の 1 つなんで、別のサーバ向けちゃえばいいよ。とりあえず。
  • メモリをばんばん使っていく
    • ぎりぎりのところで動き続けている様子
    • なんとなく気持ち悪いが、データ保全のために定期的に mysqld 落としてスナップショットとっているのでいまんとこ大丈夫そう
    • myisam_use_mmap で flush tables 的なのはなさそう
  • ファイルたくさんできる
    • カラムの作り方次第ではありますが、tritonn で 120GB, InnoDB (compact, fulltext なし) で 200GB, mroonga で 140GB ってとこ。
    • けっこう I/O 激しめ。
  • パフォーマンスは、ちゃんと見てません
    • 現状の肥大化した tritonn がヤバすぎなので比較対象としては意味ないが、それよりかはサクサク動いてます。
  • その他
    • load data を 5 億件やったら死んだ
      • GRN_IO_SEG_REF で mmap failed!!! と言われた
      • 1 億件 load -> mysqld restart 繰り返したらいけました。
    • (たぶん) slave sql が insert 中に mysqld 落としたらデータロスト
      • duplicate key で slave 止まってたので skip counter したら特定のカラムだけ空っぽのデータが入っちゃってた
      • 1回しか見かけてないので嘘かもしれないし MySQL 側の問題かも
        • transaction 中に stop slave すると rollback せずにやり直しちゃうとかありましたよね
    • しばらく負荷かけてたら lock failed
      • grn_io_lock が失敗して timeout
      • "groonga ./データファイル" して、 clearlock して念のため mysqld restart したら動いてるようです。
      • これも GRN_ATOMIC_ADD_EX に失敗してる系かな?

なんにしても、 MySQL で動く限り (fulltext 使わない件のように) MySQL 的運用の仕方が援用できるので、 tritonn が破綻寸前な人はけっこうよさそうですね。よくあるストーリーとしては「tritonn でデータ増えすぎ → partition 使えたらその場凌げそうだけど mysql-5.0 なんだよな → groonga で 5.1 以降にいける! 」なんじゃないかなー。

にしても、この例ではデータ多すぎなようです。斯波さんも参加してることだし、やっぱ spider に行くべきですかね。

gnu parallel

twitter で一瞬話題になってたのを見かけたのでちょっとだけ使ってみた。

perl だったのでげんなり。 xargs -P と比べれば早くはないけど、 pcntl_fork() なんかに比べればだいぶ早い。

% time seq 1000 | xargs -P4 -I{} echo {} >/dev/null
seq 1000  0.00s user 0.00s system 0% cpu 0.003 total
xargs -P4 -I{} echo {} > /dev/null  0.12s user 0.39s system 268% cpu 0.191 total
% time seq 1000 | parallel -P4 -I{} echo {} >/dev/null
seq 1000  0.00s user 0.00s system 0% cpu 0.003 total
parallel -P4 -I{} echo {} > /dev/null  1.24s user 1.60s system 150% cpu 1.882 total

試行錯誤した限りだと、 {} がないと arg に必ず入っちゃうのかな?

% seq 3 | xargs -I{} sh -c 'set -x; ps h -p $$ -o pid,pgid'
+ ps h -p 4056 -o pid,pgid
 4056  4054
+ ps h -p 4058 -o pid,pgid
 4058  4054
+ ps h -p 4060 -o pid,pgid
 4060  4054
% seq 3 | parallel -I{} 'set -x; ps h -p $$ -o pid,pgid'
+ ps h -p 4193 -o pid,pgid 1
    1     1
 4193  4153
+ ps h -p 4194 -o pid,pgid 2
    2     0
 4194  4153
+ ps h -p 4196 -o pid,pgid 3
    3     0
 4196  4153

ファイル名を便利に扱うための置換マーカみたいなのが最初から入ってる。 {} = aho/baka.dat だとして

  • {.} = aho/baka
  • {/} = baka.dat
  • {/.} = baka

となる。 paralles ssh 的に使うと便利、みたいな例が幾つか上がってて、たしかに DNS 的ホスト名もファイル名も "." で区切るよな、と思った。

なんでちょっといじった。いや、ほら、だいたい 3 つ削ればホスト名になるじゃん。

-- ./opt/src/parallel/parallel-20110322/src/parallel   2011-03-22 09:00:52.000000000 +0900
+++ /home/ichii386/opt/bin/parallel 2011-04-25 21:06:51.000000000 +0900
@@ -391,8 +391,12 @@
     $Global::quoting = 0;
     $Global::replace{'{}'} = '{}';
     $Global::replace{'{.}'} = '{.}';
+    $Global::replace{'{..}'} = '{..}';
+    $Global::replace{'{...}'} = '{...}';
     $Global::replace{'{/}'} = '{/}';
     $Global::replace{'{/.}'} = '{/.}';
+    $Global::replace{'{/..}'} = '{/..}';
+    $Global::replace{'{/...}'} = '{/...}';
     $/="\n";
     $Global::ignore_empty = 0;
     $Global::interactive = 0;
@@ -3092,7 +3096,11 @@
    '{}' => 0, # Total length of all {} replaced with all args
    '{/}' => 0, # Total length of all {/} replaced with all args
    '{.}' => 0, # Total length of all {.} replaced with all args
+   '{..}' => 0, # Total length of all {..} replaced with all args
+   '{...}' => 0, # Total length of all {...} replaced with all args
    '{/.}' => 0, # Total length of all {/.} replaced with all args
+   '{/..}' => 0, # Total length of all {/..} replaced with all args
+   '{/...}' => 0, # Total length of all {/...} replaced with all args
    'no_args' => undef, # Length of command with all replacement args removed
    'context' => undef, # Length of context of an additional arg 
     };  
@@ -3903,11 +3911,27 @@
        # skip
    } elsif($replacement_string eq "{.}") {
        $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+   } elsif($replacement_string eq "{..}") {
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+   } elsif($replacement_string eq "{...}") {
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
    } elsif($replacement_string eq "{/}") {
        $s =~ s:^.*/([^/]+)/?$:$1:; # Remove dir from argument. If ending in /, remove final /
    } elsif($replacement_string eq "{/.}") {
        $s =~ s:^.*/([^/]+)/?$:$1:; # Remove dir from argument. If ending in /, remove final /
        $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+   } elsif($replacement_string eq "{/..}") {
+       $s =~ s:^.*/([^/]+)/?$:$1:; # Remove dir from argument. If ending in /, remove final /
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+   } elsif($replacement_string eq "{/...}") {
+       $s =~ s:^.*/([^/]+)/?$:$1:; # Remove dir from argument. If ending in /, remove final /
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
+       $s =~ s:\.[^/\.]*$::; # Remove .ext from argument
    }
    if($Global::JobQueue->quote_args()) {
        $s = ::shell_quote_scalar($s);

ところで、この手のやつで先頭にホスト名入れたい時ってどうします?

% get_host_list | parallel -I{} 'ssh {} "ps -unko -opid,pgrp,nice,etime,stime,stat,args | sort -k4 -nr | head -n3" | sed "s/^/{...}: /"'

とかかな。でも sed って quote とか escape とかめんどいし、パッと見なにしてるかわかんなくてやなんですよね。

あとは 'while read line; do echo "{}: $line"; done' とか? いやー。なんとか yes と paste 組み合わせてなんかできないかと試行錯誤して、諦めました。並列 ssh 専用だったら他に解は沢山あるし。

感想

よくこんなの GNU に入ったね、って思った。なんというか、流儀とかいろいろ。

ちょっと試してみたメモ

build

libtool, libssl-dev, pkg-config, autoconf, automake とか入れとこう。要らないかも知んないけど。

  • groonga
% git clone git://github.com/groonga/groonga.git
% ./configure --prefix=/usr/local/groonga --with-default-encoding=euc_jp
% make -j4
% sudo make install
  • mroonga
% git clone git://github.com/mroonga/mroonga.git
% export PKG_CONFIG_PATH=/usr/local/groonga/lib/pkgconfig
% export CFLAGS="-I/usr/local/groonga/include"
% export CXXFLAGS="-I/usr/local/groonga/include"
% export LDFLAGS="-L/usr/local/groonga/lib -R/usr/local/groonga/lib"
% ./configure --with-mysql-source=/home/ichii386/opt/src/mysql/mysql-5.1.53 --with-mysql-config=/usr/local/mysql/bin/mysql_config
% make
% sudo make install

バイナリつくり直すときは drop database からやり直すと確実。

too long key

記号の超長い羅列みたいなゴミレコードで "too long key" のエラーが出る。 tokenizer が、記号列を 1 token とする "TokenBigram" になっていて、無視してもいいんだけど slave である都合 (master でエラーにならない都合) 、止めてはマズい。

こういうゴミ patch で頑張りすぎないのがオトナなんですかね?

--- a/ha_mroonga.cc
+++ b/ha_mroonga.cc
@@ -1133,7 +1133,9 @@ int ha_mroonga::create(const char *name, TABLE *table, HA_CREATE_INFO *info)
 
     if (key_alg == HA_KEY_ALG_FULLTEXT) {
       grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER;
-      grn_obj *token_type = grn_ctx_at(ctx, GRN_DB_BIGRAM);
+      //grn_obj *token_type = grn_ctx_at(ctx, GRN_DB_BIGRAM);
+      const char *t = "TokenBigramSplitSymbolAlphaDigit";
+      grn_obj *token_type = grn_ctx_get(ctx, t, strlen(t));
       grn_obj_set_info(ctx, idx_tbl_obj, info_type, token_type);
     }

謝辞

groonga 開発者のみなさまありがとうございます!もうちょい頑張って使ってみます。