BlackCurtain::DBI::Cache::Memcachedって早いの? (2) 
Thursday, January 13, 2011, 12:10 PM - Programing, Programing / Perl
Posted by Administrator
実のところBlackCurtain::DBI::Cache::Memcachedが遅い、加えてその検証結果も予定調和だったりして

検証用コードは前回検証の付録と同一(timetheseの直前でCool'n'Quiteの影響を緩和するため糞ループを追加している)
sub BlackCurtainDBI
{
$sth2->bind_param(1,$ARGV[1],DBI::SQL_INTEGER);
$sth2->execute();
while($sth2->fetch()){
}
$sth2->finish();

return();
}

結果
> === 1 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 14 wallclock secs ( 7.73 usr + 2.12 sys = 9.85 CPU) @ 10152.28/s (n=100000)
> === 10 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 19 wallclock secs (13.48 usr + 1.76 sys = 15.24 CPU) @ 6561.68/s (n=100000)
> === 100 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 71 wallclock secs (62.88 usr + 3.10 sys = 65.98 CPU) @ 1515.61/s (n=100000)
この傾向は前回の検証でも十分観測できる

と、いうことはBlackCurtain::DBI::Cache::Memcached::stのsub FETCHが高い確率で悪
邪道だが動作そのものに問題ないので削除

そして結果
> === 1 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 9 wallclock secs ( 7.51 usr + 0.87 sys = 8.38 CPU) @ 11933.17/s (n=100000)
> === 10 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 16 wallclock secs (10.18 usr + 1.83 sys = 12.01 CPU) @ 8326.39/s (n=100000)
> === 100 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 58 wallclock secs (49.93 usr + 1.05 sys = 50.98 CPU) @ 1961.55/s (n=100000)
正直コード削れば早くなるのは実に当たり前の話だがな
ともかく今回に限らずsub FETCH{ ... }へ割り込むのは余り好ましくないっていうのははっきり見える結果

そしてこれを踏襲し
・ FETCH/STOREは最小限にしろ
・ サブクラスから->SUPERするな
・ コードを書くな
の3点を重点に置いて考えるならば

->execute(...)で勝手に全部キャッシュに突っ込んで->fetch(...)で->SUPER::fetch(...)しない
こんな感じのコードが好ましい
sub execute
{
my($f,@r) = @_;

my $memcached = \$f->{Database}->{Memcached_};
my $cache = \$f->{Memcached_cache};
(my $q = $f->{Statement}) =~s/\?/$f->{ParamValues}->{my $i++}/go;
if(!($$cache = $$memcached->get($q))){
$f->SUPER::execute();
push(@{$$cache},$row) while(my $row = $f->SUPER::fetch());
$$memcached->set($q,\@$$cache);

}
return($#{$$cache});
}

sub fetch
{
my($f,@r) = @_;

return(shift(@{$f->{Memcached_cache}}));
}

結果(5回試行して最高と最低を記載、どうしてか揺れる)
> === 1 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 7 wallclock secs ( 4.65 usr + 0.84 sys = 5.49 CPU) @ 18214.94/s (n=100000)
> === 1 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 11 wallclock secs ( 5.28 usr + 2.16 sys = 7.44 CPU) @ 13440.86/s (n=100000)
> === 10 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 7 wallclock secs ( 4.74 usr + 0.86 sys = 5.60 CPU) @ 17857.14/s (n=100000)
> === 10 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 12 wallclock secs ( 5.41 usr + 2.25 sys = 7.66 CPU) @ 13054.83/s (n=100000)
> === 100 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 8 wallclock secs ( 5.41 usr + 0.89 sys = 6.30 CPU) @ 15873.02/s (n=100000)
> === 100 records, 100000 repeats ===
> Benchmark: timing 100000 iterations of BCDBI(a)...
> BCDBI(a): 10 wallclock secs ( 5.54 usr + 1.67 sys = 7.21 CPU) @ 13869.63/s (n=100000)
1.0bと比べると理想的なカーブでパフォーマンスが向上している

pDBIが1, 10, 100 recordsで7739.94/s, 4095.00/s, 880.98/sである事を考えると13000/s以上の速度は上々といったところか
add comment ( 1702 views )   |  permalink
BlackCurtain::DBI::Cache::Memcachedって早いの? 
Tuesday, January 11, 2011, 04:43 AM - Programing, Programing / Perl
Posted by Administrator
BlackCurtain::DBI::Cache::Memcachedは自分用途(も当然あるが)ではなく業務用途なので速度を気にする必要がある
1.0bはMemcachedこそ使うが、恩恵による速度向上と拡張による速度低下のバランスが麗しくないので、その検証

実験用のコードはこれ
#!/usr/bin/perl
use Benchmark;
use DBI;
use BlackCurtain::DBI::Cache::Memcached;

sub perlDBI
{
$dbh = DBI->connect(qw(dbi:mysql:database=a;host=172.16.2.205;port=3306 a));
$sth = $dbh->prepare("SELECT * FROM `a` WHERE 1 LIMIT 0,?");
$sth->bind_param(1,$ARGV[1],DBI::SQL_INTEGER);
$sth->execute();
while($sth->fetch()){
}
$sth->finish();
$dbh->disconnect();

return();
}

sub BlackCurtainDBI
{
$dbh = BlackCurtain::DBI::Cache::Memcached->connect(qw(dbi:mysql:database=a;host=172.16.2.205;port=3306 a),undef,{Memcached =>{servers =>[qw(127.0.0.1:11211)]}});
$sth = $dbh->prepare("SELECT * FROM `a` WHERE 1 LIMIT 0,?");
$sth->bind_param(1,$ARGV[1],DBI::SQL_INTEGER);
$sth->execute();
while($sth->fetch()){
}
$sth->finish();
$dbh->disconnect();

return();
}

sub BlackCurtainDBI_noncache
{
$dbh = BlackCurtain::DBI::Cache::Memcached->connect(qw(dbi:mysql:database=a;host=172.16.2.205;port=3306 a));
$sth = $dbh->prepare("SELECT * FROM `a` WHERE 1 LIMIT 0,?");
$sth->bind_param(1,$ARGV[1],DBI::SQL_INTEGER);
$sth->execute();
while($sth->fetch()){
}
$sth->finish();
$dbh->disconnect();

return();
}

printf("=== %d records, %d repeats ===\n",reverse(@ARGV));

timethese(
$ARGV[0],
{
"pDBI" =>\&perlDBI,
"BCDBI(a)" =>\&BlackCurtainDBI,
"BCDBI(b)" =>\&BlackCurtainDBI_noncache,
}
);

__END__

で、測定
(a)はMemcachedを使用、(b)は使用していない
> === 10 records, 100 repeats ===
> Benchmark: timing 100 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 0 wallclock secs ( 0.17 usr + 0.04 sys = 0.21 CPU) @ 476.19/s (n=100)
> BCDBI(b): 1 wallclock secs ( 0.15 usr + 0.03 sys = 0.18 CPU) @ 555.56/s (n=100)
> pDBI: 0 wallclock secs ( 0.10 usr + 0.02 sys = 0.12 CPU) @ 833.33/s (n=100)
> === 100 records, 100 repeats ===
> Benchmark: timing 100 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 1 wallclock secs ( 0.18 usr + 0.03 sys = 0.21 CPU) @ 476.19/s (n=100)
> BCDBI(b): 1 wallclock secs ( 0.65 usr + 0.04 sys = 0.69 CPU) @ 144.93/s (n=100)
> pDBI: 1 wallclock secs ( 0.18 usr + 0.03 sys = 0.21 CPU) @ 476.19/s (n=100)
> === 1000 records, 100 repeats ===
> Benchmark: timing 100 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 1 wallclock secs ( 0.69 usr + 0.02 sys = 0.71 CPU) @ 140.85/s (n=100)
> BCDBI(b): 3 wallclock secs ( 2.02 usr + 0.06 sys = 2.08 CPU) @ 48.08/s (n=100)
> pDBI: 3 wallclock secs ( 0.97 usr + 0.13 sys = 1.10 CPU) @ 90.91/s (n=100)
> === 10000 records, 100 repeats ===
> Benchmark: timing 100 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 6 wallclock secs ( 5.93 usr + 0.04 sys = 5.97 CPU) @ 16.75/s (n=100)
> BCDBI(b): 27 wallclock secs (17.03 usr + 0.87 sys = 17.90 CPU) @ 5.59/s (n=100)
> pDBI: 15 wallclock secs ( 4.98 usr + 0.83 sys = 5.81 CPU) @ 17.21/s (n=100)
> === 100000 records, 100 repeats ===
> Benchmark: timing 100 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 58 wallclock secs (56.26 usr + 0.22 sys = 56.48 CPU) @ 1.77/s (n=100)
> BCDBI(b): 240 wallclock secs (144.47 usr + 13.74 sys = 158.21 CPU) @ 0.63/s (n=100)
> pDBI: 131 wallclock secs (34.73 usr + 13.61 sys = 48.34 CPU) @ 2.07/s (n=100)
Memcachedを組み込む事で評判通り爆発的な速度が見込める事がわかる
拡張による速度低下も凄まじい(2~3倍)が、それを相殺しきれているのだから驚く
しかし(非現実的だが)10000レコードまで膨らむと1.0aではどうしようもなくなる、こんなの当たり前の有り得ない話(sub fetch{ ... }を見たまえ、こんなコードで10000レコードを扱えるわけがない)だが

ともかくMemcachedはぶっ飛ぶ程早い
では現状だとキャッシュ利用による速度向上幅はどの程度なのか
> === 10 records, 1000 repeats ===
> Benchmark: timing 1000 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 5 wallclock secs ( 1.82 usr + 0.53 sys = 2.35 CPU) @ 425.53/s (n=1000)
> BCDBI(b): 6 wallclock secs ( 2.03 usr + 0.30 sys = 2.33 CPU) @ 429.18/s (n=1000)
> pDBI: 5 wallclock secs ( 1.12 usr + 0.28 sys = 1.40 CPU) @ 714.29/s (n=1000)
> === 100 records, 1000 repeats ===
> Benchmark: timing 1000 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 5 wallclock secs ( 2.08 usr + 0.21 sys = 2.29 CPU) @ 436.68/s (n=1000)
> BCDBI(b): 9 wallclock secs ( 4.37 usr + 0.23 sys = 4.60 CPU) @ 217.39/s (n=1000)
> pDBI: 7 wallclock secs ( 1.97 usr + 0.35 sys = 2.32 CPU) @ 431.03/s (n=1000)
> === 1000 records, 1000 repeats ===
> Benchmark: timing 1000 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 10 wallclock secs ( 6.52 usr + 0.16 sys = 6.68 CPU) @ 149.70/s (n=1000)
> BCDBI(b): 36 wallclock secs (21.83 usr + 0.57 sys = 22.40 CPU) @ 44.64/s (n=1000)
> pDBI: 25 wallclock secs ( 9.88 usr + 1.32 sys = 11.20 CPU) @ 89.29/s (n=1000)
> === 10 records, 10000 repeats ===
> Benchmark: timing 10000 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 50 wallclock secs (19.32 usr + 3.02 sys = 22.34 CPU) @ 447.63/s (n=10000)
> BCDBI(b): 58 wallclock secs (19.18 usr + 2.28 sys = 21.46 CPU) @ 465.98/s (n=10000)
> pDBI: 46 wallclock secs ( 9.55 usr + 2.36 sys = 11.91 CPU) @ 839.63/s (n=10000)
> === 100 records, 10000 repeats ===
> Benchmark: timing 10000 iterations of BCDBI(a), BCDBI(b), pDBI...
> BCDBI(a): 47 wallclock secs (19.68 usr + 1.75 sys = 21.43 CPU) @ 466.64/s (n=10000)
> BCDBI(b): 110 wallclock secs (59.99 usr + 2.84 sys = 62.83 CPU) @ 159.16/s (n=10000)
> pDBI: 68 wallclock secs (18.11 usr + 3.25 sys = 21.36 CPU) @ 468.16/s (n=10000)
と、いうような結果になった
これは"恩恵による速度向上=拡張による速度低下"ということ
1.0bはまるで意味がない

付録(運用予定のセッション維持なコードを使ったベンチマーク)はREADMORE

Read More...
add comment ( 814 views )   |  permalink
BlackCurtain-DBI-Cache-Memcached-1.0b.tar.gz 
Monday, January 10, 2011, 09:41 AM - Release
Posted by Administrator
Archive : BlackCurtain-DBI-Cache-Memcached-1.0b.tar.gz (755B)

DBIそのままでMemcachedの恩恵を受けられるモジュール……の試作版

従来のDBIなコード
my $dbh = DBI->connect(qw(dbi:mysql:database=database;host=127.0.0.1;port=3306 user pass));

my $dbh = BlackCurtain::DBI::Cache::Memcached->connect(qw(dbi:mysql:database=database;host=127.0.0.1;port=3306 user pass),{Memcached =>{servers =>[qw(127.0.0.1:11211)]}});
と、変更するだけ

※注意
試作版なので恩恵によるパフォーマンス向上がプラスであるとは限らない件について
add comment ( 1774 views )   |  permalink
DBIのSubclassを書く 
Saturday, January 8, 2011, 07:48 AM - Programing, Programing / Perl
Posted by Administrator
いつだかTwitterでMySQL+Memcachedがどうたらこうたら言っていたが、最終的な結論としてDBIのSubclassとして実装するのが最強なんじゃないのかなあ、とか思う、これが大晦日だか元旦
そんな事を思い付いてしまったら年越し蕎麦をゆっくり食って餅の無差別殺人事件をゲラゲラ笑いながら見ている場合ではない
ともかく最適化を捨てても、既存のDBIなコードを変更せずともMemcachedの恩恵を受けるメリットは非常に大きいと思う
こうなれば「あけましておめでとう」などと心にもない挨拶回りをしている場合ではない、大体お年玉貰えないし

と、いう事でDBIを解読しながら色々模索するが、悩む部分がいくつかある

#!/usr/bin/perl
use DBI;

printf("dbh=%s\n",my $dbh = DBI->connect("dbi:mysql:database=a;host=172.16.2.130;port=3306","a",undef));
$dbh->{myparam} = "^o^";
printf("dbh->myparam = %s\n",$dbh->{myparam});
$dbh->disconnect();

__END__

の、出力
> dbh=DBI::db=HASH(0x13b13c60)
> dbh->myparam =
これは一体どういうことだろうか
HASHと見えているが、値が反映されない

少なくともこの状態はSubclassを書く上での例えば
package MyDBI;
use base qw(DBI);
use DBI;
use Cache::Memcached::libmemcached;

sub connect
{
my($h,@p) = @_;
$h->SUPER::connect(@_);
$h->{memcached} = new Cache::Memcached::libmemcached(...);
return($h);
}

こういうコードでCache::Memcached::libmemcachedのオブジェクトを維持できないことになる

全くわからなくてtieしてblessしてなんてやっていたが、実に単純
種が割れてしまえばなんて事はないのだが、丸一日要してしまった
DBIのdbh->{}やsth->{}な変数アクセスはtieされているので
package MyDBI::db;
use base qw(DBI::db);

sub FETCH
{
my($h,$k) = @_;
if($k =~ /^mydbi_/){
$h->{$k};
}else{
$h->SUPER::FETCH($k);
}
}

sub STORE
{
my($h,$k,$v) = @_;
if($k =~ /^mydbi_/){
$h->{$k} = $v;
}else{
$h->SUPER::STORE($k,$v);
}
}

このように割り込んで変数処理をしなければならない
よく考えれば当たり前の話なのだが

また$dbhとか$sthをhashとして扱うと値が見えないが、当然FETCH/STORE内で出力させれば
> FetchHashKeyName = NAME
> TraceLevel = 0
> ImplementorClass = DBD::mysql::db
> dbi_imp_data =
> State = SCALAR(0x1b287450)
> Username = miko
> Errstr = SCALAR(0x1b287410)
> Driver = DBI::dr=HASH(0x1b7bcab0)
> Statement =
> Name = database=a;host=172.16.2.130;port=3306
> dbi_connect_closure = CODE(0x1b7bcbc0)
> RootClass = CachedDBI
> Err = SCALAR(0x1b2873d0)
ちゃんとhashとして見える
これが見えなくて->{Statement}とか->{ParamValues}とか->{Database}とかで困っていた

こういう変数絡みでいくらか問題を抱えていたが、結局FETCH/STOREのこれだけで解決
脳味噌の柔軟性が失われつつあるなあ、とか思う事件

残るはアプローチの問題

Memcachedへ値を置く時のkeyをどうするか
当然DBIと異なる方法(executeでkeyを与えなければならない、とか)は方向性と反してしまう(useの一行を書き換えるのみで済ませたい)
普通に考えればSQL Queryがkeyだが、この場合insert/updateの後、一度selectしなければMemcachedを使えない

どのタイミングでMemcachedへ聞きにいくか
->execute(...)でSQL Queryを発行している以上、ここしかない
しかし実際の値を引っ張るのは->fetch(...)なので関数を跨ぐことになる、余り好ましくない
->execute(...)にしても内部へ割り込みたい(StatementやNUM_OF_FIELDSの関係)が、xsなので厳しい

->execute(...)でチェックのみ、->fetch(...)するとしても->fetch(...)は複数回呼ばれる
対してMemcachedのgetは一度
と、なると->execute(...)か最初の->fetch(...)で値を全て読み込む事になる、好ましくない
しかし->fetch(...)ごとにkeyを与えると、分散している場合に整合性が保てなくなる可能性がある、それを回避するために->fetch(...)し続けて値が無ければ->execute(...)というのもおかしな話だ

なんて頭の中で巡り巡る
この割り込み部分はまだいい案が出ない

とりあえず試し書きしたコードを貼っておく

Read More...
add comment ( 1848 views )   |  permalink
Apache+mod_perl Perl*Var 
Tuesday, January 4, 2011, 10:32 PM - Programing, Programing / Perl
Posted by Administrator
忘れたら困る個人的なApache+mod_perlの挙動
とは言っても複雑怪奇な仕様ではなく、十分推測の範疇だが

Apacheはこう
<Location /level1>
PerlSetVar set level1
PerlAddVar add level1
</Location>
<Location /level1/level2>
PerlSetVar set level2
PerlAddVar add level2
</Location>
<Location /level1/level2/level3>
PerlSetVar set level3
PerlAddVar add level3
</Location>


各階層で実行するコードはこう
#!/usr/bin/perl
use Apache2::RequestRec;
use Apache2::RequestUtil;
use APR::Table;

printf("Content-Type: text/html\r\n\r\n");
printf("%s<br>\n",Apache2::RequestUtil->request()->location());
printf(" PerlSetVar: %s<br>\n",Apache2::RequestUtil->request()->dir_config("set"));
printf(" PerlAddVar: %s<br>\n",Apache2::RequestUtil->request()->dir_config("add"));

__END__


結果は捻りもなくこちら
> /level1
> PerlSetVar: level1
> PerlAddVar: level1

> /level1/level2
> PerlSetVar: level2
> PerlAddVar: level1

> /level1/level2/level3
> PerlSetVar: level3
> PerlAddVar: level1

PerlAddVarの値は->dir_config(...)で取ると常に先頭の値がscalarで返る
arrayで取る必要があるならば->dir_config()->get(...)とする

これらのPerl*Varはhttpd.confの影響を受ける
挙動そのものは推測できる範囲だが、つまり
<Location /level1/level2/level3>
PerlSetVar set level3
PerlAddVar add level3
</Location>
<Location /level1>
PerlSetVar set level1
PerlAddVar add level1
</Location>
<Location /level1/level2>
PerlSetVar set level2
PerlAddVar add level2
</Location>

ならば、/level1/level2/level3の出力はこのように変化する
> /level1/level2
> PerlSetVar: level2
> PerlAddVar: level3 level1 level2
注意すべきは->location()が/level1/level2/level3を返す事がなくなる、ということだ

これらの前提で変数継承
<Location /level1/level2/level3>
PerlSetVar set level3
PerlAddVar add level3
PerlSetVar level3 3
</Location>
<Location /level1>
PerlSetVar set level1
PerlAddVar add level1
PerlSetVar level1 1
</Location>
<Location /level1/level2>
PerlSetVar set level2
PerlAddVar add level2
PerlSetVar level2 2
</Location>

表示用の無駄に長いコード
printf(" levels: %s %s %s<br>\n",Apache2::RequestUtil->request()->dir_config("level1"),Apache2::RequestUtil->request()->dir_config("level2"),Apache2::RequestUtil->request()->dir_config("level3"));

結果
> /level1
> PerlSetVar: level1
> PerlAddVar: level1
> levels: 1

> /level1/level2
> PerlSetVar: level2
> PerlAddVar: level1 level2
> levels: 1 2

> /level1/level2
> PerlSetVar: level2
> PerlAddVar: level3 level1 level2
> levels: 1 2 3

ここまでで基本的な動作が観測できたので、次に->location_merge(...)で意図的に値を弄る

実際にはファイルシステム上に存在していないlevel4階層の設定を変なところに追記
<Location /level1/level2/level3>
PerlSetVar set level3
PerlAddVar add level3
PerlSetVar level3 3
</Location>
<Location /level1>
PerlSetVar set level1
PerlAddVar add level1
PerlSetVar level1 1
</Location>
<Location /level1/level2/level3/level4>
PerlSetVar set level4
PerlAddVar add level4
PerlSetVar level4 4
</Location>
<Location /level1/level2>
PerlSetVar set level2
PerlAddVar add level2
PerlSetVar level2 2
</Location>

この設定でlevel4階層が存在したと仮定して今までの話ならこの出力が想定される
> /level1/level2
> PerlSetVar: level2
> PerlAddVar: level3 level1 level4 level2
> levels: 1 2 3 4

->location_merge(...)を含めた検証用コード
#!/usr/bin/perl
use Apache2::RequestRec;
use Apache2::RequestUtil;
use APR::Table;

Apache2::RequestUtil->request()->location_merge("/level1/level2/level3/level4");

printf("Content-Type: text/html\r\n\r\n");
printf("%s<br>\n",Apache2::RequestUtil->request()->location());
printf(" PerlSetVar: %s<br>\n",Apache2::RequestUtil->request()->dir_config()->get("set"));
printf(" PerlAddVar: %s %s %s %s<br>\n",Apache2::RequestUtil->request()->dir_config->get("add"));
printf(" levels: %s %s %s %s<br>\n",Apache2::RequestUtil->request()->dir_config("level1"),Apache2::RequestUtil->request()->dir_config("level2"),Apache2::RequestUtil->request()->dir_config("level3"),Apache2::RequestUtil->request()->dir_config("level4"));

__END__


結果
> /level1/level2/level3/level4
> PerlSetVar: level4
> PerlAddVar: level4
> levels: 4

> /level1/level2/level3/level4
> PerlSetVar: level4
> PerlAddVar: level4
> levels: 4

> /level1/level2/level3/level4
> PerlSetVar: level4
> PerlAddVar: level4
> levels: 4
どの階層でも同一の、->location_merge(...)で与えた階層しか得られなくなる

もし凝ったシステムを作るつもりがなければ全く知らなくていいこと

ああ、余談だが仮に各Locationに独自のPerl*Handlerを仕掛けたとして、その挙動はPerlSetVarと同一
最後のPerl*Handlerで仕掛けたhandler:methodのみが走る
add comment ( 2195 views )   |  permalink

<<First <Back | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Next> Last>>