postgres数据库错误强制事务重新启动

问题描述:

TL; DR:在AR :: Base保存事务中插入重复的连接表记录失败(由于唯一约束)导致保存失败并回滚。不添加重复连接表记录是好的。不保存是不好的。postgres数据库错误强制事务重新启动


我迁移MySQL的应用Postgres的...我曾经遵循一个模式像这样在MySQL土地加入表记录添加到DB:

class EventsSeries < ActiveRecord::Base 
    # UNIQUE KEY `index_events_series_on_event_id_and_series_id` (`event_id`,`series_id`) 
    belongs_to :event 
    belongs_to :series 
end 

class Series < ActiveRecord::Base 

    has_many :events_series 
    before_validation :add_new_event 

private 

    def add_new_event 
    # boils down to something like this 
    EventSeries.new.tap do |es| 
     es.event_id = 1 
     es.series_id = 1 
     begin 
     es.save! 
     rescue ActiveRecord::RecordNotUnique 
     # Great it exists 
     # this isn't really a problem 
     # please move on 
     end 
    end 
    end 
end 

调用像这样:

Series.first.save 
# should not blow up on duplicate join record, cause i don't care 

但是,postgres爆炸了。这里有一个很好的解释:

http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

...在“异常处理和回滚”部分(见警告)

基本上#save启动一个事务,并重复记录插入的原因一个数据库异常,它使#save的事务无效,这是sadface。

这是否有更好的模式可以在postgres-land中使用?

谢谢!


编辑:

我坚信这是有道理的,以保持这个逻辑里面系列节约交易...的模式如下:

s = Series.new 
s.new_event_id = 123 # this is just an attr_accessor 
s.save # callbacks on Series know how to add the new event. 

...它使我的控制器超小。

+0

SELECT ... FOR SHARE出现在脑海中,并检查它是否已经存在。我对可能重复的数据所做的事情是简单地使每个INSERT成为一个单独的事务。 – freeone3000 2012-04-24 23:45:31

+0

没错,但是那么我的所有逻辑都不能很好地包装在保存调用中 – jsharpe 2012-04-24 23:55:57

+0

ActiveRecord是否允许您以任何方式访问PostgreSQL的子事务 - 比如保存点? http://www.postgresql.org/docs/current/interactive/sql-savepoint.html ...或plpgsql中的EXCEPTION子句? http://www.postgresql.org/docs/current/interactive/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING – kgrittn 2012-04-25 02:22:36

如果您在一个事务中并且从错误中恢复并避免使整个事务失效,则必须使用保存点。

当您使用命令SAVEPOINT一些标签,你可以在以后运行命令ROLLBACK TO SAVEPOINT一些标签回到那个状态事务而忽略了保存点拍摄后的所有操作(包括错误)。

请在Continuing a transaction after primary key violation error看到我的其他答案更多的解释。

+0

真棒。你是先生,是英雄。对于其他人:绝对看到上面的链接在这个答案。 – jsharpe 2012-06-22 19:47:34