Optimizing Ruby on Rails Database Performance for Faster Application Scaling
Made some progress with the 48in21 challenge
Some progress
🎧 Spotlights
Some tips for optimizing Ruby on Rails applications performance
More Go
Are you experiencing slow response times and downtime on your Ruby on Rails application?
Do you want to ensure a positive user experience while scaling your application to meet increasing demand?
Optimizing your database's performance is crucial to preventing slow response times and downtime. To speed up your application, delegate tasks such as sending emails or generating reports to background jobs.
Remove N+1 Queries, use a tool like bullet to identify and remove.
Add every process that takes more than *1 second* in a background job. The number is arbitrary you can scale up or down depending on your business needs.
Following these two approaches, you might reduce at least 80% of your application's scaling problems.
Understanding the Issues with Rails Callbacks and ActiveRecord
There has been much debate on whether to use Rails callbacks. Yet, both sides miss the bigger picture.
Circular dependencies can harm the layered architectures within our applications. The performance and functionality of a software system depend on its behavior. Some systems may need greater flexibility.
The issue with ActiveRecord is that Object-Relational Mappers (ORMs) are always flawed abstractions.
ORMs translate between a database table and objects in a system. The underlying problem here is that ActiveRecord can act as a leaky abstraction. Most frameworks have established conventions for everyday use. These conventions are not strict rules but helpful guidelines.
It's important to remember that rushing to adopt a new framework can increase the cost of implementing its features.
It's up to the user to decide how to use the framework to best suit their needs.
Snippets of Code is an engineering journal documenting learnings for experiments and projects. I am learning about various programming languages with Exercism’s #48in21 challenge. Please share or like if the content is helpful.
In a Rails application handling user authentication, the use of callbacks in ActiveRecord models can lead to issues if not carefully managed.
Let's consider a scenario where we have a User model with associated models such as Profile and Subscription. These models might have callbacks defined for various purposes, such as sending notifications or updating related records.
class User < ApplicationRecord
has_one :profile
has_many :subscriptions
after_create :send_welcome_email
before_destroy :cancel_subscriptions
private
def send_welcome_email
# Logic to send welcome email to the newly registered user
end
def cancel_subscriptions
# Logic to cancel all subscriptions associated with the user
end
end
While callbacks like `after_create` and `before_destroy` seem convenient, they can introduce circular dependencies and tightly couple models.
If the `send_welcome_email` method fails during user creation, it might affect the entire transaction, including associated records like subscriptions.
If you are enjoying the learning journal, please ❤️
To address this, developers might opt for more explicit and decoupled approaches, such as using service objects or observers.
Service objects encapsulate complex business logic related to user actions, while observers allow models to react to changes without directly coupling to each other.
# Service Object for User Registration
class UserRegistrationService
def self.register(params)
user = User.create(params)
if user.valid?
# Send welcome email
UserMailer.welcome_email(user).deliver_now
end
user
end
end
# Observer for Subscription Cancellation
class UserObserver < ActiveRecord::Observer
def before_destroy(user)
user.subscriptions.each(&:cancel)
end
end
By leveraging service objects or observers, developers can achieve greater flexibility and maintainability in their Rails applications.
While Rails callbacks offer convenience, understanding their limitations and choosing alternative patterns can lead to more robust and scalable software architectures.
If you think someone will benefit from this content, please share.
💻 👓
package blackjack
// ParseCard returns the integer value of a card following blackjack ruleset.
func ParseCard(card string) int {
switch card {
case "ace":
return 11
case "two":
return 2
case "three":
return 3
case "four":
return 4
case "five":
return 5
case "six":
return 6
case "seven":
return 7
case "eight":
return 8
case "nine":
return 9
case "ten":
return 10
case "jack":
return 10
case "queen":
return 10
case "king":
return 10
default:
return 0
}
}
// FirstTurn returns the decision for the first turn, given two cards of the
// player and one card of the dealer.
func FirstTurn(card1, card2, dealerCard string) string {
card1Value := ParseCard(card1)
card2Value := ParseCard(card2)
dealerCardValue := ParseCard(dealerCard)
total := card1Value + card2Value
switch {
case card1 == card2 && card1 == "ace":
return "P"
case total == 21 && canWin(dealerCard, total):
return "W"
case shouldStand(dealerCard, total):
return "S"
case total >= 17 && total <= 20:
return "S"
case total >= 12 && total <= 16:
if dealerCardValue >= 7 {
return "H"
}
return "S"
}
return "H"
}
func canWin(dealerCard string, total int) bool {
return dealerCard != "ace" && dealerCard != "king" && dealerCard != "queen" && dealerCard != "jack"
}
func shouldStand(dealerCard string, total int) bool {
return total == 21 && (dealerCard == "ace" || dealerCard == "king" || dealerCard == "queen" || dealerCard == "jack")
}
Simulating Blackjack
🧪 Next Actions
I am currently making progress with a challenge by focusing on a single problem for a week. I plan to revisit some esoteric languages like Nim, even though the setup can be quite painful.
I will continue to refresh my knowledge of Golang and report back any findings.