require 'activerecord-import'
class BulkImportPostsService
BATCH_SIZE = 1000
def initialize(csv_file_path)
@csv_file_path = csv_file_path
end
def call
posts = []
imported_count = 0
CSV.foreach(@csv_file_path, headers: true) do |row|
posts << Post.new(
title: row['title'],
body: row['body'],
author_id: row['author_id'],
published_at: row['published_at']
)
if posts.size >= BATCH_SIZE
imported_count += import_batch(posts)
posts = []
end
end
# Import remaining posts
imported_count += import_batch(posts) if posts.any?
Result.success(imported_count: imported_count)
rescue StandardError => e
Rails.logger.error("Bulk import failed: #{e.message}")
Result.failure(error: e.message)
end
private
def import_batch(posts)
Post.import(
posts,
validate: false,
on_duplicate_key_update: {
conflict_target: [:id],
columns: [:title, :body, :updated_at]
}
)
posts.size
end
end
Inserting thousands of records one-by-one is prohibitively slow due to the overhead of individual INSERT statements. The activerecord-import gem provides bulk insert capabilities that compile multiple records into a single multi-row INSERT, dramatically improving throughput. I use this for data imports, batch processing, or seeding test environments. The gem supports validations, callbacks (optionally), and conflict resolution strategies like on_duplicate_key_update. For maximum performance, I disable validations and callbacks when data is already trusted. The trade-off is that bulk operations bypass some ActiveRecord conveniences like after_create callbacks, so I handle those concerns separately if needed. Monitoring insertion rates helps identify when bulk operations are worth the complexity.