5

現在、Pythonスクリプトに問題があり、特定の関数に対してcursor.execute()からSQL関数を実行すると、関数が実行され、関数が完了し、スクリプトが5時間近くハングしてから、残りの関数が再開されます。プログラムの。

Pythonから呼び出されたpgAdminIIIで関数のサーバーステータスを監視し、関数が完了すると、サーバーはpython-userへの接続が開いていることを示します。トランザクションでアイドル状態になる前ですが、conn.set_isolation_level(0)を追加した後は、開いている接続のみが表示されます。

また、cursor.execute()を囲むように印刷しましたが、次の印刷ステートメントに進むには5時間かかります。接続を切断して再起動しようとしましたが、役に立ちませんでした。スクリプトから関数呼び出しを完全に取り除いたので、スクリプトが処理した唯一のプロセスでした。何も機能していません。

難しさは、問題の原因として双方が他方を指し続けることです。私はそれがこの特定の機能からのある種の相互作用効果であると仮定しています。おそらく、特定の関数を実行した後、postgresからPythonにシグナルが返送されていませんか?他の関数には、同じ実行方法で問題が発生することはありません。

アイデア?ソリューション?-タイムアウトを検討していましたが、実行時間は20分から2時間までさまざまです。-実行する関数はテーブルを作成し、「完了」を示す文字列のみを返します。すべての操作は、サーバーステータスでアクティブな関数として表示されなくなった後、データベースで考慮されます。

これが私のメインスクリプトから分離された、問題を複製するために使用するコードです:

#!/usr/bin/env python

import sys
import os
import smtplib
import ftplib
import gzip
import logging
import shutil
import argparse
import traceback
import subprocess
from email.mime.text import MIMEText

import psycopg2
import psycopg2.extras
import psycopg2.extensions
from psycopg2 import OperationalError

pg_db    = "ppl"
pg_port  = "5432"
pg_user  = "<user>"
pg_pass  = "<pass>"
pg_host  = "<host>"

# connect to database
try:
    conn = psycopg2.connect(database=pg_db,
                            port=pg_port, 
                            user=pg_user, 
                            password=pg_pass, 
                            host=pg_host)
    conn.set_isolation_level(0)
except:
    print "Unable to connect to the database."

cur = conn.cursor()

print 'Starting select <function1>'
cur.execute("select <function1>(999999, null );")
print 'Assign variable...'
a = cur.fetchall()
print 'Done'

print 'Starting select <function2>'
cur.execute("select <function2>(999999, null );")
print 'Assign variable...'
b = cur.fetchall()
print 'Done'

print a
print b

conn.close()
sys.exit()

次に、これらの結果で示唆されているように、straceを実行しました。

<previous function statements for about 4k lines>

write(1, "Starting select <function>"..., 45) = 45
sendto(3, "Q\0\0\0009select <function>"..., 58, MSG_NOSIGNAL, NULL, 0) = 58
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "T\0\0\0001\0\1<function_name>\0"..., 16384, 0, NULL, NULL) = 500
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0mSINFO\0C00000\0Mv_eval_table "..., 16384, 0, NULL, NULL) = 110
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\4\247SINFO\0C00000\0Mcmd := CREATE"..., 16384, 0, NULL, NULL) = 1192
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\23hSINFO\0C00000\0Mcmd := \t--For"..., 16384, 0, NULL, NULL) = 4969
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 84
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 672
...
brk(0xbe9000)                           = 0xbe9000
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 84
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 168
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 1008
...

<does this for about 600k more lines before executing the rest of the script, I am assuming it is indefinitely looping until a timeout>

次に、2番目の関数を呼び出すコードの2番目のブロックをラップする関数に対してcProfileを実行しました。

Tue Jan 15 16:04:50 2013    function2_prof

         7 function calls in 15755.988 CPU seconds

   Random listing order was used

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {method 'cursor' of 'psycopg2._psycopg.connection' objects}
        1    0.000    0.000 15755.988 15755.988 test_sql_sub2.py:48(LR2)
        1 15755.988 15755.988 15755.988 15755.988 {method 'execute' of 'psycopg2._psycopg.cursor' objects}
        1    0.000    0.000 15755.988 15755.988 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'close' of 'psycopg2._psycopg.cursor' objects}
        1    0.000    0.000    0.000    0.000 {method 'fetchall' of 'psycopg2._psycopg.cursor' objects}
4

2 に答える 2

1

この問題は、SQL 関数の複数の for ループ内の単一行が原因でした。

raise info 'license_row_id := %', rec.license_row_id;

これらの昇給情報メッセージは、bash と pgAdmin で処理されたように、python で適切に処理されませんでした。私の推測では、それらは処理されなかったので、処理できなくなるまでループと処理を繰り返し、追加のメモリを要求し、最終的に警告やエラーなしで昇給情報を削除し、プログラムを再開したのです。

この不必要なレイズ情報行をコメントアウトすると、スクリプト全体が 20 分で実行されました。これは、プログラムの通常の実行時間です。Python ハンドルを作成して postgresql からの情報メッセージを生成する方法を誰かが知っている場合、おそらくこの問題にさらに答えるでしょう。

于 2013-01-16T21:04:06.743 に答える
1

あなたの機能は、あなたが思っている以上のことをしていると思います。あなたのstraceを見てください:

brk(0xbe9000)                           = 0xbe9000
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 84
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 168
poll([{fd=3, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "N\0\0\0SSINFO\0C00000\0Mlicense_row_i"..., 16384, 0, NULL, NULL) = 1008

brkは「メモリを増やしてください」という意味なので、明らかに Python がデータを蓄積しています。 poll待機中のデータがあるかどうかを確認します。正の戻り値 (ここでは1) は存在することを意味します。そしてrecvfrom、そのデータを読み取り、読み取ったバイト数を返します。

つまり、Postgres が大量のデータを送信しているようで、受信にかなりの時間がかかっています。Postgres のワイヤ形式についてはよくわかりませんが、license_row_i...すべてのポーリングで、大量の行が返されているように聞こえます。関数は実行中ではないため、実行中として表示されなくなりました。作業は完了し、Postgres は結果を送り返すだけです。

双方が他方を非難しているのも不思議ではありません。問題は真ん中。:)


編集:あはは、より多くの情報が含まれていRAISE INFOます. 実際、Psycopg2 はこれらすべての通知を接続オブジェクトに保存します。

最後の 50 件しか保持しませんが、クライアントに50 万件以上の通知を送信している場合、それらを Python 文字列に変換し、古いものを破棄し、最新のものを追加するなどの処理に時間がかかります。 -line client はそれらを完全に無視します (または、これをすべて C で実行するだけでも)、当然、はるかに高速になります。

したがって、簡単な解決策は「それをしない」ことです。:)これらをクライアントに送信しないように Postgres を構成することもできますが、もちろん、デバッグには役に立たなくなります。クライアントに送信された通知を収集しないように psycopg2 に依頼する方法はありません。

于 2013-01-16T01:26:12.837 に答える