Psycopg2でサーバーに接続できない

Psycopg2を使っていて、multiprocessingでデータベースに接続してデータを入れる処理を書こうとしていたんだが、↓のエラーで躓いた。

 could not connect to server: Connection refused
        Is the server running on host "localhost" and accepting
        TCP/IP connections on port 5432?
netstat -an| grep 5432

で確認しても

tcp    0    0 127.0.0.1:5432    0.0.0.0:*    LISTEN

とあって、動いてるように見える。
postgresql.confの設定も

listen_address = 'localhost'
port = 5432

となっていることを確認した。
それでも繋がらない。


結論を言うと、

con = psycopg2.connect(database=db, user=user, password=passwd)

のように書く必要がある。修正前は

con = psycopg2.connect(database=db, host='localhost', user=user, password=passwd)

としていた。
hostを設定していたとき、ログやpsなどで見ていると5432以外のportでのアクセスがあった。きっとこれがダメなんだろうとあたりをつけて何とかTCP/IPではなくUnixドメインでアクセスできないかと探していると、psycopg2では、hostの設定を省くとUnixドメインでのアクセスになるというので試してみたらうまくつながった。

クラスの並列化

multiprocessingモジュールで関数を並列化する方法はネットにあるが、クラスを並列化するというのは少なかったので、やってみた。結論としては、処理速度上げるのしんどいしよくわからんエラー出るしで結局関数を並列化する事で落ち着いた。

何がしたいのか?

  1. ファイルからデータを読み込む
  2. SQLに加工するクラスにデータを渡す
  3. 上記クラスでSQLを生成し、実行

並列化するのは↑の2、3の部分

環境

Ubuntu12.04
Python2.7
PostgreSQL9.1.6

実装

ここの、プロセスへメッセージを渡す のところを参考に実装してみた。

class Sample:
    def __init__(self, json):
        self.json = json
        self.con = DBに繋ぐ関数
        self.cur = self.con.cursor()
    def __call__(self):
        #実際に並列化して実行したい関数
        self.add_db()
    def creat_sql(self):
        hogehoge
    def add_db(self):
        #クラス変数やインスタンス変数の扱いが面倒なので、関数をネストさせてローカルスコープ内で処理
        def main():
            sql = self.creat_sql()
            self.cur.execute(sql)

class Worker(multiprocessing.Process):
    def __init__(self, task_queue, result_queue):
        multiprocessing.Process.__init__(self)
        self.task_queue = task_queue
        self.result_queue = result_queue
    def run(self)
        while True:
            next_task = self.task_queue.get()
            if next_task == None:
                self.task_queue.task.done()
                break
            answer = next_task()
            self.task_queue.task_done()
            self.result_queue.put(answer)

f = open('読み込むファイル', 'r')
jsons = simplejson.load(f)
tasks = multiprocessing.JoinableQueue()
results = multiprocessing.Queue()
workers = [Worker(tasks, results) for i in range(multiprocessing.cpu_count() * 10)]
for w in workers:
    w.start()
#各ワーカーにデータを挿入
for w, data in itertools.izip(itertools.cycle(workers), json):
    tasks.put(tweet.Tweet(data))
#各Worker に poison pill を挿入
for w in iter(json):
    tasks.put(None)
tasks.join()
jobs = len(json)
while jobs:
    res = results.get()
    print 'res:', res
    jobs -= 1

Workerクラス以下はほとんど変えてないな。
取り敢えずはこれで動いたんだが、ここでも言ってるようにあまり処理速度が向上しなかった(8coreの自分の環境の場合、CPU使用率が20%程度にしかならなかった。map関数を使ったときは90%程度まで向上した)。ただ単にプログラムの書き方が悪いんだろう。
Workerクラスを使わずにmultiprocessing.Poolのmap関数を使ったほうがずっと早かった(Workerクラス使用時:2H → map関数使用時:11m)。これだけ違うってことは、プログラムの書き方が悪いんだろう。たぶん大事(だと思うから)二度書いてみた。
map関数使用時のコード

def add(data):
    s = Sample(data)
    s.add()
p = multiprocessing.Pool()
p.map(add, jsons)

詰まったところ備忘録

1.Sampleクラスのメソッドでcursorオブジェクトは渡せない(↓のような使い方)ので、curはインスタンス変数として持っておく

#ダメな例
cur = con.cursor()
def hoge(self, a, b, cur):
    hogehoge
    cur.execute(sql)
self.hoge(a=aaa, b=bbb, cur=cur)
#良い例
self.cur = self.con.curosr()
def hoge(self, a, b):
    hogehoge
    self.cur.execute(sql)
self.hoge(a=aaa, b=bbb)

2.インスタンス変数
クラスメソッドAの中で定義したインスタンス変数AとクラスメソッドBの中で定義したインスタンス変数Aは別だと思っていたが、同じだった。

class Sample:
    def hoge(self):
        self.a = 'aaa'
    def fuga(self):
        self.a = 'bbb'
    self.hoge() # self.a = 'aaa'になる
    self.fuga() # self.a = 'bbb'に上書き

3.よくわからんエラー
"psql: FATAL: remaining connection slots are reserved for non-replication superuser connections”
解決できなかったorz

red hat4にnumpy, scipyインストール

前回はred hat4にyumを入れるだけで肝心のnumpyとscipyをインスト出来なかったので、再挑戦してみた。


前回numpyがインスト出来なかったときにでてきたエラーが、Connection Peerだったので、改めて実行するとすんなり葉行った。なのでここは特に記載なし。
次にnosetestをpipでいれて、scipyも入れるかーとコマンドを打つと

sudo pip install scipy

Import Error: libimf.so: cannot open shared object file: No such file or directory

このlibimf.soって何なん?って思ってぐぐると、インテルコンパイラ関係のライブラリなのかな?それでこのエラーの解決方法に従って.bashrcを書き換えて再度読み込んだが、変わらなかった。
さらに探していくと、このページを見つけ、従ってやってみたらうまく入ったので、コマンドを残しておく。

find /opt/intel -name libimf.so

/opt/intel/cc/10.1/lib/libimf.so # for 32bit
/opt/intel/cce/10.1/lib/libimf.so  # for 64bit
/opt/intel/fc/10.1/lib/libimf.so
/opt/intel/fce/10.1/lib/libimf.so
/opt/intel/cc/9.1/lib/libimf.so
/opt/intel/cce/9.1/lib/libimf.so
/opt/intel/fc/9.1/lib/libimf.so
/opt/intel/fce/9.1/lib/libimf.so
...

#red hatのバージョンが古いから取り敢えず古い方いれておこうということで、9.1を設定ファイルに書き込む
sudo vim /etc/ld.so.conf

/opt/intel/cce/9.1/lib ←追記

#ライブラリへのリンクを作成
sudo /sbin/ldconfig

として、再度実行すると

sudo pip install scipy

Import Error: libifport.so.5: cannot open shared object file: No such file or directory

#↑と同様のエラーのようなので、
find /opt/intel -name libifport.so.5
#で見つけたやつを設定ファイルに書き込む
sudo vim /etc/ld.so.conf

/opt/intel/fce/9.1/lib ←追記

これで再度実行すると、無事インストできました^^


追記
インテルCコンパイラを使用してソースからインストールする場合、9.1では動かず10.1を使う必要があった。/opt/intel/cce/10.1/bin/などにiccvars.shがあり、

source /opt/intel/10.1/bin/iccvars.sh

環境変数を読み込んでおくとソースからのインストールの際にコンパイラを見つけられるようになる。

RHEL4にyumをインストール

そもそもOSはRed Hatだがyumがないらしい。とりあえずバージョンが何なのかを知らないことには…と思い、ここを参考に

cat /etc/*-release

を実行すると、

Red Hat Enterprise Linux WS release 4

どうやらRHEL 4らしい。
…関係ないが、RHEL 4のサポート(厳密には違うんだろうが…)が2012.2で切れてた。どーすんだろ?

さて、せっかくならyum欲しいよね?ってことでインストールしてみた。
yumの公式サイト?を眺めて、取り敢えず2.9ならいいかな?ってことでこれをダウンロード、インストール

wget http://yum.baseurl.org/download/2.9/yum-2.9.8-1.src.rpm
sudo rpm -ivh yum-2.9.8-1.src.rpm
sudo rpmbuild --ba /usr/src/redhat/SPECS/yum.spec

ここまで実行したところで、

RPM build error:
    Fie not found by glob:/var/tmp/yum-2.9.8root/usr/lib/python?.?/site-packages/yum
    Fie not found by glob:/var/tmp/yum-2.9.8root/usr/lib/python?.?/site-packages/rpmUtils

というエラーが…少しぐぐったが分からずorz
別バージョンで試してみるか、ということで2.4.3をダウンロードして試したが同じエラー…
File not foundということでもしかしたら…と思い確認してみると、ファイルのパスが違ってた!
そしてふとsrc.rpmhoge.spec という設定ファイルが云々とか見たのを思い出し、yum.specを開いてみると、ありましたよ

%{_bindir}/yum-arch
/usr/lib/python?.?/site-packages/yum

これを↓のように書きなおして(localを追加)

%{_bindir}/yum-arch
/usr/local/lib/python?.?/site-packages/yum

再度実行すると、python-elementtree, python-sqlite, urlgrabberがないようだ。どうやら依存関係が解消されない模様。

wget http://vault.centos.org/4.5/os/x86_64/CentOS/PRMS/python-{elementtree-1.2.6-5.el4.centos.x86_64,sqlite-1.1.7-1.2.1.x86_64,urlgrabber-2.9.8-2.noarch}.rpm
sudo rpm -Uvh *.rpm

でダウンロードしてインストール。python-sqliteについてはsqlite3が入ってなかったのでまた依存関係で躓いた…

wget http://vault.centos.org/4.5/os/x86_64/CentOS/PRMS/sqlite-3.3.6-2.x86_64.rpm
sudo rmp -Uvh sqlite-3.3.6-2.x86_64.rpm

でダウンロードしてインストール。依存関係を解消したところで再度python-sqliteをいれて、

sudo rpmbuild --ba /usr/src/redhat/SPECS/yum.spec

を試してみると Wrote: hogehoge
とか出てきたのでうまく入ったようだ。

sudo rpm -ivh /usr/src/redhat/RPMS/noarch/yum-2.4.3-1.rpm

で、whereis yumでパスが通ってることを確認して無事インスト成功!やったね(∩´∀`)∩ワーイ

さて、それではscipyいれますか!と思った矢先にまた問題発生…

There was a problem importing one of the Python modules
required to run yum. The error leading to this problem was:
    No module named yum
...
It's possible that the above module doesn't match the current version of Python, which is:
2.3.4

yumってモジュールが見つからないとか…
同じようなエラーに遭遇して解決した方がいらっしゃったので、その方のブログを参考にやってみた。
実際にパスが通ってるpythonは2.7だったので、

sudo ln -s /usr/local/lib/python2.7/site-packages/yum /usr/local/lib/python2.3/site-packages/

これを実行して試すとまた同様のエラーが…取り敢えずエラーとして出てきたrpmUtilsとrepomdもリンクを貼って再度scipyをインストールしようとすると、

Setting up repositories
No Repositories Available to Set Up
Reading repository metadata in from local files

と出た!やっとエラーから解放されたようだ

さて、scipyのインストールはeasy_installが楽だったかな?…と思いきや、

sudo yum list python-setuptools
Setting up repositoies
No Repositories Available to Set Up
Reading repository metadata in from local files

…設定が必要なのね( ・´ω・`)
ってことで、JAISTリポジトリに設定してみた。

wget http://ftp.jaist.ac.jp/pub/Linux/Fedora/epel/RPM-GPG-KEY-EPEL-4
sudo rpm --import RPM-GPG-KEY-EPEL-4
sudo vi /etc/yum.repos.d/epel.repo

[epel]
name = Fedora epel repository at Jaist
baseurl = http://ftp.jaist.ac.jp/pub/Linux/Fedora/epel/4WS/$basearch/
gpgcheck = 1
enabled = 1

これで yum list python-setuptools が通った!

python-mecabで文字列をパースしてみる

環境はPython2.7, mecab0.98

def extract_keyword(string, word_class=['名詞']):
        tagger = MeCab.Tagger('mecabrc')
        nodes = tagger.parse(u'テスト文字列です')

のように文字列をパースしようとすると、下記のエラーが出た。

Traceback (most recent call last):
File "hoge.py", line 35, in <module>
extract_keyword(text)
File "hoge.py", line 16, in extract_keyword
nodes = tagger.parse(u'テスト文字列です #JAISTFES')
File "/usr/lib/python2.7/dist-packages/MeCab.py", line 220, in parse
def parse(self, *args): return _MeCab.Tagger_parse(self, *args)
TypeError: in method 'Tagger_parse', argument 2 of type 'char const *'

ちなみに、tagger.parseでもtaggaer.parseToNodeでも同様。

ユニコード文字を渡すとこのエラーが出るらしい。
バイト文字列だとうまくパースできた^^

行列の和をとってみる

こんな感じの行列があって、すべてのキーワードの出現回数の合計を出したい。

paragraph_id keyword1 keyword2 keyword3 keyword4
0 5 3 1 4
1 2 4 4 4
2 3 2 5 3

計算したい要素のみのリストを作っておく

matrix = [
           [5,3,1,4],
           [2,3,3,3],
           [3,2,5,3]
         ]

すぐ思いつくのはforで回す方法だけど、sum関数とmap関数とリスト内包表記で実現してみた。

sum([sum(map(int,row)) for row in matrix])

ファイルから読み込むときに、文字列で解釈される場合は↑のようにint関数でキャストする。
初めから整数のリストを持ってる時は、↓の方法でいけると思う。

sum([sum(row) for row in matrix])

まず内側のmap(int,row) (rowはmatrixの要素)で、rowの要素を整数型に変換し、整数の要素を持つリストを作成する。
sum(map(int,row)) は作成されたリストの要素の合計を算出する。
[sum(map(int,row)) for row in matrix] でmatrixのすべての要素で、要素の要素の合計を要素として持つリストを作成する(分かりづらい表現やな)
最後に、↑で作成したリストの要素の和を算出する。