MySQLをデフォルトの分離レベル(REPEATABLE-READ)で実行しています。重複キーエラーの挿入後にSELECTが失敗する
私のPythonコードは、2つの同時プロセス内で同時に実行できるトランザクションを管理するためにsqlalchemyを使用しています。
c.begin();
# this SELECT establishes the transaction snapshot from which all other SELECTs will read data
c.execute("SELECT something FROM sometable;")
try:
c.execute("INSERT INTO othertable (unique_key) VALUES (1)")
except sqlalchemy.exc.IntegrityError as e:
code, msg = e.orig
if code != 1062:
raise
# duplicate key: another transaction commited the above INSERT so I can't rely on LAST_INSERT_ID
rows = c.execute("SELECT * FROM table WHERE unique_key=1")
inserted_id = None
for id, in rows:
inserted_id = id
break
assert inserted_id is not None
else:
inserted_id = c.last_insert_id()
c.commit()
正確なコードは明らかに少し複雑より多くのクエリであるが、問題の核心は、このコードはかなり頻繁に例外ハンドラでアサートに当たるということです。
理由は、このトランザクションがさまざまなプロセスで同時に実行されることが多いためです。最初のSELECTで確立されたDBスナップショットに新しく挿入された行が含まれていないため、重複キー例外でトリガーされるSELECTが失敗します最初のSELECTの後でINSERTの前に別のトランザクションによって挿入されました)。
私は自分のSELECTをFOR UPDATEを使用するように変更でき、挿入された実際の値を読み取るためにスナップショットに穴をあけることを理解しました。
しかし、私はこれをすることを躊躇しています。なぜなら、これはかなりの場所でこれを振りかけることになり、これは比較的壊れやすいと私は見ています。
これらのコンクレインインサートを処理するには、他のより堅牢で標準的で控えめではない方法があります(これは私が処理しなければならない事実です)。