Rails7.2がbetaフェーズとなっています。
リリースに先立ち、新機能や変更点などをまとめつつ、実際に検証してみました。
前編も併せてご確認ください。
トランザクションの最中にActive Jobをキューに渡さない
トランザクションの最中でActive Jobをエンキューした場合に、トランザクションのコミット完了までエンキューが遅延されるようになりました。
トランザクションがロールバックされた場合はジョブを削除します。
トランザクション内でジョブをエンキューしてしまうと、コミットが完了する前にジョブが別プロセスで実行されてしまう可能性があるからです。
例えば以下のコードがある場合、コミット完了前にジョブが実行されてしまうと、実行時点でまだuser
は存在しないため、エラーが発生します。
ActiveRecord::Base.transaction do
user = User.create!(...)
HogeJob.perform_later(user.id)
end
一方で必ずしもトランザクション完了前にジョブが実行されるとも限らないため、ある時はエラー・ある時は正常動作という予測不可能な状況が生まれてしまうのです。
こういったデータ不整合の問題を未然に防ぐためRails7.2から変更が加わりました。
enqueue_after_transaction_commit
オプションを指定することで、ジョブ毎にトランザクションの最中にエンキューするか、コミット後にエンキューするか選択できるようになります。
以下のIssueで議論がされていました。
なおこの議論は2016年から続いていた模様です。
実際にやってみた
まずはPost
テーブルとPost
コントローラーを作成します。
$ rails g model Post content:text
$ rails db:migrate
$ rails g controller posts new create
作成したコントローラーに以下の記述を追加します。またルーティングも作成します。
class PostsController < ApplicationController
def new
end
def create
ActiveRecord::Base.transaction do
post = Post.create(content: "New Content")
# 後ほど作成するJob
UpdatePostJob.perform_later(post.id)
# Jobの実行タイミングを観察するためにわざと例外を発生させる
raise ActiveRecord::Rollback
end
render json: { message: "Transaction rolled back" }
end
end
Rails.application.routes.draw do
get "posts/new", to: "posts#new"
post "posts", to: "posts#create"
end
ERBファイルで簡易的なフォームも作成しておきます。
<!DOCTYPE html>
<html>
<head>
<title>Create Post</title>
</head>
<body>
<h1>Create a New Post</h1>
<%= form_with url: posts_path, method: :post do %>
<div>
<%= submit_tag "Create Post" %>
</div>
<% end %>
</body>
</html>
次にコントローラーに記載したUpdatePostJob
を作成します。
$ rails g job UpdatePostJob
app/jobs/update_post_job.rb
の実装は次のとおりです。
class UpdatePostJob < ApplicationJob
queue_as :default
self.enqueue_after_transaction_commit = :never # この1行がRails7.2の新機能
def perform(post_id)
Post.find(post_id).update(content: "Updated Content")
end
end
まずはenqueue_after_transaction_commit
メソッドに:never
オプションを指定します。
:never
オプションは、トランザクションのコミットを待たず即座にジョブをエンキューするオプションです。
サーバーを起動し、http://localhost:3000/posts/new
を開きます。
Create Post
ボタンを押してみてください。
ログは以下のようになります。
(個人情報が含まれる一部のパスは改変しています)
Started POST "/posts" for 127.0.0.1 at 2024-07-21 18:03:34 +0900
Processing by PostsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "commit"=>"Create Post"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/posts_controller.rb:7:in `block in create'
Post Create (0.2ms) INSERT INTO "posts" ("content", "created_at", "updated_at") VALUES (?, ?, ?) RETURNING "id" [["content", "New Content"], ["created_at", "2024-07-21 09:03:34.258860"], ["updated_at", "2024-07-21 09:03:34.258860"]]
↳ app/controllers/posts_controller.rb:7:in `block in create'
[ActiveJob] Enqueued UpdatePostJob (Job ID: 88e42cde-ac56-4785-b579-d67fcf269c62) to Async(default) with arguments: 1
[ActiveJob] ↳ app/controllers/posts_controller.rb:8:in `block in create'
TRANSACTION (0.0ms) rollback transaction
↳ app/controllers/posts_controller.rb:6:in `create'
Completed 200 OK in 23ms (Views: 0.2ms | ActiveRecord: 1.8ms (1 query, 0 cached) | GC: 0.4ms)
[ActiveJob] [UpdatePostJob] [88e42cde-ac56-4785-b579-d67fcf269c62] Performing UpdatePostJob (Job ID: 88e42cde-ac56-4785-b579-d67fcf269c62) from Async(default) enqueued at 2024-07-21T09:03:34.265483000Z with arguments: 1
[ActiveJob] [UpdatePostJob] [88e42cde-ac56-4785-b579-d67fcf269c62] Post Load (0.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[ActiveJob] [UpdatePostJob] [88e42cde-ac56-4785-b579-d67fcf269c62] ↳ app/jobs/update_post_job.rb:6:in `perform'
[ActiveJob] [UpdatePostJob] [88e42cde-ac56-4785-b579-d67fcf269c62] Error performing UpdatePostJob (Job ID: 88e42cde-ac56-4785-b579-d67fcf269c62) from Async(default) in 6.24ms: ActiveRecord::RecordNotFound (Couldn't find Post with 'id'=1):
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activerecord-7.2.0.beta3/lib/active_record/core.rb:256:in `find'
/hogehoge/rails7.2-app/app/jobs/update_post_job.rb:6:in `perform'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/execution.rb:68:in `block in _perform_job'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/i18n-1.14.5/lib/i18n.rb:351:in `with_locale'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/translation.rb:9:in `block (2 levels) in <module:Translation>'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `instance_exec'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/core_ext/time/zones.rb:65:in `use_zone'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/timezones.rb:9:in `block (2 levels) in <module:Timezones>'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `instance_exec'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:141:in `run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/execution.rb:67:in `_perform_job'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/instrumentation.rb:32:in `_perform_job'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/execution.rb:51:in `perform_now'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/instrumentation.rb:26:in `block in perform_now'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activerecord-7.2.0.beta3/lib/active_record/railties/job_runtime.rb:13:in `block in instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/instrumentation.rb:40:in `block in instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/notifications.rb:210:in `block in instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/notifications.rb:210:in `instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/instrumentation.rb:39:in `instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activerecord-7.2.0.beta3/lib/active_record/railties/job_runtime.rb:11:in `instrument'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/instrumentation.rb:26:in `perform_now'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/logging.rb:32:in `block in perform_now'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/tagged_logging.rb:138:in `block in tagged'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/tagged_logging.rb:38:in `tagged'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/tagged_logging.rb:138:in `tagged'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/broadcast_logger.rb:241:in `method_missing'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/logging.rb:39:in `tag_logger'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/logging.rb:32:in `perform_now'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/execution.rb:29:in `block in execute'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/railtie.rb:81:in `block (4 levels) in <class:Railtie>'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/reloader.rb:77:in `block in wrap'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/execution_wrapper.rb:91:in `wrap'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/reloader.rb:74:in `wrap'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/railtie.rb:80:in `block (3 levels) in <class:Railtie>'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `instance_exec'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activesupport-7.2.0.beta3/lib/active_support/callbacks.rb:141:in `run_callbacks'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/execution.rb:27:in `execute'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/activejob-7.2.0.beta3/lib/active_job/queue_adapters/async_adapter.rb:70:in `perform'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:359:in `run_task'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:350:in `block (3 levels) in create_worker'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `loop'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/opt/homebrew/lib/ruby/gems/3.2.0/gems/concurrent-ruby-1.3.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
トランザクションはロールバックされているものの、ジョブはすでに実行されてしまっているため、ActiveRecord::RecordNotFound (Couldn't find Post with 'id'=1)
とエラーが出てしまっています。
次にenqueue_after_transaction_commit
オプションを:always
に変更して再度Create Post
ボタンを押してみます。
結果は次のようになります。
Started POST "/posts" for 127.0.0.1 at 2024-07-21 18:06:18 +0900
Processing by PostsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "commit"=>"Create Post"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/posts_controller.rb:7:in `block in create'
Post Create (0.1ms) INSERT INTO "posts" ("content", "created_at", "updated_at") VALUES (?, ?, ?) RETURNING "id" [["content", "New Content"], ["created_at", "2024-07-21 09:06:18.833509"], ["updated_at", "2024-07-21 09:06:18.833509"]]
↳ app/controllers/posts_controller.rb:7:in `block in create'
[ActiveJob] Enqueued UpdatePostJob (Job ID: ac905a7e-3719-410a-8a2b-880d49622ca6) to Async(default) with arguments: 1
[ActiveJob] ↳ app/controllers/posts_controller.rb:8:in `block in create'
TRANSACTION (0.0ms) rollback transaction
↳ app/controllers/posts_controller.rb:6:in `create'
Completed 200 OK in 11ms (Views: 0.1ms | ActiveRecord: 2.3ms (1 query, 0 cached) | GC: 0.3ms)
今度はトランザクションがロールバックされただけでログが終了しています。
これはトランザクションのコミットが完了するまで、ジョブのキューへの登録が遅延されていることを表しています。
トランザクション内でのコールバックが使える
Rails 7.2ではトランザクションの中でafter_commit
とafter_rollback
コールバックを指定できるようになります。
ActiveRecord::Base.transaction
がトランザクションオブジェクトを返すようになったのです。
# after_commit の使用例
ActiveRecord::Base.transaction do |transaction|
user.save!
transaction.after_commit do
NotificationService.send_welcome_email(user)
end
end
# after_rollback の使用例
ActiveRecord::Base.transaction do |transaction|
user.save!
transaction.after_rollback do
ErrorLoggerService.log_user_creation_failure(user)
end
end
またActiveRecord::Base.current_transaction
メソッドも導入されました。
先ほどの実装は次のように書くこともできます。
# after_commit の使用例
ActiveRecord::Base.transaction do
user.save!
ActiveRecord::Base.current_transaction.after_commit do
NotificationService.send_welcome_email(user)
end
end
# after_rollback の使用例
ActiveRecord::Base.transaction do
user.save!
ActiveRecord::Base.current_transaction.after_rollback do
ErrorLoggerService.log_user_creation_failure(user)
end
end
実際にやってみた
まずはafter_commit
からやってみます。
先ほどのサンプルを一部流用し、以下のコードを記述しました。
class PostsController < ApplicationController
def new
end
def create
ActiveRecord::Base.transaction do |transaction|
post = Post.create(content: "New Content")
# after_commit を使用
transaction.after_commit do
puts "Transaction committed"
end
end
end
end
サーバーを起動し、http://localhost:3000/posts/new
を開きます。
Create Post
ボタンを押してみてください。
ログは以下のようになります。
Started POST "/posts" for 127.0.0.1 at 2024-07-25 10:36:01 +0900
Processing by PostsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "commit"=>"Create Post"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/posts_controller.rb:7:in `block in create'
Post Create (0.5ms) INSERT INTO "posts" ("content", "created_at", "updated_at") VALUES (?, ?, ?) RETURNING "id" [["content", "New Content"], ["created_at", "2024-07-25 01:36:02.005851"], ["updated_at", "2024-07-25 01:36:02.005851"]]
↳ app/controllers/posts_controller.rb:7:in `block in create'
TRANSACTION (0.3ms) commit transaction
↳ app/controllers/posts_controller.rb:6:in `create'
Transaction committed
Completed 200 OK in 33ms (Views: 0.1ms | ActiveRecord: 2.0ms (1 query, 0 cached) | GC: 0.5ms)
トランザクションのコミット完了後にTransaction committed
と出力されることが確認できました。
また次のようにcurrent_transaction
を使った場合も挙動は同じでした。
class PostsController < ApplicationController
def new
end
def create
ActiveRecord::Base.transaction do
post = Post.create(content: "New Content")
# current_transaction.after_commit を使用
ActiveRecord::Base.current_transaction.after_commit do
puts "Transaction committed"
end
end
end
end
続いてafter_rollback
を試します。
class PostsController < ApplicationController
def new
end
def create
ActiveRecord::Base.transaction do |transaction|
post = Post.create(content: "New Content")
transaction.after_rollback do
puts "Transaction rollback"
end
# わざと例外を発生
raise ActiveRecord::Rollback
end
end
end
ログは以下のようになります。
Started POST "/posts" for 127.0.0.1 at 2024-07-25 10:43:28 +0900
Processing by PostsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "commit"=>"Create Post"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/posts_controller.rb:7:in `block in create'
Post Create (0.2ms) INSERT INTO "posts" ("content", "created_at", "updated_at") VALUES (?, ?, ?) RETURNING "id" [["content", "New Content"], ["created_at", "2024-07-25 01:43:28.251642"], ["updated_at", "2024-07-25 01:43:28.251642"]]
↳ app/controllers/posts_controller.rb:7:in `block in create'
TRANSACTION (0.1ms) rollback transaction
↳ app/controllers/posts_controller.rb:6:in `create'
Transaction rollback
Completed 200 OK in 14ms (Views: 0.1ms | ActiveRecord: 3.1ms (1 query, 0 cached) | GC: 0.4ms)
トランザクションのロールバック完了後にTransaction rollback
と出力されていますね。
こちらも同じくcurrent_transaction
を使っても同じ挙動になりました。
class PostsController < ApplicationController
def new
end
def create
ActiveRecord::Base.transaction do
post = Post.create(content: "New Content")
# current_transaction.after_rollback を使用
ActiveRecord::Base.current_transaction.after_rollback do
puts "Transaction rollback"
end
# わざと例外を発生
raise ActiveRecord::Rollback
end
end
end
Ruby3.3以上の場合、デフォルトでYJITが有効
Ruby3.3以降を使用している場合、YJITをデフォルトで有効にする設定が加わりました。
これによりRuby3.3以上を使ったRailsアプリケーションは追加設定なしにパフォーマンス向上が見込めます。
以下のPRで変更が取り込まれているようです。
YJIT(Yet Another Ruby JIT)はRubyの新しいJIT(Just In Time)コンパイラです。
JITコンパイラとは、プログラムの実行中にコードを機械語に変換し、実行速度を向上させる技術です。
コード生成を遅延させて必要な記述だけを部分的に変換することで、高速化を図っているようです。
Ruby3.1で導入後、Ruby3.3で安定化が行われています。
この恩恵をRailsアプリケーションでも受けられるように、Ruby3.3以上を使っている場合のみデフォルトでYJITが有効になります。
実際に見てみる
まずはRuby3.3以上、Rails7.2であることを確認します。
ruby3.3-rails7.2-app % ruby -v
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin22]
ruby3.3-rails7.2-app % rails -v
Rails 7.2.0.beta3
railsコンソールでRubyVM::YJIT.enabled?を実行し、YJITが有効になっていることを確認します。
ruby3.3-rails7.2-app % rails c
Loading development environment (Rails 7.2.0.beta3)
ruby33-rails72-app(dev)> RubyVM::YJIT.enabled?
=> true
たしかに有効になっていました。
メモリ割り当て最適化のためDockerfileにjemallocを設定
Rails7.2からデフォルトで作成されるDockerfileに、jemalloc
というライブラリをインストールするステップが追加されました。
jemalloc
とはメモリ割り当てを最適化するためのライブラリです。
Railsアプリケーションのメモリ効率とそれに伴うアプリケーション全体のパフォーマンス向上を目的に導入されました。
実際に見てみる
Rails7.2でrails new
した場合に生成されるDockerfileは次のとおりです。
# syntax = docker/dockerfile:1
# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t my-app .
# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY=<value from config/master.key> my-app
# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.2.2
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
# Rails app lives here
WORKDIR /rails
# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libvips && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base AS build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git pkg-config && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
# Final stage for app image
FROM base
# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER 1000:1000
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libvips && \
の部分でlibjemalloc2
という形でインストールされていました。
bin/setupにpuma-dev設定用のコメントを追加
bin/setup
ファイルにpuma-devを設定するためのコメントが追加されました。
puma-devはDockerを使わずに環境構築する際、開発用サーバーの設定を迅速かつ簡単にしてくれるためのものです。
実際に見てみる
rails new
した時点のbin/setup
の中身はこんな感じです。
#!/usr/bin/env ruby
require "fileutils"
APP_ROOT = File.expand_path("..", __dir__)
APP_NAME = "rails7_2_app"
def system!(*args)
system(*args, exception: true)
end
FileUtils.chdir APP_ROOT do
# This script is a way to set up or update your development environment automatically.
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
# Add necessary setup steps to this file.
puts "== Installing dependencies =="
system! "gem install bundler --conservative"
system("bundle check") || system!("bundle install")
# puts "\n== Copying sample files =="
# unless File.exist?("config/database.yml")
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
# end
puts "\n== Preparing database =="
system! "bin/rails db:prepare"
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"
puts "\n== Restarting application server =="
system! "bin/rails restart"
# puts "\n== Configuring puma-dev =="
# system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
# system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
end
Rails7.2で一番最後の3行が追加されています。
# puts "\n== Configuring puma-dev =="
# system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
# system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
おわりに
この記事では、Rails 7.2の新機能や変更点について、一部の内容を実際に試しながら解説しました。
前編からやってきたため、結構パワーを使うなぁ、という感想です。
今回の記事執筆を機に、RailsのコードやIssueを読む時間を意識的に確保していきたいと思います。
またこの記事に関して間違っている点がありましたら、教えていただけますと幸いです。
参考資料