#Rails + #MySQL / begin rescue end と Transaction を外側・内側に置いた動作の違いを確認してみたコード断片ですが

- Transactionの中で エラーが発生した場合、後続の処理を続けずに ROLLBACK させ、なおかつエラーをハンドリングして特定の処理をおこないたい
- Transaction で begin rescue end で囲ってしまうと、特定のエラーを rescue するときに、ROLLBACK を発生させるための例外も起こらなくなってしまい、やりたいことが出来ない
- Transaction を内側に、begin rescue end を外側に書いてみる。こうすることで ROLLBACK が起こった後に 例外をキャッチして、特定のエラーハンドリング処理をおこなうということを実現する

```rb
class User < ApplicationRecord
  validates_uniqueness_of :unique_id
end
```

```rb
def call_inner_transaction(id: , something_wrong: false)
  begin
    ActiveRecord::Base.transaction do
      StripeWebhookSucceededEvent.create!(unique_id: id)
      raise 'SOMETHING WRONG' if something_wrong
      puts '-' * 100
      puts 'EXECUTED!'
      puts '-' * 100
    end
  rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => e
    puts '*' * 100
    puts "RAISED!"
    puts '*' * 100
    puts e.message
  end
end
```
```rb
dupulicated_id = rand(999_999_999_999)

# EXECUTE
# COMMIT 

SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s)
# [88] pry(main)> SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s)
#    (0.4ms)  BEGIN
#   StripeWebhookSucceededEvent Create (0.7ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('225043383392', '2020-01-09 10:53:13')
# ----------------------------------------------------------------------------------------------------
# EXECUTED!
# ----------------------------------------------------------------------------------------------------
#    (2.5ms)  COMMIT
# => nil

# DUPULICATE and RAISE
# NO EXECUTE
# ROLLBACK
SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s)
# [89] pry(main)> SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s)
#    (0.4ms)  BEGIN
#   StripeWebhookSucceededEvent Create (1.1ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('225043383392', '2020-01-09 10:53:17')
#    (2.6ms)  ROLLBACK
# ****************************************************************************************************
# RAISED!
# ****************************************************************************************************
# Mysql2::Error: Duplicate entry '225043383392' for key 'index_users_on_unique_id': INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('225043383392', '2020-01-09 10:53:17')
# => nil


# DUPULICATE and RAISE before SOMETHING WRONG
# NO EXECUTE
# ROLLBACK
SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s, something_wrong: true)
# [90] pry(main)> SomeClass.new.call_inner_transaction(id: dupulicated_id.to_s, something_wrong: true)
#    (0.4ms)  BEGIN
#   StripeWebhookSucceededEvent Create (2.6ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('225043383392', '2020-01-09 10:53:24')
#    (3.3ms)  ROLLBACK
# ****************************************************************************************************
# RAISED!
# ****************************************************************************************************
# Mysql2::Error: Duplicate entry '225043383392' for key 'index_users_on_unique_id': INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('225043383392', '2020-01-09 10:53:24')
# => nil

# SOMETHING WRONG
# NO DUPULICATE
# NO EXECUTE
# RAISE AND ROLLBACK
SomeClass.new.call_inner_transaction(id: rand(999_999_999_999).to_s, something_wrong: true)
# [91] pry(main)> SomeClass.new.call_inner_transaction(id: rand(999_999_999_999).to_s, something_wrong: true)
#    (0.9ms)  BEGIN
#   StripeWebhookSucceededEvent Create (1.0ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('357583484588', '2020-01-09 10:53:28')
#    (3.0ms)  ROLLBACK
# RuntimeError: SOMETHING WRONG
# from (pry):120:in `block in call_inner_transaction'
```


```rb
def call_outer_transaction(id: , something_wrong: false)
  ActiveRecord::Base.transaction do
    begin
      StripeWebhookSucceededEvent.create!(unique_id: id)
      raise 'SOMETHING WRONG' if something_wrong
      puts '-' * 100
      puts 'EXECUTED!'
      puts '-' * 100
    rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => e
      puts '*' * 100
      puts "RAISED!"
      puts '*' * 100
      puts e.message
    end
  end
end
```

```rb
dupulicated_id = rand(999_999_999_999)

# EXECUTE
# COMMIT
SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s)
# [93] pry(main)> SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s)
#    (0.4ms)  BEGIN
#   StripeWebhookSucceededEvent Create (0.6ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('64910455241', '2020-01-09 10:54:18')
# ----------------------------------------------------------------------------------------------------
# EXECUTED!
# ----------------------------------------------------------------------------------------------------
#    (4.7ms)  COMMIT
# => nil



# DUPLUCATED AND RAISED
# NO EXECUTE
# BUT COMMIT HAPPENS
SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s)
# [94] pry(main)> SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s)
#    (0.5ms)  BEGIN
#   StripeWebhookSucceededEvent Create (6.4ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('64910455241', '2020-01-09 10:54:26')
# ****************************************************************************************************
# RAISED!
# ****************************************************************************************************
# Mysql2::Error: Duplicate entry '64910455241' for key 'index_users_on_unique_id': INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('64910455241', '2020-01-09 10:54:26')
#    (5.4ms)  COMMIT
# => nil

# DUPLUCATED AND RAISED
# NO EXECUTE
# BUT COMMIT HAPPENS

SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s, something_wrong: true)
# [95] pry(main)> SomeClass.new.call_outer_transaction(id: dupulicated_id.to_s, something_wrong: true)
#    (1.0ms)  BEGIN
#   StripeWebhookSucceededEvent Create (1.3ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('64910455241', '2020-01-09 10:54:32')
# ****************************************************************************************************
# RAISED!
# ****************************************************************************************************
# Mysql2::Error: Duplicate entry '64910455241' for key 'index_users_on_unique_id': INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('64910455241', '2020-01-09 10:54:32')
#    (4.3ms)  COMMIT
# => nil

# SOMETHING WRONG
# NO DUPULICATE
# NO EXECUTE
# BUT RAISE AND ROLLBACK

SomeClass.new.call_outer_transaction(id: rand(999_999_999_999).to_s, something_wrong: true)
# [96] pry(main)> SomeClass.new.call_outer_transaction(id: rand(999_999_999_999).to_s, something_wrong: true)
#    (0.5ms)  BEGIN
#   StripeWebhookSucceededEvent Create (0.8ms)  INSERT INTO `users` (`unique_id`, `created_at`) VALUES ('710309047775', '2020-01-09 10:54:37')
#    (2.4ms)  ROLLBACK
# RuntimeError: SOMETHING WRONG
# from (pry):137:in `block in call_outer_transaction'
```

# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2929








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->