投稿者「たなか」のアーカイブ

webサーバを5秒で立ち上げるたったひとつの冴えたやりかた

webサーバが欲しい。とても欲しい。でもApacheとか入れてる暇ないよ。
そんなことよくありますね。
そんな時、悩むことなく5秒でwebサーバが立ち上げられます。そうPythonならね。

$ python -m SimpleHTTPServer 8080

これでポート8080でHTTPを待ち受けるぜ。
DocumentRootはカレントだ!
まあ、セキュリティもクソもないので、絶対に外部に晒そうとか思っちゃダメだぜ。

あとでちゃんとかく

まともに使えるPython環境の作り方。 pipとVirtualenv編。
まずはeasy_install。

$ sudo apt-get install python-setuptools

pip入れる。

$ sudo easy_install pip

apt-getで入るやつはバージョンが古いのでやめておけ。

virtualenvとvirtualenvwrapper入れる。

$ sudo pip install virtualenv
$ sudo pip install virtualenvwrapper

.bashrcに設定追加。

export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python2.7
export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

新しいvirtualenv環境作る。

$ mkvirtualenv hoge

virtualenv環境に入る。

$ workon hoge

ざっとこんなもん。

MySQLの行ロックのふしぎ挙動で夜も安心して眠れない

MySQLのびみょーな行ロックに悩まされたのでメモ代わりに。
全部MySQL5.5でInnoDB使っている時のお話デス。

こんなテーブルが有るとしますよ。

create table table001 (
  id int primary key,
  name text
);

そしてこんなデータが入っています。

A> select * from table001;
+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | 水瀬伊織        |
|  2 | 伊織さま        |
|  3 | いおりん        |
|  4 | デコちゃん      |
+----+-----------------+

まあ、データの中身は気にしない方向で。

そんなアイマス好きのAさん。
伊織さまを「デコちゃん」呼ばわりしている id=4 が許せないので書き換えてやろうと決意し、 id=4 に対して select ~ for update で行ロックを取得すべく以下のようなSQLを実行します。

A> begin;
A> select * from table001 where id = 4 for update;
+----+-----------------+
| id | name            |
+----+-----------------+
|  4 | デコちゃん      |
+----+-----------------+

そんな作業中、別の人(プロンプトBね)が何かしようとしていますよ。

B> begin;
B> select * from table001 where name = 'いおりんビーム' for update;

はい、なぜかBさんはここでロックかかってしまいました。
え、name=’いおりんビーム’ なんて行は存在しないのですぐに返ってきて欲しいのですが、なぜ?

ところでAさん、伊織さまの悪口修正作業が終わったみたいですね。

A> update table001 set name = 'デコ呼ばわりとは何事か、このビッチめ' where id = 4;
A> select * from table001 where id = 4 for update;
+----+--------------------------------------------------------+
| id | name                                                   |
+----+--------------------------------------------------------+
|  4 | デコ呼ばわりとは何事か、このビッチめ                   |
+----+--------------------------------------------------------+
A> commit;

ふう、無事に悪口を消してやったぜ。
なんということでしょう、Aさんがコミットした瞬間にBさんのロックが解かれたのです!

えええええええええ、なんでAさんの行ロックがBさんを止めるわけ?????

そしてAさん、id=5を追加しようとしました。

A>  insert into table001 values(5, '水瀬伊織さま');

これもロックに引っかかる!

は? これテーブルロックかかってね?

そう、実にMySQLの行ロック周りはクセのある動きをしやがるのです。
この場合、Bさんの打ったSQLがポイント。
再度提示しましょう。

B> select * from table001 where name = 'いおりんビーム' for update;

ポイントはカラムname。こいつは主キーでもユニークキーでもない、どんだけ重複しようがnullが入ろうが自由だ! なカラムです。
MySQLの select ~ for update は、一意に定まらないカラムに対して検索かけるとテーブルロックをかけようと振舞いやがります。
なので、まずBさんのselectがAさんのロックとかち合ってすっ止まりやがり、そしてAさんがコミットしたら、今度はBさんのテーブルロックがかかるのでAさんのinsertがすっとまる。

えええええええええ、なにこれ?????

逆に一意に定まるカラム(この場合はidの方)は、存在しなくてもテーブルロックかけたりしません。
さっきの例だとBさんは以下のSQLなら何事も無く動いたでしょう。

B> select * from table001 where id = 100 for update;

id=100なんて存在しない主キーのためロックもかかりません。
当然だよね、存在しないんだから。

あーーーーーーーーめんどくせーーーーーーーー。

などと言っていても解決しないので、MySQLでは select ~ for update で行ロックを取りたい時は必ず一意に決まるカラムで検索しましょう。主キーとかユニークキーとかでね。

となると、例えば検索条件が主キー+制約なしのカラムだとどうなるか、試してみたくなりますね。
そこでこんなテーブルを作りますた。

create table table002 (
  id int primary key,
  name text,
  nazo int
);

このテーブルにはこんなデータが入ってます。

A> select * from table002;
+----+-----------------+------+
| id | name            | nazo |
+----+-----------------+------+
|  1 | 三浦あずさ      |   91 |
|  2 | 如月千早        |   72 |
+----+-----------------+------+

くっ…
さて、さっそく主キー+制約なしカラムで行ロックを取得しましょう。

A> begin;
A> select * from table002 where id = 1 and nazo > 90 for update;
+----+-----------------+------+
| id | name            | nazo |
+----+-----------------+------+
|  1 | 三浦あずさ      |   91 |
+----+-----------------+------+

そしてBさんもなにかやってみます。

B> select * from table002 where id = 1 for update;
→Aさんのロックに引っかかる。

B> select * from table002 where id = 2 for update;
→問題なく取得成功。

B> select * from table002 where id = 1 and nazo < 90 for update;
→Aさんのロックに引っかかる(!!)

B> select * from table002 where id = 2 and nazo > 90 for update;
→問題なく取得成功。該当データなし。

B> select * from table002 where id = 3 for update;
→問題なく取得成功。該当データなし。

B> select * from table002 where id = 3 and nazo > 90 for update;
→問題なく取得成功。該当データなし。

うーん、なんともしっくり来ませんが、これがMySQLの振る舞いでやがりますよ。

なので、よくある「存在すればupdate、存在しなければselect」なんてやる時に、必ず検索条件が主キーかユニークキーになっていなければ、なんと恐ろしいことになるのやら。
夜も安心して眠れないのでありますですよ…

XHR2のためにXMLHttpRequestをいじってみる

最近すっかりXHR2ばかりいじってる日々です。
ところがどっこい、JavaScriptの主だったライブラリ(prototype.jsとかjQueryとかjQueryとかjQueryとか)のXHR2実装がなんとも帯に短したすきに長し状態で、思わず自前でXMLHttpRequestをいじるという、我々が何年前に通過したんだろうという所まで舞い戻ってみました。
まあ、コードはあっさりと書け、サーバ側で Access-Control-Allow-Origin:* を返してあげると、さくっとXHR2成功。クロスドメイン通信だよ。やったね。
XHR2だからって特殊なコードはXMLHttpRequestにはほとんど要らないね。
書いたのはこんなコード。クライアント側ね。

function get_xhr_handler() {
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
     } else if(window.ActiveXObject) {
        try {
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    }
    return null;
}

function xhr_get(url, params, callback) {
    var xhr = get_xhr_handler();
    if (xhr == null) {
        console.log("not support XHR.");
        return false;
    }

    var query_array = new Array();
    for (var key in params) {
        query_array.push(key + "=" + encodeURIComponent(params[key]));
    }
    var query = query_array.join("&");

    xhr.open("GET", url + "?" + query, true);
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200){
            callback(xhr);
        }
    };

    xhr.send();
    return true;
}

var send_param = {"str": "ここであったが三年目",
                  "int": 999};
xhr_get("http://api.sample.co.jp/", send_param,
        function(res) {
            // 通信成功...
        });

文字コードはきっとencodeURIComponent()あたりがいい感じにしてくれているはず。
エラー処理が全然入ってないけど、それは宿題だ。
なお、サーバ側のコードは汚いので見せたげないw

PythonでURLを弄るだけの簡単なお仕事

PythonでちょいとばかりURLを弄る必要があったのでメモ。
あるいは、urlparseモジュールで遊んでみたとも言えよう。
まあ、話は簡単でこんなURLがあるとしますぜ。

http://www.sample.co.jp/apps/view?lang=ja&code=utf-8&id=123#top

こいつをこんな感じにパースしたいと。

www.sample.co.jp, /apps/view, lang=ja%code=utf-8%id=123, top

イメージできた?
難しく言うと、ホスト部とパス部、パラメータ群、HTMLで言うところの#hogehogeのあれ、にそれぞれ分割したい。

さっそくgoogle様に教えを請うたところ、urlparseモジュールを使いなさいと。
ふむ、ではいじってみましょう。

$ python
>> import urlparse
>>> parse_url = urlparse.urlparse('http://www.sample.co.jp/apps/view?lang=ja&code=utf-8&id
=123#top')
>>> parse_url
ParseResult(scheme='http', netloc='www.sample.co.jp', path='/apps/view', params='', query='lang=ja&code=utf-8&id=123', fragment='top')
>>> parse_url.netloc
'www.sample.co.jp'
>>> parse_url.path
'/apps/view'
>>> parse_url.query
'lang=ja&code=utf-8&id=123'
>>> parse_url.fragment
'top'

何だあっさりと出来ちまったぜ。さすがPython、恐るべし。

ここまで来るとURLにくっついてきているパラメータもパースしたいですね。

>>> urlparse.parse_qsl(parse_url.query)
[('lang', 'ja'), ('code', 'utf-8'), ('id', '123')]

これまたurlparseモジュールでできちゃったよ。
タプルは扱いづらいよぅ、せめてdictに。という場合はdict()にぶち込めばいい。

>>> dict(urlparse.parse_qsl(parse_url.query))
{'lang': 'ja', 'code': 'utf-8', 'id': '123'}