SlideShare a Scribd company logo
PL/Pythonで2層アプリを作ってみた
PostgreSQLカンファレンス2015/05
アシストセミナールーム
who am i
名前:山田 聡
会社:アシスト
仕事:ポスグレとオラクルのサポートしてます
年齢:社会人5年目(ポスグレ4年生)
注意
初心者がやってみた系です
若干ギャグなので生暖かい目で見てください
9.5 について知りたい方は
今直ぐ
←
Long Time Ago..
クラサバ
クライアントとデータベースが直
ビジネスロジックはクライアント側中心
graph LR id1(クライアント
ロジック)-->id2(データベース) style id1 fill:#955,stroke:#111,stroke-
width:4px; style id2 fill:#559,stroke:#f66,stroke-width:2px;
3層アプリケーション
間にWEB/APサーバ増えた
クライアント側はブラウザ
ビジネスロジックはWEB/APサーバ
graph LR id1(クライアント:
ブラウザ)-->id2(WEBサーバ:
ロジック) id2-->id3(DB) style id1 fill:#955,stroke:#111,stroke-
width:4px; style id2 fill:#555,stroke:#111,stroke-width:4px; style id3
fill:#559,stroke:#f66,stroke-width:2px;
でも...
管理とか
負荷とか
速度とか
そうだ2層に戻ろう
できあがったもの
PG2LAYER
201505 PostgreSQLアンカンファレンス(PL/Pythonで作るWEBアプリ)
PG2LAYER
PostgreSQL Two Layer Management System
管理用のダッシュボード
クエリ結果をJSONで戻すAPIを提供
普通のWEBアプリに見える何か
構造
PostgreSQLに子プロセスで直接リスニングさせる
PL/Pythonを使用
Pythonの を利用bottoleフレームワーク
インデント原理主義
2系と3系があって過渡期
機械学習とか統計学で最近よく見る
def say_hello():
    for i in range(1,100):
        print "HELLO No %s!" % i
PL/Python
create extensionで追加
PythonをPostgreSQLで使える
plpyモジュールでSQLもあつかえる
プレースホルダーを使って実行す
る場合
CREATE FUNCTION pystrip(x text)
  RETURNS text
AS $$
target_query = "select empno from emp where ename=$1"
plan = plpy.prepare(target_query,["text"])
r_set = plpy.execute(plan, [ "SMITH" ])
return r_set[0]["empno"]
$$ LANGUAGE plpythonu;
直接実行する場合
CREATE FUNCTION pl_py_test(x text)
  RETURNS text
AS $$
r_set = plpy.execute("select empno from emp where ename='SMITH'")
return r_set[0]["empno"]
$$ LANGUAGE plpythonu;
PythonのWEBフレームワーク
1ファイルに全てが含まれる
最軽量
PG2LAYER 構成
でも2層構造
graph LR id1(クライアント:
ブラウザ)-->id2(DBサーバ:
ロジック) style id1 fill:#955,stroke:#111,stroke-width:4px; style id2
fill:#555,stroke:#111,stroke-width:4px;
低依存性 (bottole.py /DBLINK / ドライバ不要)
低レイヤー (ブラウザ <-> データベース)
低管理コスト (DBのバックアップ = APのバックアップ, htmlファイル
レス)
起動
使用ポートを引数にしてファンクションを起動するだけ
pg2layer_db=# select start_bottole_httpd(1192);
ソース
CREATE OR REPLACE FUNCTION start_bottole_httpd(v_port integer) RETURNS text AS
$$
"""
PG2LAYER ­­ PostgreSQL Two Layer Management System
* Low dependency ( only bottole.py , only dblink)
* Low layer (browser <­> DB, never AppServer or HTTP Server)
* Low manage cost ( backup database backup the Web App)
It's Joke Web App.
2015/05 sayamada
"""
import sys
import os
import json
import signal
# $PGDATA配下に配置したbottole.pyをPYTHONPATHに追加しないといけないので
sys.path.append(os.getcwd()+"/PL_Python_Httpd")
from bottle import route, run, template, response, request, get, post, redirect
# for DBLINK
DB_NAME = "pg2layer_db"
DB_USER = "sayamada"
DB_HOST = "localhost"
api(select_list,table_name,where_col=None,col=None,col_val=None):
URLからクエリを生成しJSONのレスポンスを戻す
static(content_type,file_name):
静的なソース(css/js等)を戻す
静的ファイルは表データとして格納
get_tmplt(tmplt_name=None):
bottoleのテンプレートファイルを戻す
ほぼHTML
表データとして格納
do_query_over_dblink(v_query_string):
DBLIK経由でSQL処理しJSONで戻す(後述)
edit():
POSTリクエストに基づきテンプレートを更新
構想1時間
作成1日(のはずが1週間)
はまったところ
(きっと自分だけ)
問題その1
ImportError: No module named bottole
カレントに配置したbottole.pyが読み込めない
普通はカレントディレクトリは指定しなくてもいい
PostgreSQLの子プロセスは$PGDATAがカレント
pg2layer_db=# CREATE OR REPLACE FUNCTION test() RETURNS text AS
pg2layer_db­# $$
pg2layer_db$# 
pg2layer_db$# import bottole
pg2layer_db$# $$
pg2layer_db­# LANGUAGE 'plpythonu' VOLATILE;
CREATE FUNCTION
pg2layer_db=# 
pg2layer_db=# select test();
ERROR:  ImportError: No module named bottole 
CONTEXT:  Traceback (most recent call last):
  PL/Python function "test", line 3, in <module>
    import bottole
PL/Python function "test"
原因
PYTHONPATHに$PGDATAが含まれていない
javaのCLASSPATHとかLD_LIBRARY_PATH的なもの
ライブラリを読み込むディレクトリ
対処
AP内で動的に$PGDATA配下をPYTHONPATH
に追加
import sys
# $PGDATA配下に配置したbottole.pyをPYTHONPATHに追加
sys.path.append(os.getcwd()+"/PL_Python_Httpd")
問題その2
トランザクションがCommitできない
HTMLソース編集機能を追加
フォームからPOSTしたら反映はされる
PG2LAYERが異常終了したら変更データが戻る(!?)
PL/Pythonでトランザクション管理
をしてもだめだった(というかcommitないし)
43.8. 明示的サブトランザクション
原因
呼び出し元のstart_bottole_httpdファンクションが終了しないから
行った変更はstart_bottole_httpdを正常停止させない限りrollback
対処
DBLINKで自律型トランザクション
DBLINKでループバックして自律型トランザクションで対応
    base_query = "SELECT * FROM dblink('host=%s port=%s dbname=%s user=%s',%s) AS t(r text)"
    target_query = "update pg_2_template set src=%s where file_name=%s returning file_name" % (
                                                plpy.quote_literal(edit_src),
                                                plpy.quote_literal(edit_file_name)
                                            )
    last_query = base_query % (
        DB_HOST,
        DB_PORT,
        DB_NAME,
        DB_USER,
        plpy.quote_literal(target_query)
    )
    r_set = plpy.execute(last_query)
問題3
稼動統計が意図せず読み取り一貫性を発揮
api()関数で発生
pg_stat_activity等がstart_bottole_httpd起動時点の結果しかとれない
repetable read 的な挙動
普通の表はちゃんとread commited
原因
不明(多分問題2と同じ?)
対処
DBLINK
稼動統計系だけ分けるもの面倒なのでAPIは全部DBLINK経由に変更
問題4
DBLINK経由のクエリで列リストが不定
APIでは列リストをURLで指定する実装
url query
/api/*/hoge -> select * from hoge
/api/col1,col2/hoge -> select col1,col2 from hoge
DBLINKは戻り値のデータ型を明示しないといけない
対処
そうだJSONにしよう
元クエリをjson_aggでラップ
戻り値はかならずJSON
dblink('dbname=pg_2_layer', 'select json_agg(t) from (元クエリ) t')
AS t(result json)
# DBLINKで自律型トランザクションとする
# 型にしばられないため、json_aggでラップしている
# 戻りは全部JSON
def do_query_over_dblink(v_query_string):
    # DBLINKの大枠
    base_query = "SELECT * FROM dblink('host=%s port=%s dbname=%s user=%s', %s) AS t(result json)
    last_query = base_query % (
        DB_HOST,
        DB_PORT,
        DB_NAME,
        DB_USER,
        plpy.quote_literal("select json_agg(t) from (" + v_query_string+ ") t") # クエリも引数なので
    )
    plpy.log( last_query)
    r_set = plpy.execute(last_query)
    plpy.log(r_set)
    # jsonで戻しても取得時はstrになってたのでstrとして統一
    result_json_str = "[]"
    if r_set[0]["result"] is not None:
        result_json_str = r_set[0]["result"]
    # 利用側の利便性を考えてjsonで戻す
元のクエリを
select * from emp
json_aggでラップして
select json_agg(t) from (
    select * from emp
) t
dblinkでラップする
select * from dblink('
    select json_agg(t) json from (
        select * from emp
    ) t'
) AS t(result json)
こんだけラップで何がパフォーマ
ンスか
問題5
停止できない
start_bottole_httpdが止められない
ctrl+c/pg_terminate_backendできず
ctrl+c
pg2layer_db=# select start_bottole_httpd(1192);
^CCancel request sent
ctrl+c
pg2layer_db=# select start_bottole_httpd(1192);
^CCancel request sent
^CCancel request sent
ctrl+c
pg2layer_db=# select start_bottole_httpd(1192);
^CCancel request sent
^CCancel request sent
^CCancel request sent
ctrl+c
pg2layer_db=# select start_bottole_httpd(1192);
^CCancel request sent
^CCancel request sent
^CCancel request sent
^CCancel request sent
落ちないorz
原因
実行中はpostgresqlのコンテキストではなくPythonのコンテキスト
シグナルハンドラがPythonコンテキストで動作してない?
対処
自分でシグナルハンドラ書く
PythonコンテキストでSIGINTを処理するように
受け取ったら落ちる
def signal_handler(num, frame):
    plpy.log("SIGINT_restart")
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
run(host='0.0.0.0', port=v_port)
いないと思いますが
PL/Pythonを使ったWEBアプリを作成されようと思っている方の
一助になれば幸いです。
終わり

More Related Content

Similar to 201505 PostgreSQLアンカンファレンス(PL/Pythonで作るWEBアプリ) (20)

160705-03 RTミドルウエア講習会・名城大
160705-03 RTミドルウエア講習会・名城大
openrtm
 
Example using LattePanda
Example using LattePanda
Hirokazu Egashira
 
Getting Started with Graph Database with Python
Getting Started with Graph Database with Python
ロフト くん
 
これからのアプリ開発はIPv6対応で行こう!(2014/09/20 OSC Hiroshima版)
これからのアプリ開発はIPv6対応で行こう!(2014/09/20 OSC Hiroshima版)
v6app
 
Scala + Finagleの魅力
Scala + Finagleの魅力
Kota Mizushima
 
SPA×Auth0
SPA×Auth0
春奈 岡
 
Reco choku tech night #09 -reinvent2018報告会-
Reco choku tech night #09 -reinvent2018報告会-
recotech
 
Apache spark 2.3 and beyond
Apache spark 2.3 and beyond
NTT DATA Technology & Innovation
 
SAIS/SIGMOD参加報告 in SAIS/DWS2018報告会@Yahoo! JAPAN
SAIS/SIGMOD参加報告 in SAIS/DWS2018報告会@Yahoo! JAPAN
Yahoo!デベロッパーネットワーク
 
東海GTUG 20110910発表資料
東海GTUG 20110910発表資料
Kenji NAKAGAKI
 
Big data解析ビジネス
Big data解析ビジネス
Mie Mori
 
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
Shinya Takamaeda-Y
 
Before Study, Gifu University 2016
Before Study, Gifu University 2016
Kiyoshi Ogawa
 
JAWS-DAYS 2015 / 北海道 x 農業 x クラウド
JAWS-DAYS 2015 / 北海道 x 農業 x クラウド
Takehito Tanabe
 
SLOのすすめ
SLOのすすめ
Takeo Sawada
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
Akihiro Kuwano
 
GraphQLはどんな時に使うか
GraphQLはどんな時に使うか
Yutaka Tachibana
 
ビッグデータ活用支援フォーラム
ビッグデータ活用支援フォーラム
Recruit Technologies
 
Java Clientで入門する Apache Kafka #jjug_ccc #ccc_e2
Java Clientで入門する Apache Kafka #jjug_ccc #ccc_e2
Yahoo!デベロッパーネットワーク
 
月間 250 億 imps 配信するために fluct が考えていること!
月間 250 億 imps 配信するために fluct が考えていること!
MasamichiIdeue
 
160705-03 RTミドルウエア講習会・名城大
160705-03 RTミドルウエア講習会・名城大
openrtm
 
Getting Started with Graph Database with Python
Getting Started with Graph Database with Python
ロフト くん
 
これからのアプリ開発はIPv6対応で行こう!(2014/09/20 OSC Hiroshima版)
これからのアプリ開発はIPv6対応で行こう!(2014/09/20 OSC Hiroshima版)
v6app
 
Scala + Finagleの魅力
Scala + Finagleの魅力
Kota Mizushima
 
Reco choku tech night #09 -reinvent2018報告会-
Reco choku tech night #09 -reinvent2018報告会-
recotech
 
東海GTUG 20110910発表資料
東海GTUG 20110910発表資料
Kenji NAKAGAKI
 
Big data解析ビジネス
Big data解析ビジネス
Mie Mori
 
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
Shinya Takamaeda-Y
 
Before Study, Gifu University 2016
Before Study, Gifu University 2016
Kiyoshi Ogawa
 
JAWS-DAYS 2015 / 北海道 x 農業 x クラウド
JAWS-DAYS 2015 / 北海道 x 農業 x クラウド
Takehito Tanabe
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
Akihiro Kuwano
 
GraphQLはどんな時に使うか
GraphQLはどんな時に使うか
Yutaka Tachibana
 
ビッグデータ活用支援フォーラム
ビッグデータ活用支援フォーラム
Recruit Technologies
 
月間 250 億 imps 配信するために fluct が考えていること!
月間 250 億 imps 配信するために fluct が考えていること!
MasamichiIdeue
 

More from Satoshi Yamada (15)

Pythonで業務改善をしたときにあった問題(ライト版)
Pythonで業務改善をしたときにあった問題(ライト版)
Satoshi Yamada
 
pythonでemlファイルを扱う話
pythonでemlファイルを扱う話
Satoshi Yamada
 
PostgreSQLとPythonとSQL
PostgreSQLとPythonとSQL
Satoshi Yamada
 
bottle.pyをつかったチャットアプリ作成チュートリアル
bottle.pyをつかったチャットアプリ作成チュートリアル
Satoshi Yamada
 
bottleで始めるWEBアプリの最初の一歩
bottleで始めるWEBアプリの最初の一歩
Satoshi Yamada
 
Requestsで始める5分前帰社
Requestsで始める5分前帰社
Satoshi Yamada
 
DBエンジニアに必要だったPythonのスキル
DBエンジニアに必要だったPythonのスキル
Satoshi Yamada
 
本気でPythonで宛名書きした話
本気でPythonで宛名書きした話
Satoshi Yamada
 
10080分でPythonからIP Messeneger
10080分でPythonからIP Messeneger
Satoshi Yamada
 
15分で情シスに怒られる方法
15分で情シスに怒られる方法
Satoshi Yamada
 
Djangoで業務改善したい
Djangoで業務改善したい
Satoshi Yamada
 
PostgreSQL実行計画入門@関西PostgreSQL勉強会
PostgreSQL実行計画入門@関西PostgreSQL勉強会
Satoshi Yamada
 
PythonでテキストをJSONにした話(PyCon mini sapporo 2015)
PythonでテキストをJSONにした話(PyCon mini sapporo 2015)
Satoshi Yamada
 
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
Satoshi Yamada
 
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
Satoshi Yamada
 
Pythonで業務改善をしたときにあった問題(ライト版)
Pythonで業務改善をしたときにあった問題(ライト版)
Satoshi Yamada
 
pythonでemlファイルを扱う話
pythonでemlファイルを扱う話
Satoshi Yamada
 
PostgreSQLとPythonとSQL
PostgreSQLとPythonとSQL
Satoshi Yamada
 
bottle.pyをつかったチャットアプリ作成チュートリアル
bottle.pyをつかったチャットアプリ作成チュートリアル
Satoshi Yamada
 
bottleで始めるWEBアプリの最初の一歩
bottleで始めるWEBアプリの最初の一歩
Satoshi Yamada
 
Requestsで始める5分前帰社
Requestsで始める5分前帰社
Satoshi Yamada
 
DBエンジニアに必要だったPythonのスキル
DBエンジニアに必要だったPythonのスキル
Satoshi Yamada
 
本気でPythonで宛名書きした話
本気でPythonで宛名書きした話
Satoshi Yamada
 
10080分でPythonからIP Messeneger
10080分でPythonからIP Messeneger
Satoshi Yamada
 
15分で情シスに怒られる方法
15分で情シスに怒られる方法
Satoshi Yamada
 
Djangoで業務改善したい
Djangoで業務改善したい
Satoshi Yamada
 
PostgreSQL実行計画入門@関西PostgreSQL勉強会
PostgreSQL実行計画入門@関西PostgreSQL勉強会
Satoshi Yamada
 
PythonでテキストをJSONにした話(PyCon mini sapporo 2015)
PythonでテキストをJSONにした話(PyCon mini sapporo 2015)
Satoshi Yamada
 
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
Satoshi Yamada
 
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
Satoshi Yamada
 
Ad

201505 PostgreSQLアンカンファレンス(PL/Pythonで作るWEBアプリ)