What is the correct way to seed a rails app in docker so it can be started repeatedly

Viewed 1028

I have a rails app with mysql as the DB, that I'm trying to dockerize, so I can eventually deploy it with docker-machine. When I run docker-compose up the first time, it initializes correctly and seeds the database, like it's supposed to. However, after I shut it down, the second time I run docker-compose up, it fails with the following error, because the seed content is already present:

web_1     | ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry 'antun@example.com' for key 'index_users_on_email'

In my docker-compose.yml file, I'm doing the following under the web service:

command: /bin/sh -c "bin/wait-for db:3306 -- rm -f /home/ubuntu/MY_APP/tmp/pids/server.pid && bundle exec rake db:create db:migrate db:seed && rails server puma -p 80"

I'm using the ! method approach in db/seeds.rb, since I understand that's considered a best-practice:

u = User.new(
  {email: "antun@example.com", encrypted_password: "XXXXX", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, first_name: "Antun", last_name: "LAST_NAME", provider: "facebook", uid: "12345", username: "antun"}
) 
u.save!(validate: false)

I know I could skip the ! in u.save!() but as I said, I understand it's a best practice to use it in the seeds file, so that it fails. My question is: what is the correct, best-practice approach for creating and seeding a DB only on first run with docker-compose? Once it's live, I want to make sure that subsequent updates to the app (which may include migrations/additional seeds for new data types) work smoothly, without erasing data that's in the database.

Here's the full docker-compose.yml file:

docker-compose.yml

    version: '3.1'
    services:
      db:
        image: mysql:5.6
        command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --init-connect='SET NAMES UTF8;' --innodb-flush-log-at-trx-commit=0
        environment:
          MYSQL_ROOT_PASSWORD: XXXXX
          MYSQL_DATABASE: XXXXX
          MYSQL_USER: XXXXX
          MYSQL_PASSWORD: XXXXX
      web:
        build: .
        command: /bin/sh -c "bin/wait-for db:3306 -- rm -f /home/ubuntu/MY_APP/tmp/pids/server.pid && bundle exec rake db:create db:migrate db:seed && rails server puma -p 80"
        volumes: 
          - $PWD:/MY_APP
        ports:
          - "80:80"
        links:
          - "db:database" 
        env_file:
          - .env.production
        depends_on:
          - db
      worker:
        build: .
        command: /bin/sh -c "bundle exec bin/delayed_job -n 1 --log-dir=/home/ubuntu/MY_APP/shared/log/delayed_job.log --pool='notifications-poller:1' --pool='broadcast,default,elasticsearch,firebase:2' restart && bundle exec shoryuken --logfile '/home/ubuntu/MY_APP/shared/log/shoryuken.log' --config '/home/ubuntu/MY_APP/config/shoryuken_staging.yml' -R"
        links:
          - "db:database"
        volumes:
          - $PWD:/MY_APP
          - '/home/ubuntu/MY_APP/node_modules'
        env_file:
          - .env.production
        depends_on:
          - db
1 Answers

Try with ActiveRecord::Relation#find_or_create_by instead:

User.find_or_create_by(email: "antun@example.com", encrypted_password: "XXXXX", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, first_name: "Antun", last_name: "LAST_NAME", provider: "facebook", uid: "12345", username: "antun")

Answering to your comment; I'm afraid the repository is already unmaintained, but you can use the branch I just made for this.

If you see the changes, you're able to do:

SeedDump.dump(ModelName, method: :find_or_create_by)
# "ModelName.find_or_create_by([\n  {name: \"<name>\", status: nil},\n...)\n"

Answering to your last comment and your question:

What is the correct way to seed a rails app in docker so it can be started repeatedly

I don't know if it's the correct, but an easy one; as you're using MySQL just use the tools it provides. You can dump and import the data from any environment, that'll keep the integrity of your data, which might be faster and less error-prone.

Related