新規作成
OpenIDで認証してエントリーを新規作成します
共有
エントリーにはOpenIDで閲覧と編集に制限かけることができます
変更履歴
編集履歴もあるので、コラボレーションにも活用できます

マルチスレッドなmemcachedのincrが想定通り動かない時がある件

memcached-1.4.4、libevent-1.3bな環境で、memcachedをマルチスレッドで動作させた場合、incrの結果が想定通りじゃないことがある。

とりあえず、現象だけ。


memcachedは、-tでスレッド数を変更できる。

$ memcached -U 0 -p 11211 -t 4

テストスクリプト

use Cache::Memcached::Fast;
use Parallel::ForkManager;

while(1) {
my $memcached = Cache::Memcached::Fast->new({
    servers            => [ { address => "localhost:11211", noreply => 0 }  ] ,
    compress_threshold => 4_000,
    ketama_points      => 250,
    max_failures       => 3,
    failure_timeout    => 2,
});
$memcached->set("hoge",1);
print $memcached->get("hoge");

my $pm =Parallel::ForkManager->new(100);
foreach my $id ( 1..1000 ) {
    $pm->start and next;

    my $childmemcached = Cache::Memcached::Fast->new({
        servers            => [ { address => "localhost:11211", noreply => 0 }  ] ,
        compress_threshold => 4_000,
        ketama_points      => 250,
        max_failures       => 3,
        failure_timeout    => 2,
    });
    for my $loop ( 1..100 ) {
        my $ret = $childmemcached->incr("hoge");
        warn "[$id] failed?" unless $ret;
    }
    $pm->finish;
}
$pm->wait_all_children;

sleep 1;
my $ret = $memcached->get("hoge");
print $ret;
die if $ret != 100001;
sleep 30;
}

期待通りに動けば、最後のgetで100001が返ってループが続くのだが、thread数を2以上にしたときに、たまに100001ではない100000とか99999とか返って来てdieする。また、failed?のwarnはでてこない。

created by blog.nomadscafe.jp

TEST

TEST

created by https://me.yahoo.co.jp/a/VpZUwKlacZ72gB5LIm9ULj16lookDf1ckEYu#0985f

nginxでクライアントが接続を切った場合の400 bad requestの件

★以下のpatchでは解決しないみたい。=> patch変更して対応。

ECONNRESETでクライアントとの接続が切れることはまぁまぁ、あることだということがわかって来た。


nginxでクライアントがリクエストを送る前に接続を切ると、ログに

127.0.0.1 - - [16/Feb/2010:11:30:44 +0900] "-" 400 0 "-" "-"

な感じのものが残り、エラーログにも残るんだけど。

2010/02/16 11:21:52 [info] 29643#0: *1 client closed prematurely connection while reading client request line, client: 127.0.0.1, server: _

たぶん、アクセスログに残るのは違う気がする。


おそらく以下のpatchだけでアクセスログからは消える。

diff -ur nginx-0.7.65.orig/src/http/ngx_http_request.c nginx-0.7.65/src/http/ngx_http_request.c
--- nginx-0.7.65.orig/src/http/ngx_http_request.c       2010-02-02 00:06:25.000000000 +0900
+++ nginx-0.7.65/src/http/ngx_http_request.c    2010-02-16 17:12:09.000000000 +0900
@@ -1132,7 +1132,7 @@
         c->error = 1;
         c->log->action = "reading client request headers";
 
-        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        ngx_http_close_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE);
         return NGX_ERROR;
     }
 
@@ -2846,7 +2846,9 @@
 
     log->action = "logging request";
 
-    ngx_http_log_request(r);
+    if ( error != NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE ) {
+      ngx_http_log_request(r);
+    }
 
     log->action = "closing request";
 
diff -ur nginx-0.7.65.orig/src/http/ngx_http_request.h nginx-0.7.65/src/http/ngx_http_request.h
--- nginx-0.7.65.orig/src/http/ngx_http_request.h       2010-02-02 00:54:02.000000000 +0900
+++ nginx-0.7.65/src/http/ngx_http_request.h    2010-02-16 13:25:33.000000000 +0900
@@ -112,6 +112,7 @@
  * own code to log such situation when a client has closed the connection
  * before we even try to send the HTTP header to it
  */
+#define NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE     498
 #define NGX_HTTP_CLIENT_CLOSED_REQUEST     499
  
created by blog.nomadscafe.jp

Coro::LWP::UserAgent::timeout

use Coro::LWP; すると $ua->timeout が効かなくなるのでpatch

package Coro::LWP::UserAgent::timeout;

use strict;
use warnings;

use Coro;
use Coro::LWP;
use Coro::Timer;
use LWP::UserAgent;
use ;;
use HTTP::Response ();
use HTTP::Status ();

{
    no strict 'refs';
    no warnings 'redefine';
    my $original_send_request = *{'LWP::UserAgent::send_request'}{CODE};
    *{'LWP::UserAgent::send_request'} = sub {
        my $self = shift;

        my $ua_thread;
        my $response = HTTP::Response->new(HTTP::Status::HTTP_REQUEST_TIMEOUT, "timeout");
        $ua_thread  = async {
            my $to_h = AE::timer(
                $self->{timeout},
                0,
                sub {
                    $ua_thread->cancel("");
                }) if($self->{timeout});
            $response = $self->$original_send_request(@_);
        } @_;
        $ua_thread->join;
        $response;
    };
};

1;
created by https://id.mixi.jp/hanabokuro

モブストライクについて全裸で考えてみた

課金による無限レベルアップ効率についてちょっと計算

ヤマモトさんがついったーでつぶやいてらっしゃったことを僕なりにまとめてみました。

 

 

レベルアップごとに上昇する必要経験値

1レベルごとに上昇する必要経験値 = 12.5exp

母国に旅行の経験値効率 = 95exp / 46体力 = 2.06


無限レベルアップ状態を維持するために上げなければならない体力 = 12.5exp / 2.06 = 6.07sp

レベルアップボーナスのみでの不足分 = 6.07 - 5 = 1.07

 

 

リワードポイントをスキルポイントと引き換えて無限レベルアップ

不足分1.07spを、課金でのスキルポイントブーストによって補う


RPのスキルポイント効率 = 14rp / 4sp = 3.5

不足分1.07spを補うのに必要なRP = 1.07sp * 3.5 = 3.75rp

レベルアップすると1rpボーナスがあるので、課金によって必要なRP = 3.75rp - 1rp = 2.75rp

 

 

リワードポイントを体力回復に使用して無限レベルアップ

同様に必要経験値量/2の体力で、RPによる体力回復を利用して無限LvUpを行うと

レベルアップごとに 10rp - 1rp = 9rp 必要になる。

また、連続レベルアップを止めた時点で保有する体力に大きな差もでる。

 

 

レベルアップの効率に焦点を当てると、ステータスアップのほうが効率が良い

RP購入による無限レベルアップの、日本円効率は、ステータスアップのほうが3倍以上良い。


15,000円課金すると 700 / 2.75 = 254.5レベルのスパイラルが可能になる

これにより上昇する体力は約1500となる

 

 

ヤマモトさんのご発言

http://twitter.com/ymtkyk/status/6643610318

http://twitter.com/ymtkyk/status/6643627785

http://twitter.com/ymtkyk/status/6643648792

http://twitter.com/ymtkyk/status/6643682363

created by http://www.hatena.ne.jp/kabitter/

LWP::UserAgent::parse_headerのベンチマーク

titleタグとか勝手にparseするやつね


結果

% perl parse_header.pl
            Rate        lwp lwp-header
lwp        543/s         --       -19%
lwp-header 673/s        24%         --

script

use Benchmark;
use LWP::UserAgent;

my $count = 3000;

Benchmark::cmpthese( $count , {
    'lwp' => sub {
        my $ua = LWP::UserAgent->new;
        my $res = $ua->get('http://localhost/')
    },
    'lwp-header' => sub {
        my $ua = LWP::UserAgent->new;
        $ua->parse_head(0);
        my $res = $ua->get('http://localhost/');
    },
});

created by blog.nomadscafe.jp

LWP vs AnyEvent::HTTP (+ Coro)

AnyEvent::HTTPで同時にアクセスしないようにしてテスト

% perl -I. anyevnet_http.pl
Benchmark: timing 3000 iterations of anyevent, lwp...
  anyevent:  3 wallclock secs ( 1.57 usr +  1.27 sys =  2.84 CPU) @ 1056.34/s (n=3000)
       lwp:  6 wallclock secs ( 4.25 usr +  1.22 sys =  5.47 CPU) @ 548.45/s (n=3000)

script

use Benchmark;
use LWP::UserAgent;
use AnyEvent;
use AnyEvent::HTTP qw//;

$AnyEvent::HTTP::MAX_PER_HOST = 1;
my $count = 3000;

timethese( $count , {
    'lwp' => sub {
        my $ua = LWP::UserAgent->new;
        my $res = $ua->get('http://localhost/')
    },
    'anyevent' => sub {
        my $cv = AE::cv;
        AnyEvent::HTTP::http_get 'http://localhost/', sub { $cv->send('1'); };
        $cv->recv;
    },
});

LWPは速くない


同じようにCoro::LWP

若干遅くなる

Benchmark: timing 3000 iterations of coro...
      coro:  5 wallclock secs ( 1.75 usr +  3.83 sys =  5.58 CPU) @ 537.63/s (n=3000)

use Benchmark;
use Coro;
use Coro::LWP;
use LWP::UserAgent;

my $count = 3000;

async {
timethese( $count , {
    'coro' => sub {
        my $ua = LWP::UserAgent->new;
        my $res = $ua->get('http://localhost/')
    },
});
exit;
};

schedule;
created by blog.nomadscafe.jp

Coro::Channelとtimeoutを組み合わせる

use Plack::Request;
use HTTP::Request;
use Time::HiRes;
use Coro;
use Coro::Channel;
use Coro::AnyEvent;
use Coro::LWP;
use LWP::UserAgent;

my $UA_WORKER_CORO_DESC_IN_WORK = "ua_worker_coro_desc_in_work";
my $UA_WORKER_CORO_DESC_NOT_WORK = "ua_worker_coro_desc_not_work";
my $UA_WORKER_CORO_TIMEOUT_MESSAGE = "ua_worker_coro_timeout";
my $MAX_UA_WORKER = 100;

sub coro_timeout {
    my $timeout = shift;
    my $cb = shift;
    my $coro = $Coro::current;
    $coro->{timeout_at} = Time::HiRes::time() + $timeout;
    my $installed_destroy = $coro->{on_destroy_once} ? 1 : 0;
    $coro->{on_destroy_once} = $cb;
    if ( !$installed_destroy ) {
        $coro->on_destroy( sub {
            my $message = shift;
            return if $message ne $UA_WORKER_CORO_TIMEOUT_MESSAGE;
            my $cb = delete $coro->{on_destroy_once};
            return unless $cb;
            $cb->($message);
        });
    }
}

sub ua_worker {
    my $channel = shift;
    async {
        $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK);
        while(1) {
            my $req = $channel->get();
            $Coro::current->desc($UA_WORKER_CORO_DESC_IN_WORK);
            my $ua = LWP::UserAgent->new( timeout => $req->[1] ); #timeout is ignored
            coro_timeout( $req->[1], sub {
                $req->[2]->send( LWP::UserAgent::_new_response( $req->[0], &HTTP::Status::RC_INTERNAL_SERVER_ERROR, "request timeout" ) );
            });
            my $res = $ua->request($req->[0]);
            $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK);
            $req->[2]->send($res);
        }
    }
}

# build worker threads
my $worker_timer;
sub build_ua_channel {
    warn "[$$] build channel";
    my $channel = Coro::Channel->new();
    for ( my $i=0; $i < $MAX_UA_WORKER; $i++ ) {
        ua_worker($channel);
    }
    $worker_timer = AnyEvent->timer(
        after => 0.5,
        interval => 0.5,
        cb => sub {
            #timeout
            my $now = Time::HiRes::time;
            my @lwp_coro = grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK } Coro::State::list;
            for my $coro (@lwp_coro) {
                if ($now > $coro->{timeout_at}) {
                    $coro->cancel($UA_WORKER_CORO_TIMEOUT_MESSAGE);
                }
            }

            #spawn worker
            my $dead_worker = $MAX_UA_WORKER - 
                scalar( grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK || $_->desc eq $UA_WORKER_CORO_DESC_NOT_WORK } Coro::State::list );
            for ( my $i=0; $i < $dead_worker; $i++ ) {
                warn "[$$] respwan";
                ua_worker($channel);
            }
        }
    );
    return $channel;
}

my $ua_channel;
sub async_ua_request {
    my $request = shift;
    my %args = @_;
    my $timeout = $args{timeout} || 180;
    my $cb = $args{cb} || sub {};
    $ua_channel ||= build_ua_channel();

    my $cv = AE::cv;
    $ua_channel->put( [$request, $timeout, $cv ] );
    $cv->cb( sub { $cb->(shift->recv) } );
}

my $hanlder = sub {
    my $env = shift;
    my $req = Plack::Request->new($env);

    my $cv = AE::cv;

    my $request = HTTP::Request->new(
        $env->{REQUEST_METHOD},
        URI->new_abs($env->{REQUEST_URI},'http://localhost/'),
        [
            map {
                (my $field = $_) =~ s/^HTTPS?_//;
                ( $field => $env->{$_} );
            }
            grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env
        ],
        $req->raw_body
    );

    async_ua_request( $request, timeout => 1, cb => sub {
        my $res = shift;
        my @res_header;
        $res->headers->scan(sub{
            push @res_header, @_;
        });
        $cv->send([
            $res->code,
            \@res_header,
            [$res->content]
        ]);
    });
    
    return sub {
        my $start_response = shift;
        $cv->cb(
            sub {
                $start_response->( shift->recv );
            }
        );
    };
}

TODO: global変数の部分をpackageにしてオブジェクトの中に納める

created by blog.nomadscafe.jp

Coro::Channelをつかってみた

use Plack::Request;
use HTTP::Request;
use Data::Dumper;
use Coro;
use Coro::Channel;
use Coro::AnyEvent;
use Coro::LWP;
use LWP::UserAgent;


# worker threads
sub build_channel {
    warn "[$$] build channel";
    my $channel = Coro::Channel->new();
    for my $i (0..100) {
        async {
            while(1) {
                my $req = $channel->get();
                my $ua = LWP::UserAgent->new();
                my $res = $ua->request($req->[0]);
                my @res_header;
                $res->headers->scan(sub{
                    push @res_header, @_;
                });
                $req->[1]->send([
                    $res->code,                    \@res_header,
                    [$res->content]
                ]);
            }
        }
    }
    return $channel;
}

my $channel;
my $hanlder = sub {
    my $env = shift;
    my $req = Plack::Request->new($env);

    $channel ||= build_channel();

    my $request = HTTP::Request->new(
        $env->{REQUEST_METHOD},
        URI->new_abs($env->{REQUEST_URI},'http://localhost/'),
        [
            map {
                (my $field = $_) =~ s/^HTTPS?_//;
                ( $field => $env->{$_} );
            }
            grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env
        ],
        $req->raw_body
    );

    my $cv = AE::cv;
    $channel->put( [$request,$cv ] );

    return sub {
        my $start_response = shift;
        $cv->cb(
            sub {
                $start_response->( shift->recv );
            }
        );
    };
}

created by blog.nomadscafe.jp

mobstrikeに於ける他攻略要素についての考察等

mobstrikeの攻略とか

自由に編集して下さって結構です。

各階層に於けるクリアボーナス

ごろつき(lvls 1 - 4)
体力 -50秒短縮
ちんぴら(lvls 5 - 8)
スタミナ -50秒短縮
ソルジャー(lvls 9 - 12)
体力 -40秒短縮
用心棒(lvls 13 - 17)
スタミナ -40秒短縮
ヒットマン(lvls 18 - 24)
体力 -30秒短縮
支部長(lvls 25 - 34)
スタミナ -30秒短縮
アンダーボス(lvls 60 - 99)
体力 -20秒短縮
ボス(lvls 99 ~)
スタミナ -20秒短縮

各々をクリア(全ての仕事をレベル3の習熟度100%)することでボーナスが享受出来る。勿論、ごろつき->ソルジャーの様な順にクリアしても問題無く効果を享受出来る様だ。

懸賞金TIPS

  • 気になったマフィアに掛ける事で、現状のそいつを倒せるマフィアを探す事が出来る。

上限

ファミリ
2510#(ただし、URLによる追加は可能な為、更なる高見は目指す事が可能。
created by juner.net

<12345678910>