Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cascade:

minutes_to_complete: 40

who_is_this_for: This learning path is intended for software developers deploying and optimizing Ruby on Rails workloads on Linux/Arm64 environments, specifically using Google Cloud C4A virtual machines powered by Axion processors.
who_is_this_for: This is an introductory topic intended for software developers deploying and optimizing Ruby on Rails workloads on Linux Arm64 environments, specifically using Google Cloud C4A virtual machines powered by Axion processors.

learning_objectives:
- Provision an Arm-based SUSE SLES virtual machine on Google Cloud (C4A with Axion processors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,157 @@ layout: learningpathall
---

## Baseline Setup for Ruby on Rails with PostgreSQL
This section covers the installation and configuration of **PostgreSQL** and a **Rails application** on a SUSE Arm-based GCP VM. It includes setting up PostgreSQL, creating a Rails app, configuring the database, and starting the Rails server.
This section sets up PostgreSQL and connects it with a Ruby on Rails application on a SUSE Arm64 Google Cloud C4A virtual machine. You’ll install PostgreSQL, configure it for Rails, create a database user, and verify that Rails can connect and serve requests successfully.

### Install and Configure PostgreSQL
PostgreSQL is used with Ruby on Rails as a robust, production-ready relational database that reliably stores and manages application data.
PostgreSQL is a robust, production-grade relational database that integrates seamlessly with Ruby on Rails.

Install PostgreSQL and its development headers:

```console
sudo zypper install postgresql-devel postgresql-server
sudo systemctl start postgresql
sudo systemctl enable postgresql
```
- `postgresql-devel` is required to compile the pg gem for Rails.

Verify that the PostgreSQL service is active and running:
After installation, ensure that PostgreSQL is running and configured to start automatically at boot:

```console
sudo systemctl start postgresql
sudo systemctl enable postgresql
systemctl status postgresql
```
The output should look like:
```output
● postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2025-11-04 21:25:59 UTC; 18s ago
Main PID: 26997 (postgres)
Tasks: 7
CPU: 372ms
CGroup: /system.slice/postgresql.service
├─ 26997 /usr/lib/postgresql15/bin/postgres -D /var/lib/pgsql/data
├─ 26998 "postgres: logger " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
├─ 26999 "postgres: checkpointer " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
├─ 27000 "postgres: background writer " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" >
├─ 27002 "postgres: walwriter " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
├─ 27003 "postgres: autovacuum launcher " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ">
└─ 27004 "postgres: logical replication launcher " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ">
```
If the Active state reads running, your PostgreSQL service is operational and ready for configuration.

This command creates a new PostgreSQL role (user) named `gcpuser` with **superuser privileges**.
### Create a Database Role for Rails
Next, create a dedicated PostgreSQL role (user) that Rails will use to connect to the database.

```console
sudo -u postgres createuser --superuser gcpuser
sudo -u postgres psql -c "CREATE USER gcpuser WITH SUPERUSER PASSWORD 'your_password';"
```
- `sudo -u postgres` → Runs the command as the `postgres` user (default PostgreSQL superuser).
- `createuser --superuser gcpuser` → Creates a PostgreSQL role named `gcpuser` with full admin privileges.
- Can create databases
- Can create other roles/users
- Can grant privileges
This command:

Executes under the default PostgreSQL superuser account (postgres).
Creates a new PostgreSQL role called gcpuser.
Assigns superuser privileges, allowing the user to create databases, manage roles, and execute administrative tasks.

This user will serve as the Rails database owner and be referenced in the Rails configuration file (config/database.yml) later.

This role will be used by Rails to connect to the PostgreSQL database.
### Set Environment variables

Before creating your Rails application, export environment variables so Rails and the `pg gem` can authenticate automatically with PostgreSQL.

```console
export PGUSER=gcpuser
export PGPASSWORD=your_password
export PGHOST=localhost
```

PGUSER → Specifies the PostgreSQL user that Rails will connect as.
PGPASSWORD → Stores the password for that user in memory (temporary for this session).
PGHOST → Points to the PostgreSQL host (in this case, the local VM).

### Create a Rails App with PostgreSQL
Creates a new Rails application configured to use PostgreSQL as its database.
Now, generate a new Rails application configured to use PostgreSQL as its default database adapter:

```console
rails new db_test_rubyapp -d postgresql
cd db_test_rubyapp
bundle install
```
- Creates a new Rails application called `db_test_app`.
- `d postgresql` → Tells Rails to use PostgreSQL as the database instead of the default SQLite.
- `bundle install` ensures all required gems are installed.
- rails new db_test_rubyapp → Creates a new Rails application named db_test_rubyapp.
- `d postgresql` → Instructs Rails to use PostgreSQL instead of the default SQLite database.
- bundle install → Installs all gem dependencies defined in the Gemfile, including the pg gem that connects Rails to PostgreSQL.

{{% notice Note %}}
Check `config/database.yml` to ensure the `username` and `password` match your PostgreSQL role `(gcpuser)`.
{{% /notice %}}

### Verify and Update Database Configuration
Open the Rails database configuration file:
Rails uses the `config/database.yml` file to define how it connects to databases in different environments (development, test, and production).
It's important to verify that these credentials align with the PostgreSQL role you created earlier.

Open the file with your preferred text editor:

```console
nano config/database.yml
sudo vi config/database.yml
```
Find the `default`: and `development`: sections.
Ensure the username matches the PostgreSQL user you created (gcpuser):
Locate the default and development sections, and make sure they match the PostgreSQL user and password you configured.

You should see output similar to:
Your configuration file should have the following fields set:
```output
default: &default
adapter: postgresql
encoding: unicode
username: gcpuser
password:
password: your_password
host: localhost
pool: 5

development:
<<: *default
```

### Change the Authentication Method
By default, PostgreSQL on many Linux distributions (including SUSE) uses the ident authentication method for local connections. This method maps Linux system usernames directly to PostgreSQL roles. While convenient for local access, it prevents password-based authentication, which is necessary for Rails and most application connections.

To allow Rails to connect using a username and password, change the authentication method in PostgreSQL’s configuration file `pg_hba.conf` from ident to md5.

Open your configuration file
```console
sudo vi /var/lib/pgsql/data/pg_hba.conf
```
The file location `/var/lib/pgsql/data/pg_hba.conf` is the default data directory path for PostgreSQL on SUSE Linux.

Find lines like the following in the file:

```output
# IPv4 local connections:
host all all 127.0.0.1/32 ident
# IPv6 local connections:
host all all ::1/128 ident
```
Modify both lines to use md5, which enables password-based authentication:

```output
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 md5
```
After saving the file, restart the PostgreSQL service to apply the new authentication settings:

```console
sudo systemctl restart postgresql
```

Verify the change:
```console
sudo systemctl status postgresql
```
The service should show as active (running).

### Create and Initialize the Database
Initializes and creates the development and test databases for your Rails app using PostgreSQL.
Once PostgreSQL is configured and Rails can authenticate, you can create your application’s development and test databases.
This step verifies that Rails is correctly connected to PostgreSQL and that the pg gem is working on your Arm64 environment.

Run the following command from inside your Rails app directory:
```console
rails db:create
```
Expand All @@ -87,22 +166,26 @@ You should see output similar to:
Created database 'db_test_rubyapp_development'
Created database 'db_test_rubyapp_test'
```
This means Rails successfully connected to PostgreSQL and created both dev and test databases.
This output confirms that Rails successfully. It connected to the PostgreSQL service using the credentials from `config/database.yml` and created two new databases — one for development and one for testing.

### Generate a Scaffold for Testing
A database and Scaffold are required to create the actual PostgreSQL database for your Rails app and quickly generate the model, controller, views, and migrations for your data.
Let’s create a small test model and table — for example, a simple Task tracker:
To verify your Ruby on Rails and PostgreSQL integration, you’ll create a small scaffold application.
A scaffold is a Rails generator that automatically builds a model, controller, views, and database migration, allowing you to test CRUD (Create, Read, Update, Delete) operations quickly.

For this example, you’ll create a simple Task Tracker app that manages tasks with titles and due dates.
Run the following command inside your Rails project directory:

```console
rails generate scaffold task title:string due_date:date
```
This command automatically generates:
- Database migration for the tasks table
- A model (task.rb)
- A controller and views for CRUD operations
- **Scaffold** → Automatically generates boilerplate code for CRUD operations, saving time and ensuring your app has working forms and routes.
This single command automatically generates:
A database migration to create the tasks table in PostgreSQL.
A model file (app/models/task.rb) that maps to the tasks table.
A controller (app/controllers/tasks_controller.rb) with full CRUD actions (index, show, new, create, edit, update, destroy).
Corresponding views in app/views/tasks/ with ready-to-use HTML + embedded Ruby templates.
Route entries in config/routes.rb to make the new resource accessible via /tasks.

Then apply the migration:
Now apply the migration to create the tasks table in your PostgreSQL database:

```console
rails db:migrate
Expand All @@ -116,22 +199,27 @@ You should see output similar to:
== 20251006101717 CreateTasks: migrated (0.0128s) =============================
```

Database schema successfully updated.
This confirms that Rails connected successfully to PostgreSQL and the database schema was updated.
The new tasks table was created inside your db_test_rubyapp_development database.

### Verify Table and Database Connectivity
The previous command `rails generate scaffold task title:string due_date:date` created a `tasks` table in your PostgreSQL database.

Now, verify that the table exists and has the correct structure following the steps below:
TThe scaffold created a tasks table in your PostgreSQL database. Verify it exists and has the expected schema:

```console
sudo -u postgres psql
```
Inside the PostgreSQL shell, run:

```console
\c db_test_rubyapp_development
\d tasks
\q
```
- `sudo -u postgres psql` → Launches the PostgreSQL shell as the superuser `postgres`.
- `\c db_test_rubyapp_development` → Connects to the Rails app’s development database.
- `\d tasks` → Displays the schema (columns and types) of the `tasks` table.

- `\q → Exit from the PostgreSQL shell

You should see output similar to:
```output
psql (15.10)
Expand All @@ -152,7 +240,7 @@ Indexes:
"tasks_pkey" PRIMARY KEY, btree (id)
```

### Run Rails Server
### Open port 3000 in Google Cloud (VPC firewall)
Before proceeding to run the Rails server, you need to allow port 3000 from your GCP console. Below are the steps to do that:

a. On the GCP console, navigate to **Firewall** -> **Create Firewall Rule**
Expand Down Expand Up @@ -182,13 +270,22 @@ In the **"Protocols and Ports"**, click on **"TCP"**, and mention the port numbe
Click on **"Create"**. The Firewall rule will be created successfully and can be viewed in the Firewall Policies Page:

![ Create Firewall rule alt-text#center](images/firewall5.png "Figure 5: Create Firewall rule")

Once done, go back to the VM, and execute the below commands to allow port 3000:

### OS firewall (firewalld) on SUSE
Once done, go back to your VM, install FirewallD:
```console
sudo zypper install firewalld
```
Now start FirewallD and execute the commands to allow port 3000:

```console
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload
```

## Start Rails
Now that port 3000 is allowed in your VM’s ingress firewall rules, you can start the Rails server using the following command:

```console
Expand All @@ -210,4 +307,4 @@ You will see a Rails welcome page in your browser if everything is set up correc

![Rails-info page alt-text#center](images/rails-web.png "Figure 6: Ruby/Rails Welcome Page")

This verifies the basic functionality of the Ruby/Rails installation before proceeding to the benchmarking.
With port 3000 reachable and the welcome page loading, your Rails stack on SUSE Arm64 (C4A Axion) is verified end-to-end and you can proceed to benchmarking.
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ layout: learningpathall
---


## Ruby on Rails Benchmarking with built-in Benchmark
This section benchmarks Ruby on Rails using Ruby’s built-in `Benchmark` library to measure execution time for database inserts, queries, and CPU computations on GCP SUSE VMs, providing insights into performance metrics and bottlenecks.
## Ruby on Rails Benchmarking
In this section you will benchmark Ruby on Rails using Ruby’s built-in `Benchmark` library to measure execution time for database inserts, queries, and CPU computations on GCP SUSE VMs, providing insights into performance metrics and bottlenecks.

### Go into your Rails app folder
You need to navigate into the folder of your Rails application. This is where Rails expects your application code, models, and database configurations to be located. All commands related to your app should be run from this folder.
Navigate into the folder of your Rails application. This is where Rails expects your application code, models, and database configurations to be located. All commands related to your app should be run from this folder.

```console
cd ~/db_test_rubyapp
````

### Create the benchmark file inside the app
We create a new Ruby file named `benchmark.rb` where we will write code to measure performance.
### Create the benchmark
Now create a new Ruby file named `benchmark.rb` where you will write code to measure performance.

```console
nano benchmark.rb
vi benchmark.rb
```

### Benchmark code for measuring Rails app performance
Below mentioned code (`benchmark.rb` file) measures database inserts, queries, and CPU computations in your Rails application using Ruby’s Benchmark library.
Copy the code below into `benchmark.rb`. It measures database inserts, queries, and CPU computations in your Rails application using Ruby’s Benchmark library.

```ruby
require 'benchmark'
Expand Down Expand Up @@ -62,21 +62,21 @@ end
This code gives you a basic understanding of how your Rails app performs under different types of workloads.

### Run the benchmark inside Rails
Now that your benchmark file is ready, run it **within the Rails environment** using the following command:
Now that your benchmark file is ready, run it within the Rails environment using the following command:

```console
rails runner benchmark.rb
```
- `rails runner` runs any Ruby script in the context of your Rails application.
`rails runner` runs any Ruby script in the context of your Rails application.

- It automatically loads your **Rails environment**, including:
It automatically loads your Rails environment, including:
- All models (like `Task`)
- Database connections
- Configuration and dependencies

- This ensures that your benchmark can interact with the **PostgreSQL database** through ActiveRecord, rather than running as a plain Ruby script.
This ensures that your benchmark can interact with the PostgreSQL database through ActiveRecord, rather than running as a plain Ruby script.

You should see an output similar to:
You should see output similar to:

```output
user system total real
Expand All @@ -92,17 +92,8 @@ Computation: 0.410907 0.000000 0.410907 ( 0.410919)
- **total** → `user + system` (sum of CPU processing time).
- **real** → The **wall-clock time** (actual elapsed time, includes waiting for DB, I/O, etc).

### Benchmark summary on x86_64
To compare the benchmark results, the following results were collected by running the same benchmark on a `x86 - c4-standard-4` (4 vCPUs, 15 GB Memory) x86_64 VM in GCP, running SUSE:

| Task | user (sec) | system (sec) | total (sec) | real (sec) |
|--------------|------------|--------------|-------------|------------|
| DB Insert | 1.902564 | 0.061805 | 1.964369 | 2.606764 |
| DB Query | 2.725923 | 0.009513 | 2.735436 | 2.735956 |
| Computation | 0.331519 | 0.000083 | 0.331602 | 0.331610 |

### Benchmark summary on Arm64
Results from the earlier run on the `c4a-standard-4` (4 vCPU, 16 GB memory) Arm64 VM in GCP (SUSE):
Results summarized from the your run on the `c4a-standard-4` (4 vCPU, 16 GB memory) Arm64 VM in GCP (SUSE):


| Task | user (sec) | system (sec) | total (sec) | real (sec) |
Expand All @@ -112,10 +103,12 @@ Results from the earlier run on the `c4a-standard-4` (4 vCPU, 16 GB memory) Arm6
| Computation | 0.410907 | 0.000000 | 0.410907 | 0.410919 |


### Ruby/Rails performance benchmarking comparison on Arm64 and x86_64
### Key Takeaways

When you look the benchmarking results, you will notice that on the Google Axion C4A Arm-based instances:

When you compare the benchmarking results, you will notice that on the Google Axion C4A Arm-based instances:
Rails on Arm64 performs consistently: Ruby and PostgreSQL are both natively optimized for Arm, providing stable, predictable latency.
Database I/O remains the main optimization target: Techniques such as query caching, connection pooling, and async queries can improve DB-heavy performance.
Compute-bound tasks scale well: Axion’s Arm cores and Ruby’s YJIT show excellent CPU utilization for non-I/O workloads.

- **Database operations are the main bottleneck:** DB Insert and DB Query take the most time.
- **DB Query has the highest latency:** It is the slowest operation at 3.39 seconds.
- **Core computation is fast:** Pure Ruby/Rails calculations complete quickly at 0.41 seconds.
Ruby on Rails runs efficiently on Google Cloud’s Axion-based C4A Arm64 instances.
Loading