[Ruby] [Rails] I implemented a transaction that brings together multiple DB processes.

2 minute read

Introduction

I was developing an application with Rails and implemented a process to create data for multiple tables at the same time. In that process, I knew that there was a concept called transaction, in which if either process failed, both processes were not done, so I will write transaction and the implemented contents as a memorandum and output.

What is # transaction Looking up the definition of the word “transaction” [Processing, handling, treatment, business, trading, buying and selling, newsletter, bulletin, minutes]

In IT and programming “Individual things that combine multiple processes into one”

It can be defined as

This article is a very easy-to-understand summary of the concept of transactions, and I have used it as a reference.

What is a “transaction”? I tried to talk in a super easy way!

transaction method usage

  • Treat multiple processes as one cohesive process
  • If even one process fails, the entire transaction fails.
  • If it fails, it means that all processing was not done

This is the basic syntax


Model. transaction do
  # Access to table
  # Access to table
end
  #Processing when transaction processing is successful
rescue => e
  #Processing when transaction processing fails

Note that “use a method that raises an exception when processing fails”.

If any one of the processes included in the transaction fails, the transaction is considered not to have done all the processes in the transaction, and the condition for not doing that is “occurrence of exception”.

Let’s actually implement it.

Implementation

The app I was developing this time had a user grouping function. Therefore, we assume that the user who creates a new group will automatically belong to the group. Therefore, when creating a group, consider using transactions to avoid unexpected behavior.

The table structure is like this. Screenshot 2020-07-07 19.57.10.png

I wanted to create a groups table instance and a group_users table (an intermediate table between the users table and the groups table) with the id of the logged-in user as the user_id column.

  1. Create groups table instance
  2. Create group_users table instance

If either process of 1 or 2 fails for some reason, transaction is used in the create action of groups controller so that both processes have not been performed.

groups_controller.rb


# create action part excerpt
# The variable current_user stores the instance of the login user

  def create
    @group = Group.new(group_params)

# Apply transaction (create group and create intermediate table at the same time)
    Note that # save!, create! and "!" are added!
    @group.transaction do
      @group.save!
      current_user.group_users.create!(group_id: @group.id, permission: true)
    end
# Process when transaction is successful
      flash[:success] ='Created a new group'
      redirect_to @group
    rescue => e
# Processing when transaction fails
      flash.now[:danger] ='Group creation failed'
      render :new
  end

Note that “!” is added to both processes in this transaction, and the method that raises an exception when table data creation fails is used!

If it is implemented as follows without using transaction, there is a concern that unattended group will be created if creation of group_users table fails for some reason.

groups_controller.rb



When not using # transaction

def create
  @group = Group.new(group_params)

  if @group.save
    current_user.group_users.create(group_id: @group.id, permission: true)
    flash[:success] ='Created a new group'
    redirect_to @group
  else
    flash.now[:danger] ='Group creation failed'
    render :new:
  end
end

Finally

Thank you for reading the article! I tried to implement it while investigating this time, but honestly my understanding of transactions is not so deep. If you have any mistakes or a better description, please feel free to comment. I have referred to the following article. Thank you very much.

  • https://web-camp.io/magazine/archives/18310
  • https://www.sejuku.net/blog/27240