This article will discuss database transactions and how to use them in Ruby on Rails. Transaction in a database means the state of the database will only change if all the statements in a transaction block succeed, otherwise all the statements will roll back and no state change will occur.
For an example of a case using this concept, consider an application where users reserve bus tickets online. The reservation will go through the following steps:
- Users will enter general reservation information like date, time, number of seats, etc.
- Users will enter their credit card details.
- The application will hit the third party, Stripe maybe, to charge the credit card.
- The application will record the charge from the user.
- The application will create an entry in the reservations table.
- The application will notify the user and the admin that a reservation has been created.
Here steps 3-6 need attention because steps 1 and 2 only involve getting information from the user, but step 3 onwards will change the state of the database.
Imagine step 3 doesn’t succeed because of not connecting to, or an exception from, the third party, but steps 4, 5, and 6 succeed, resulting in the creation of a reservation and notification but no charge to the user.
Among steps 3-6, if any one step causes problems and the other two succeed, it will cause an issue with the application and reservation system. Here’s a working example:
1 2 3 4 5 6 rails new transaction rails generate model User name rails generate scaffold Reservation booking_time:datetime seats:integer user:references rails generate model Notification message rails generate model Charge charge_time:datetime status:string rails db:create db:migrate
Secure the database from inconsistent data by editing the create method in
1 2 3 4 5 6 7 8 def create ActiveRecord::Base.transaction do @reservation = Reservation.create(reservation_params) Stripe::Charge.create(......) # call to third party Charge.create(charge_time: Time.now, status: 'paid') Notification.create(message: 'Booking made successfully..!!') end end
In this way, if one operation fails, the transaction will make sure none of the operations change the state of the database.
Rescuing from the transaction block can be accomplished like this:
1 2 3 4 5 6 7 8 9 10 def create ActiveRecord::Base.transaction do @reservation = Reservation.create(reservation_params) Stripe::Charge.create(......) # call to third party Charge.create(charge_time: Time.now, status: 'paid') Notification.create(message: 'Booking made successfully..!!') end rescue ActiveRecord::RecordInvalid puts "There was an error..!!" end
This method helps to avoid errors in applications in many different types of situations.