#はじめに
初投稿ですw
観測した現象から推測して知見としてまとめてますので、違ってる可能性はありまーす。ご利用は自己責任でお願いします。もっと詳しいこと知ってたら、教えてエロい人
#発生原因と対処法
このエラーは以下のルールに違反するので発生する
http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_serial_isolation.html
以下のトランザクションは
同時に実行しても直列化可能なので問題ない。
begin transaction;
delete from target
using stage
where target.primarykey = stage.primarykey;
insert into target
select * from stage;
end transaction;
しかし以下のようにテーブルへのselectなど、
何かしらの処理を行った場合は
ふたつ目のトランザクションで
Serializable isolation violationが発生し、
ロールバックしてしまった。
begin transaction;
select * from hoge limit 1;
delete from target
using stage
where target.primarykey = stage.primarykey;
insert into target
select * from stage;
end transaction;
恐らく処理を行う際に
データベースのスナップショットが作成され、
ひとつ目のtransactionのcommit後と差異が発生、
redshiftは直列化可能ではないと認識し
ロールバックするものと思われる。
http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_serial_isolation.html
から引用
同時トランザクションは互いに認識されません。互いの変更を検出できません。各同時トランザクションにより、トランザクションの始めにデータベースのスナップショットが作成されます。データベーススナップショットは、ほとんどの SELECT ステートメント、COPY、DELETE、INSERT、UPDATE、TRUNCATE などの DML コマンド、次の DDL コマンドの最初の発生時にトランザクション内で作成されます。
直列化可能に見えるが、redshiftちゃん的には
スナップショットから判断してNGらしい。。厳しい。。
なお
select * from hoge limit 1;
ではなく
select 1;
だと
データベースのスナップショットが作成されないようで
エラーは発生しない。
またtempテーブルは同じ名前でも処理ごとに別物なので
selectして更新しようが何しようが直列化可能なのでエラーは発生しない
#systemコマンドでsqlを流した場合とActiveRecordでsqlを流した場合のトランザクションの違い
以下のsqlをファイルに書き込んで、systemコマンド(psql ~ < sqlのファイル)で流した場合はトランザクションは全て別のものになる
select * from hoge limit 1;
select * from hoge limit 2;
begin transaction;
select * from hoge limit 3;
end transaction;
select * from hoge limit 4;
begin transaction;
select * from hoge limit 5;
end transaction;
SQL
しかし以下のSQLをActiveRecordで流した場合
sql = <<SQL
select * from hoge limit 1;
select * from hoge limit 2;
begin transaction;
select * from hoge limit 3;
end transaction;
select * from hoge limit 4;
begin transaction;
select * from hoge limit 5;
end transaction;
SQL
ActiveRecord::Base.connection.execute(sql)
トランザクションは以下のようになる
トランザクション1
select * from hoge limit 1;
select * from hoge limit 2;
select * from hoge limit 3;
トランザクション2
select * from hoge limit 4;
select * from hoge limit 5;
ActiveRecordの場合、begin transactionがない場合は勝手にbegin transactionを付加しているような挙動をするということである。(ActiveRecordのどこで行っているのか現在確認中 Adapter?)
ActiveRecordでRedshiftに接続して使う場合は、この辺りを理解していないとSerializable isolation violationのワナにかなりハマりやすくなる
#おまけ
最近ロールバックしちゃったトランザクションを確認できるSQL
select stl.*,sv.table
from stl_tr_conflict stl
left join SVV_TABLE_INFO sv ON stl.table_id = sv.table_id
order by abort_time desc limit 100;
特定のクエリのトランザクションID(xid)を確認
select starttime,xid, querytxt from stl_query
where querytxt LIKE '%select * from hoge limit 4%'
order by starttime desc limit 1;
特定のトランザクションのクエリを確認
select starttime,querytxt from stl_query
where xid = 5864985 order by starttime;