Hateburo: kazeburo hatenablog

SRE / 運用系小姑 / Goを書くPerl Monger

Kossy::Validator

I released Kossy::Validator to CPAN. It was separated from Kossy

https://metacpan.org/release/Kossy-Validator

You can use Kossy::Validator modules w/o Kossy.

Cache::Memcached::Fast::Safe 0.04 was out. It has get_or_set()

I added get_or_set() to Cache::Memcached::Fast::Safe and released as ver. 0.04

https://metacpan.org/release/Cache-Memcached-Fast-Safe

I saw some people make a module that inherits Cache::Memcached::Fast::Safe only for adding get_or_set().

sample code

use Cache::Memcached::Fast::Safe;
 
my $memd = Cache::Memcached::Fast::Safe->new({
  servers => [..]
});
$memcached->get_or_set('key:'.$id,sub {
  MyDB->retrieve($id)
},$expires);

#or 

$memcached->get_or_set('key:'.$id,sub {
  my $val = MyDB->retrieve($id);
  return ($val, $expires)
});

Released Kossy-0.23. Improve Performance

Kossy-0.23 gets 2x or more faster.

https://metacpan.org/release/Kossy

I made these changes.

  • Cache Kossy::Request->base. URI->canonical is slow.
  • Use Router::Boom for router
  • Use HTTP::Headers::Fast
  • Optimize Kossy::Response->finalize

Benchmark

#!/usr/bin/env perl

use strict;
use warnings;

package MyApp;

use strict;
use warnings;
use Kossy;

get "/" => sub {
    my ( $self, $c )  = @_;
    $c->response->body("ok");
};

router [qw/GET POST/] => "/bar" => sub {
    my ( $self, $c )  = @_;
    $c->response->body("ok");
};

get "/uri_for" => sub {
    my ( $self, $c )  = @_;
    for (1..4) {
        $c->request->uri_for("/uri_for")->as_string;
    }
    $c->response->body( $c->request->uri_for("/uri_for")->as_string );
};

get '/ok' => sub {
    my ( $self, $c )  = @_;
    $c->response->body("ok");
};

package main;

use HTTP::Request::Common;
use HTTP::Message::PSGI;
use Plack::Test;
use Plack::Builder;
use Benchmark qw/cmpthese timethese/;

my $env = req_to_psgi(GET "/uri_for");
my $env_ok = req_to_psgi(GET "/ok");
my $app = MyApp->psgi;

cmpthese(timethese(0,{
    'kossy_uri_for' => sub {
        $app->($env);
    },
    'kossy_ok' => sub {
        $app->($env_ok);
    }
}));
Kossy 0.20
Benchmark: running kossy_ok, kossy_uri_for for at least 3 CPU seconds...
  kossy_ok:  3 wallclock secs ( 3.00 usr +  0.01 sys =  3.01 CPU) @ 5860.47/s (n=17640)
kossy_uri_for:  3 wallclock secs ( 3.02 usr +  0.02 sys =  3.04 CPU) @ 1647.04/s (n=5007)
                Rate kossy_uri_for      kossy_ok
kossy_uri_for 1647/s            --          -72%
kossy_ok      5860/s          256%            -- 
Kossy 0.23
Benchmark: running kossy_ok, kossy_uri_for for at least 3 CPU seconds...
  kossy_ok:  4 wallclock secs ( 3.16 usr +  0.00 sys =  3.16 CPU) @ 12560.13/s (n=39690)
kossy_uri_for:  4 wallclock secs ( 3.24 usr +  0.00 sys =  3.24 CPU) @ 4211.73/s (n=13646)
                 Rate kossy_uri_for      kossy_ok
kossy_uri_for  4212/s            --          -66%
kossy_ok      12560/s          198%            --

Plack::Middleware::Session uses Cookie::Baker

Plack::Middleware::Session 0.21 uses Cookie::Baker for improve performance. Cookie::Baker is simple cookie generator/parser module.

https://metacpan.org/release/Plack-Middleware-Session

Previous version of Plack::Middleware::Session uses Plack::Response for only generating cookie strings. By using Cookie::Baker, cookie header setter becomes 600% faster.

benchmark: https://gist.github.com/kazeburo/6896740

Benchmark: running baker, original for at least 1 CPU seconds...
     baker:  1 wallclock secs ( 1.06 usr +  0.00 sys =  1.06 CPU) @ 95466.98/s (n=101195)
  original:  1 wallclock secs ( 1.14 usr +  0.00 sys =  1.14 CPU) @ 13472.81/s (n=15359)
            Rate original    baker
original 13473/s       --     -86%
baker    95467/s     609%       --

Released Cookie::Baker to CPAN

I released Cookie::Baker that provides HTTP cookie generator and parser

http://search.cpan.org/~kazeburo/Cookie-Baker/
https://metacpan.org/release/Cookie-Baker

### synopsis

use Cookie::Baker;

$headers->push_header('Set-Cookie', bake_cookie('foo','val'));

my $cookie = bake_cookie('foo', {
    val => 'val',
    path => "test",
    domain => '.example.com',
    expires => '+24h'
});

my $cookies_hashref = crush_cookie($headers->header('Cookie'));

bake_cookie's expires accepts some formats like this.

expires => time + 24 * 60 * 60 # epoch time
expires => 'Wed, 03-Nov-2010 20:54:16 GMT' 
expires => '+30s' # 30 seconds from now
expires => '+10m' # ten minutes from now
expires => '+1h'  # one hour from now 
expires => '-1d'  # yesterday (i.e. "ASAP!")
expires => '+3M'  # in three months
expires => '+10y' # in ten years time
expires => 'now'  #immediately

I know that cpan has already may cookie related modules. But dead simple cookie generator and parser module to use Plack::Middlewares and Web Applications isn't there.

Monoceros supports accept4(2)

tokuhirom released Linux::Socket::Accept4 that provides accept4(2)
Monoceros-0.25 uses accept4 if Linux::Socket::Accept4 is avaliable.

accept4(2) can set FD_CLOEXEC and O_NONBLOCK in one system call.

Monoceros-0.24

select(16, [4 10], NULL, NULL, {1, 0})  = 1 (in [4], left {0, 999997})
accept(4, {sa_family=AF_INET, sin_port=htons(41296), sin_addr=inet_addr("127.0.0.1")}, [16]) = 6
ioctl(6, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffc03016c0) = -1 EINVAL (Invalid argument)
lseek(6, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ioctl(6, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffc03016c0) = -1 EINVAL (Invalid argument)
lseek(6, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
fcntl(6, F_SETFD, FD_CLOEXEC)           = 0
fcntl(6, F_SETFL, O_RDONLY|O_NONBLOCK)  = 0
setsockopt(6, SOL_TCP, TCP_NODELAY, [1], 4) = 0
read(6, "GET / HTTP/1.0\r\nHost: localhost:"..., 131072) = 82
gettimeofday({1381121587, 895814}, NULL) = 0
write(6, "HTTP/1.1 200 OK\r\nDate: Mon, 07 O"..., 112) = 112
close(6)                                = 0

Monoceros-0.25

select(16, [4 10], NULL, NULL, {1, 0})  = 1 (in [4], left {0, 999997})
accept4(4, {sa_family=AF_INET, sin_port=htons(42605), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_CLOEXEC|SOCK_NONBLOCK) = 6
setsockopt(6, SOL_TCP, TCP_NODELAY, [1], 4) = 0
read(6, "GET / HTTP/1.0\r\nHost: localhost:"..., 131072) = 82
gettimeofday({1381121654, 200708}, NULL) = 0
write(6, "HTTP/1.1 200 OK\r\nDate: Mon, 07 O"..., 112) = 112
close(6)                                = 0

Monoceros-0.25 also skips lseek and ioctl system calls. Perlio layer add these system calls. so I added "use open IO => :unix" before accept().

In my benchmarking, there are no performance effects with accept4. hmmm

ISUCON3 予選に参加してきました。無念ばかり残る

isucon3予選に @sugyan @tagomorisとLINE選抜チームを組んで参加しました。共催枠なので結果に関わらず本戦には出れるのですが、結果は総合8位。無念です

やったこと

sugyanのエントリが詳しいのですが、自分がやったのは

  • nginx(openresty)への入れ替え
  • mysql 5.5へのダウングレード
  • Starlet/Monocerosへの入れ替え

正直5.6とか分からないので5.5で。my.cnfを公開してあるやつから持って来てbinlogを消したりした。おかげで memcached plugin にまったく気付いてなかった。

あれこれ

自分とtagomorisはこれまでの2回とも出題側だったので、参加は今回が初。予選は免除されているのですが、出ておいてよかった。これほど出来る事が少ないのかと驚いた。この短時間でアプリケーションを作り直すのマジ凄い。自分、速くプログラム書くのが苦手なので sugyan がいて助かった

ということで、予選の復習と本戦に向けてのあれこれ作戦をチームで考えて行きたい所存

さいごに

予選・本戦あるの大変だ。 @fujiwara ++。