3

次の表があります。

class Feedback(Base):
  __tablename__ = 'feedbacks'
  __table_args__ = (UniqueConstraint('user_id', 'look_id'),)
  id = Column(Integer, primary_key=True)
  user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
  look_id = Column(Integer, ForeignKey('looks.id'), nullable=False)

現在、UniqueConstraint に違反する多くのエントリをこのテーブルに挿入しています。

次のコードを使用しています。

  for comment in session.query(Comment).filter(Comment.type == Comment.TYPE_LOOK).yield_per(100):
    feedback = Feedback()
    feedback.user_id = User.get_or_create(comment.model_id).id
    feedback.look_id = comment.commentable_id
    session.add(feedback)
    try:        # Refer to T20
      session.flush()
    except IntegrityError,e:
      print "IntegrityError", e
      session.rollback()
  session.commit()

次のエラーが表示されます。

IntegrityError (IntegrityError) duplicate key value violates unique constraint "feedbacks_user_id_look_id_key"
DETAIL:  Key (user_id, look_id)=(140, 263008) already exists.
 'INSERT INTO feedbacks (user_id, look_id, score) VALUES (%(user_id)s, %(look_id)s, %(score)s) RETURNING feedbacks.id' {'user_id': 140, 'score': 1, 'look_id': 263008}
IntegrityError (IntegrityError) duplicate key value violates unique constraint "feedbacks_user_id_look_id_key"
...
(there's about 24 of these integrity errors here)
...
DETAIL:  Key (user_id, look_id)=(173, 263008) already exists.
 'INSERT INTO feedbacks (user_id, look_id, score) VALUES (%(user_id)s, %(look_id)s, %(score)s) RETURNING feedbacks.id' {'user_id': 173, 'score': 1, 'look_id': 263008}
No handlers could be found for logger "sqlalchemy.pool.QueuePool"
Traceback (most recent call last):
  File "load.py", line 40, in <module>
    load_crawl_data_into_feedback()
  File "load.py", line 21, in load_crawl_data_into_feedback
    for comment in session.query(Comment).filter(Comment.type == Comment.TYPE_LOOK).yield_per(100):
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2337, in instances
    fetch = cursor.fetchmany(self._yield_per)
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 3230, in fetchmany
    self.cursor, self.context)
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 3223, in fetchmany
    l = self.process_rows(self._fetchmany_impl(size))
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 3343, in _fetchmany_impl
    row = self._fetchone_impl()
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 3333, in _fetchone_impl
    self.__buffer_rows()
  File "/Volumes/Data2/Dropbox/projects/Giordano/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 3326, in __buffer_rows
    self.__rowbuffer = collections.deque(self.cursor.fetchmany(size))
sqlalchemy.exc.ProgrammingError: (ProgrammingError) named cursor isn't valid anymore None None

このエラーが yield_per によって引き起こされているという結論に飛びつく前に、yield_per がここでの原因ではないことを保証できます。

一意の制約なしで同じコードを試してみましたが、エラーはまったく発生しませんでした。

整合性エラーがNo handlers could be found for logger "sqlalchemy.pool.QueuePool" を引き起こしていると思います。

すべての整合性エラーがキュープール内の各「スレッド」を強制終了していると思います。

誰かが何が起こっているかについて私を啓発できますか?

この時点でデータについてあまりできない場合、何をするように勧められますか?

4

2 に答える 2

5

そのエラーは Pythonloggingモジュールからのものです。プール クラスがデバッグ メッセージをログに記録しようとしていますが、SQLA ログが構成されていません。 ロギングの構成は簡単で、実際に何を言おうとしているのかを確認できます。

ここで何が起こっているのかよくわかりませんが、トップレベルのトランザクションを何十回もロールバックしていることは確かに役に立ちません。ロールバックはトランザクションを終了し、すべてのライブ行オブジェクトを無効にします。それは確かに とうまく相互作用しませんyield_per

データベースがセーブポイントまたはネストされたトランザクションをサポートしている場合 (つまり、Postgres か Oracle... または最近の MySQL ですか?)、試行ごとにネストされたトランザクションを開始してみてください。

for comment in session.query(Comment).filter(Comment.type == Comment.TYPE_LOOK).yield_per(100):
    try:
        with session.begin_nested():
            feedback = Feedback()
            feedback.user_id = User.get_or_create(comment.model_id).id
            feedback.look_id = comment.commentable_id
            session.add(feedback)
            session.flush()
    except IntegrityError, e:
        print "IntegrityError", e

session.commit()

withエラーが発生するとロールバックし、成功するとコミットするため、失敗しflushてもメイン トランザクションの残りの部分が混乱することはありません。

バックエンドのサポートがない場合、他に考えられる賢明なオプションは次のとおりです。

  • クエリを複雑にLEFT JOINします。フィードバック テーブルを使用して、フィードバック行が既に存在するかどうかをアプリ内で把握できるようにします。

  • を主キーにしたい場合は(user_id, look_id)、 を使用できると思いますsession.merge(feedback)。これは、主キーに基づく挿入または更新のように機能します。SQLA が同じ pk を持つ既存の行を見つけることができる場合、それを更新します。そうでない場合、データベースに新しい行を作成します。SELECTただし、新しい行ごとに余分に発射するリスクがあります。

于 2013-01-18T17:39:23.173 に答える
4

「このエラーがyield_perによって引き起こされているという結論にジャンプする前に、yield_perがここでの原因ではないことを保証できます。」

ここで--yield_per()が非常に重要であると考える理由はわかりません。これは、yield_per()を使用せずに同じテストを試して、動作が異なるかどうかを確認するだけですぐに理解できます。yield_per()を使用することにより、そのループが続く間、psycopg2カーソルは開いたままになります。しかし、その後、を介してpsycopg2接続でロールバックを発行していますsession.rollback()。これにより、「名前付きカーソルはもう有効ではありません」などのエラーが発生します。実際、名前付きカーソルがある唯一の理由、yield_per()が有効にするものの一部であるpsycopg2を使用してサーバー側カーソルを実行する方法だからです。

「固有の制約なしで同じコードを試しましたが、エラーはまったく発生しませんでした。」

これは、制約がない場合、例外がスローされず、rollback()がヒットしないためです。

于 2013-01-18T17:45:38.740 に答える