I have a webapp that lets users rate movies. Any time a user searches for movies, data for up to 20 movies is fetched from an external api and sent to my Rails backend, where it checks to see if that movie already exists in the database. If it is there, it returns the movie info. If it is not yet in the database, it first adds it before returning the info.
This works fine for a while, but for some reason I do not understand, eventually the movies in the movies in the database start acting strangely, one by one they do not show up on the webapp and give me error messages in my Rails logs. If I change find_or_create_by! to find_or_create_by (no space) in the controller I don't get the error message in the logs, and most of the movie shows up correctly, but its :id (not api_id), and thus, ratings and users associations are not returned.
I thought that maybe duplicate entries were getting created despite my validations but when I check the effected movie in question in the Rails console with Movie.where(api_id: 269) I only get one entry, so that does not seem to be the case.
I'm having trouble deciphering the logs to figure out why this happen in the first place, one by one to movies in the database. The only way I have found to get things back to working correctly is to destroy all the movies in the movie table, but this also means I have to destroy all the ratings as well. I'd like to figure out exactly what is happening to make mess the entries up, making them unusable.
My controller:
class Api::V1::MoviesController < ApplicationController
def index
movies = Movie.all
render json: movies
end
def show
movie = Movie.find(params[:id])
render json: movie
end
def create
movie = Movie.find_or_create_by!(movie_params)
render json: movie
end
private
def movie_params
params.require(:movie).permit(:title, :poster_path, :overview, :api_id, :release_date)
end
end
My class:
class Movie < ApplicationRecord
has_many :ratings
has_many :users, through: :ratings
validates :api_id, uniqueness: true
end
Serializer:
class MovieSerializer < ActiveModel::Serializer
attributes :id, :title, :poster_path, :api_id, :overview, :release_date
has_many :ratings
has_many :users, through: :ratings
end
Schema:
create_table "movies", force: :cascade do |t|
t.string "title"
t.string "poster_path"
t.text "overview"
t.integer "api_id"
t.string "release_date"
t.index ["api_id"], name: "index_movies_on_api_id", unique: true
end
Log of a movie not rendering successfully:
Started POST "/api/v1/movies"
Processing by Api::V1::MoviesController#create as JSON
Parameters: {"api_id"=>269, "title"=>"Breathless", "poster_path"=>"/9Wx0Wdn2EOqeCZU4SP6tlS3LOml.jpg", "overview"=>"A young car thief kills a policeman and tries to persuade a girl to hide in Italy with him.", "release_date"=>"1960-03-16", "movie"=>{"title"=>"Breathless", "poster_path"=>"/9Wx0Wdn2EOqeCZU4SP6tlS3LOml.jpg", "overview"=>"A young car thief kills a policeman and tries to persuade a girl to hide in Italy with him.", "api_id"=>269, "release_date"=>"1960-03-16"}}
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Movie Load (2.3ms) SELECT "movies".* FROM "movies" WHERE "movies"."title" = $1 AND "movies"."poster_path" = $2 AND "movies"."overview" = $3 AND "movies"."api_id" = $4 AND "movies"."release_date" = $5 LIMIT $6 [["title", "Breathless"], ["poster_path", "/9Wx0Wdn2EOqeCZU4SP6tlS3LOml.jpg"], ["overview", "A young car thief kills a policeman and tries to persuade a girl to hide in Italy with him."], ["api_id", 269], ["release_date", "1960-03-16"], ["LIMIT", 1]]
(1.9ms) BEGIN
Movie Exists? (1.5ms) SELECT 1 AS one FROM "movies" WHERE "movies"."api_id" = $1 LIMIT $2 [["api_id", 269], ["LIMIT", 1]]
(2.7ms) ROLLBACK
Completed 422 Unprocessable Entity in 65ms (ActiveRecord: 9.6ms | Allocations: 10309)
FATAL -- : [8dce0090-dd46-4fc6-8370-08902fa5e13c]
app/controllers/api/v1/movies_controller.rb:14:in `create'
Log of a movie rendering properly:
Started POST "/api/v1/movies" for 67.86.25.53 at 2021-05-04 21:02:22 +0000
Processing by Api::V1::MoviesController#create as JSON
Parameters: {"api_id"=>26317, "title"=>"Man with a Movie Camera", "poster_path"=>"/Ded5Kl7MDZQTbFpoNI62tLVWUN.jpg", "overview"=>"A cameraman wanders around Moscow, Kharkov, Kiev and Odessa with a camera slung over his shoulder, documenting urban life with dazzling invention.", "release_date"=>"1929-01-08", "movie"=>{"title"=>"Man with a Movie Camera", "poster_path"=>"/Ded5Kl7MDZQTbFpoNI62tLVWUN.jpg", "overview"=>"A cameraman wanders around Moscow, Kharkov, Kiev and Odessa with a camera slung over his shoulder, documenting urban life with dazzling invention.", "api_id"=>26317, "release_date"=>"1929-01-08"}}
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Movie Load (1.3ms) SELECT "movies".* FROM "movies" WHERE "movies"."title" = $1 AND "movies"."poster_path" = $2 AND "movies"."overview" = $3 AND "movies"."api_id" = $4 AND "movies"."release_date" = $5 LIMIT $6 [["title", "Man with a Movie Camera"], ["poster_path", "/Ded5Kl7MDZQTbFpoNI62tLVWUN.jpg"], ["overview", "A cameraman wanders around Moscow, Kharkov, Kiev and Odessa with a camera slung over his shoulder, documenting urban life with dazzling invention."], ["api_id", 26317], ["release_date", "1929-01-08"], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
[active_model_serializers] Rating Load (1.4ms) SELECT "ratings".* FROM "ratings" WHERE "ratings"."movie_id" = $1 [["movie_id", 3745]]
[active_model_serializers] User Load (1.3ms) SELECT "users".* FROM "users" INNER JOIN "ratings" ON "users"."id" = "ratings"."user_id" WHERE "ratings"."movie_id" = $1 [["movie_id", 3745]]
[active_model_serializers] Rendered MovieSerializer with ActiveModelSerializers::Adapter::Attributes (6.75ms)
Completed 200 OK in 16ms (Views: 5.6ms | ActiveRecord: 5.4ms | Allocations: 2946)
movie_params, some of them (:title, :poster_path, :overview, :api_id, :release_date) might be giving you troubles.