rails / rails

Ruby on Rails
https://rubyonrails.org
MIT License
56.06k stars 21.68k forks source link

Active Storage bug #51329

Closed St4niuu closed 8 months ago

St4niuu commented 8 months ago

I've encountered something really strange, like I see I may be the first one tackling with this. To the basis - I've got a table with all employees, each employee has an NFC id card as id column, their name and now I wanted them to have a "profile picture", something like this. I decided to use Active Storage library, everything works fine with query N + 1, fetching all employees and for each of them fetch separately their profile picture. I don't want to drain my database so I decided to optimise it and here comes the bug I would say.

Here is the N + 1 query:

@employees = Employee.all.map do |employee|
      { :id => employee.id, :name => employee.name, :avatar => employee.avatar.attached? ? url_for(employee.avatar) : nil }
    end

The result of the first fetched employee:

{
            "id": "0029216630",
            "name": "SPECIAL WORKS9",
            "avatar": "http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--45c6a565c85aede1125f25bacd31d70297362a73/IMG_4629.JPG"
        }

And this is how it is supposed to work, now let's go ahead to the "optimised" query, yes, with quotation marks 'cause I don't even know whether I'm doing it correctly.

Optimised query

@employees = Employee.with_attached_avatar.all.map do |employee|
      { :id => employee.id, :name => employee.name, :avatar => employee.avatar.attached? ? url_for(employee.avatar) : nil }
    end

The result of optimised query

{
            "id": "0029216630",
            "name": "SPECIAL WORKS9",
            "avatar": null
        }

Nevertheless, there is attached an image to the record, rails cannot see this image, like there is no any.

The Employee model definition

class Employee < ApplicationRecord
  has_many :started_occurrences, foreign_key: :start_employee, class_name: "Occurrence"
  has_many :finished_occurrences, foreign_key: :finish_employee, class_name: "Occurrence"
  has_one_attached :avatar
end

It's an API Rails app Got frontend built upon React.

Any advice?

System configuration

Rails version: 7.0.8

Ruby version: 3.2.2

JoeDupuis commented 8 months ago

I can't reproduce. Can you copy the setup code too? (Creating the employees with the avatars).

St4niuu commented 8 months ago
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2024_03_11_082941) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "active_storage_attachments", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.string "service_name", null: false
    t.bigint "byte_size", null: false
    t.string "checksum"
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end

  create_table "active_storage_variant_records", force: :cascade do |t|
    t.bigint "blob_id", null: false
    t.string "variation_digest", null: false
    t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
  end

  create_table "employees", id: :string, force: :cascade do |t|
    t.string "name", null: false
    t.index ["id"], name: "index_employees_on_id", unique: true
  end

  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"

end

Here is whole database schema definition.

Employee.create(id: "0029216630", name: "Example") - here is an instance of an employee.


class Employee < ApplicationRecord
  has_one_attached :avatar
end
``` - Employee model class

> source "https://rubygems.org"
> git_source(:github) { |repo| "https://github.com/#{repo}.git" }
> 
> ruby "3.2.2"
> 
> # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
> gem "rails", "~> 7.0.8"
> 
> # Use postgresql as the database for Active Record
> gem "pg", "~> 1.1"
> 
> # Use the Puma web server [https://github.com/puma/puma]
> gem "puma", "~> 5.0"
> 
> # Build JSON APIs with ease [https://github.com/rails/jbuilder]
> # gem "jbuilder"
> 
> # Use Redis adapter to run Action Cable in production
> # gem "redis", "~> 4.0"
> 
> # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
> # gem "kredis"
> 
> # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
> # gem "bcrypt", "~> 3.1.7"
> 
> # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
> gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
> 
> # Reduces boot times through caching; required in config/boot.rb
> gem "bootsnap", require: false
> 
> # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
> # gem "image_processing", "~> 1.2"
> 
> # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
> gem "rack-cors"
> 
> gem "devise"
> gem "devise-jwt"
> 
> gem "redis"
> 
> gem "whenever"
> 
> gem "sidekiq"
> 
> gem 'will_paginate', '~> 4.0'
> 
> gem 'image_processing', '~> 1.2'
> 
> group :development, :test do
>   # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
>   gem "debug", platforms: %i[ mri mingw x64_mingw ]
> end
> 
> group :development do
>   # Speed up commands on slow machines / big apps [https://github.com/rails/spring]
>   # gem "spring"
> end

 - Gemfile is as follows.

**Hopes it sufices**
St4niuu commented 8 months ago

Ok, I got it working. There was a problem with id type of my employee model. It was a string, I had to change the schema accordingly.