Datenmigrationen in Ruby on Rails

img



TL; DR Bitte verschieben Sie Ihren Datenmigrationscode in Rake-Aufgaben oder verwenden Sie vollwertige Edelsteine ​​im Schema-Stil. Decken Sie diese Logik mit Tests ab.



Ich arbeite als Backend-Entwickler bei FunBox. In einer Reihe von Projekten schreiben wir ein Ruby On Rails-Backend. Wir bemühen uns um angemessene Entwicklungsprozesse. Wenn wir mit einem Problem konfrontiert werden, versuchen wir, es zu verstehen und methodische Empfehlungen zu entwickeln. Dies geschah auch mit dem Problem der Datenmigration. Einmal habe ich die Datenmigration in einer separaten Rake-Aufgabe durchgeführt, die mit Tests behandelt wurde, und das Team hatte die Frage: "Warum nicht bei der Schemamigration?" Ich habe die Entwickler im internen Chat gefragt, und zu meiner großen Überraschung waren die Meinungen geteilt. Es wurde klar, dass die Frage mehrdeutig und einer sorgfältigen Analyse und eines Artikels würdig war. Das maximale Programm in Bezug auf die Ziele für einen Artikel wird für mich erfüllt, wenn jemand einen Link zu diesem Text in der Codeüberprüfung als Antwort auf die Frage zitiert, warum eine bestimmte Datenmigration herausgenommen oder im Gegenteil nicht aus der Schemamigration herausgenommen wurde.



Lyrischer Exkurs



, . . « . ». « ».



IT . Ruby, - . , , . , : , , , , , , .



, , . , , , , .





— , (views), , . .

— (, , , .) . . , . CI, , ( ) SQL , .

— . , DML- UPDATE SQL. . .

(Continuous Delivery) — , .



Rails , , DDL-. , . , Rails omakase- . , .





, , . , . , , . .





. , . (, ) , .





, . . -, , .



deadlocks .



, , , Zero Downtime Migrations Strong Migrations.







— DSL (Domain Specific Language) Ruby DDL- SQL . DSL, , . , .



DSL, , SRP. . , , …



( , )



Ruby On Rails Data Migration , . , . Rails-, . .





SQL ORM ActiveRecord.



:



  1. . .
  2. , .
  3. callbacks , .


«» . , .



«» Rails ( 4.2):



    # db/migrate/20100513121110_add_flag_to_product.rb

    class AddFlagToProduct < ActiveRecord::Migration
      class Product < ActiveRecord::Base
      end

      def change
        add_column :products, :flag, :boolean
        Product.reset_column_information
        Product.all.each do |product|
          product.update_attributes!(:flag => false)
        end
      end
    end


.



, each find_each c batch-.



, , 4.2 .



SQL



, , SQL, :



  1. , . , , , (SQL), .
  2. JOIN-, , .
  3. , deadlock.




Thoughtbot : -, DDL. CI. .



. , , - . , , . , .



, , . , .



, . , .



, , . , , , .



,



, .



, nullable- .



, .



, :



    UPDATE table SET field = 'f' WHERE field IS NULL


:



    class ClientDemandsMakeApprovedNullable < ActiveRecord::Migration
      def up
        change_column_null :client_demands, :approved, true
        change_column_default :client_demands, :approved, nil
      end

      def down
        execute("UPDATE client_demands SET approved = 'f' WHERE approved IS NULL")
        change_column_null :client_demands, :approved, false
        change_column_default :client_demands, :approved, false
      end
    end


, . , , . Dan Mayer Managing DB Schema & Data Changes Modifying Large Tables.







. «», . . , , , .



, .



, , .





, . . REPL .

, :



  1. ;
  2. ;
  3. .


. , . . .

, , , , .



Rake-



, — Rake-. . -.



Rake- . , . . . — .



, , Rake, Thoughtbot:



    # lib/tasks/temporary/users.rake
    namespace :users do
      desc "Actualize achievements counter cache"
      task actualize_achievements_counter_cache: :environment do
        # C (ActiveRelation)   
        users = User.with_achievements
        #    
        puts "Going to update #{users.count} users"
        # ,   ,  
        #    .    
        ActiveRecord::Base.transaction do
          # Batch-   find_each
          users.find_each do |user|        
            #     
            user.actualize_achievements_counter_cache!
            #  
            print "."
          end
        end

        puts "Done!"
      end
    end


each find_each, . memory bloats. Akshay Mohite.



. , Rake- .



, . . , , . .





Mark Qualie up, . «» . :



    class AddLastSmiledAtColumnToUsers < ActiveRecord::Migration[5.1]
      def change
        add_column :users, :last_smiled_at, :datetime
        add_index :users, :last_smiled_at
      end

      class Data
        def up
          User.all.find_in_batches(batch_size: 250).each do |group|
            ActiveRecord::Base.transaction do
              group.each do |user|
                user.last_smiled_at = user.smiles.last.created_at
                user.save if user.changed?
              end
            end
          end
        end
      end
    end


:



    Dir.glob("#{Rails.root}/db/migrate/*.rb").each { |file| require file }
    AddLastSmiledAtColumnToUsers::Data.new.up


Job, .





, - , .



, , .



, . , .



data-migrate (> 670), , Readme. Rails 5+.



, Rails 4+:





. .



, Rake-. . , .



db/data, db/migrate c :



rails g data_migration add_this_to_that


:



rake data:migrate
rake db:migrate:with_data
rake db:rollback:with_data
rake db:migrate:status:with_data


, .





  Rails Rake
+ - - - -
Zero Downtime Deployment - + + + +
Test First - - + +
+ - - +
+ - + + +
+ - - + +


.

— , :



  1. — ;
  2. Zero Downtime Deployment — , ;
  3. Test First — ;
  4. — ;
  5. — , ;
  6. — , , .




, Rake- — .



, . .



, — , . , .







UPD 2020-08-06: Extrapolator « ».

- , Test First .

.



UPD 2020-08-07: . , . . , .



UPD 2020-08-08: , . , . , , . « », , . . , .




All Articles