Ruby on Rails + legacy_migrations:2つのプロジェクト間の一方向のデータ同期

この記事は、Ruby on Rails、 legacy_migrationsヘム、および比較的ストレートなハンドを使用した2つのプロジェクトのデータベースでの自動一方向データ同期-些細な問題の解決策を説明することを目的としています。



初期状況



深刻なリファクタリングを行わずに3年以上にわたっていくつかの段階で記述されたロード済みプロジェクトがあります。そのため、コードが膨張し、使用されているテクノロジーがかなり古くなっています。 新しいものすべてについて、プロジェクトを最初から書き直すことにしました。





古いプロジェクト:
新しいプロジェクト:


主な難点は、データベースコンテンツと、新しいプロジェクトの古いデータベースアーキテクチャから移行する機能との同期です。 多くのオプションが検討されましたが、最終的に選択されたものは、レコードIDを保存し、レコードタイムスタンプを保存し、新しいオプションを追加し、既存のものを更新する自動コンテンツ同期システムを作成しました。 そして、これらすべてにより、既存のデータベースのスキーマを厳密にコピーする必要はありません



宝石legacy_migrations



半完成したソリューションを検索するプロセスで、Rails 2.3.xの時代に書き戻された便利なgemが見つかりました(ror2ruグループの助けなしではありません)。 良いスタートでしたが、テストプロセス中に重大な欠点が発見されました。

要素のIDを保持する理由に焦点を当てたいと思います。次の方法があります-old_idのような属性を作成し、その新しいIDに再バインドします。 ただし、タスクは新しいプロジェクトを作成するだけでなく、古いプロジェクトを新しいプロジェクトに置き換えることであり、これは少なくともすべてのURLのIDを意味します。 これがどれほど重要かを理解するには、路上でSEOスペシャリストを捕まえて、作業中のプロジェクトのURLをどのように変更したいかについて話し合うだけで十分です。 明白な反応が続きます。それは、失神から精神病まで、さまざまな形で現れます。

特定された欠点を解消するために、このフォークを作成しました。 これが何であるかをよりよく理解するために、私はあなたがここに従うことができると書きました

ARモデルを介してデータを転送する方法には1つの重大な欠点があります-パフォーマンスが低いことに注意してくださいが、私の場合、データベースは比較的小さく(合計約400 MB)、サーバーはこのアプローチを放棄しないほど強力です。



転送プロセス



そもそも、古いプロジェクトを移動する(そしてRailsバージョンを2.3.4から2.3.12に並行アップグレードする)とき、両方のプロジェクトのデータベースが同じサーバー上にあったのは幸運だったことに注意する必要があります-定期的な同期のためのより良い方法はありません。



必要なGemのインストール


まず、両方のDBMSのアダプターがGemfileに入力されていることを確認する必要があります。

gem 'mysql2' gem 'pg'
      
      





legacy_migrationsを設定するための2つのオプションがあります-fork(必要な変更を既に行っています)またはそれを手動で終了できる元のgem(両方のオプションについてGemfileの行をそれぞれ引用します):

 gem 'legacy_migrations', :git => 'git://github.com/Antiarchitect/legacy_migrations.git' gem 'legacy_migrations', :path => 'vendor/gems/legacy_migrations-0.3.7'
      
      





その後、自分でコードを変更し、gemがパスに表示されるようにするには、アプリケーションのルートでこのコマンドを実行する必要があります。

 gem unpack legacy_migrations --target vendor/gems
      
      







動作原理


legacy_migrationsの本質は次のとおりです。データを取得するプロジェクトとこれらのレコードをミラーリングするモデルがプロジェクトに存在する必要があります。 したがって、新しいプロジェクトのconfig / database.ymlは次のようになります。

 production: adapter: postgresql encoding: utf8 database: newapp_production username: postgres password: somecomplicatedpassword legacy: adapter: mysql2 encoding: utf8 database: oldapp_production username: root password: anothercomplicatedpassword
      
      





legacyは、古いプロジェクトのベースの構成です(名前は任意に選択できます)。

次に、古いプロジェクトのモデルを見て、さらに混乱を避けるために無料のプレフィックスを選択する必要があります。 私の場合、それは接頭辞「Old」でした。 その後、ディレクトリapp / models / oldを作成し、そこに抽象クラスを配置します。そこから他のすべてが継承されます。 サンプルアプリ/モデル/古い/ old_base.rb:

 class OldBase < ActiveRecord::Base self.abstract_class = true establish_connection 'legacy' end
      
      





引数 'legacy'はconfig / database.ymlの古いデータベースの設定グループの名前と一致する必要があります。 したがって、(ActiveRecord :: Baseから直接ではなく)OldBaseクラスから継承するすべてのモデルは、接続するデータベースを認識します。 以下は、そのようなモデルの例です。

 class OldNewsDoc < OldBase set_table_name 'news_docs' end
      
      





クラスには元々意図されていなかったプレフィックスがあるため、テーブルの名前を直接指定する必要があります。

app / models / oldからのクラスを自動的にロードするには、次のようにconfig / application.rbにこのパスを登録する必要があります。

 module NewApp class Application < Rails::Application ... config.autoload_paths += %W(#{config.root}/app/models/old) ... end end
      
      





そして、rakeタスクを作成するには、すべてが非常に単純に必要です。たとえば、次のようなものです(lib / tasks / legacy.rake):

 require 'legacy_migrations' namespace :legacy do namespace :transfer do desc 'Transfers News Docs from onru to onru2' task :news_docs => :environment do transfer_from OldNewsDoc, :to => NewsDoc do from :id, :to => :id from :updated_at, :to => :updated_at from :created_at, :to => :created_at from :news_rubric_id, :to => :news_rubric_id from :title, :to => :title from :annotation, :to => :annotation from :text, :to => :text end end end end
      
      





これで完了です。タスクは次のように起動できます。

 bundle exec rake legacy:transfer:news_docs RAILS_ENV=production
      
      





この著者の投稿で、読む価値のあるlegacy_migrationsの可能性について詳しく読んでください。



プロセス自動化



rakeタスクはすでに存在し、起動方法は10番目の問題であるため、可能なオプションがありますが、最も気に入ったプロジェクトのタスクを定期的に開始するオプションを提供したいと思います。



いつでも宝石


cronを介してアプリケーションのニーズに応じてタスクを自動的に起動するための非常に便利なgemがあります。これはCapistranoに簡単に統合でき、基本的なもの(rakeタスク、ランナースクリプト、コンソールコマンドなど)の起動を特定の運用環境に合わせて調整し、独自のタイプを作成できます実行されたタスク。

これを行うには、いつでもインストールする必要があります(Gemfileからの行):
 gem 'whenever', :require => false
      
      



アプリケーションルートからコマンドを実行する
 wheneverize .
      
      





そして、新しく作成されたconfig / schedule.rbファイルに以下を追加します。
 job_type :rake, "rvm use ree && cd :path && RAILS_ENV=:environment bundle exec rake :task :output" if environment == 'production' every :day, :at => '2am' do rake "legacy:transfer:news_docs" end end
      
      





私は自分の環境のrakeタスクの定義を書き直しました:カスタムインストールrvmとreeをrubyインタープリターとして使用します(Rails 3.1が安定したらすぐに1.9.2に切り替えます-いくつかの問題がありますが)、私もバンドラーを使用します。バンドルexecを実行します。

Capistranoにいつでも簡単に自然に統合できます(deploy.rb):

 require 'whenever/capistrano' ... set :whenever_command, "bundle exec whenever" #  ,   bundler -      whenever
      
      





展開後、crontabの美しくきれいな行を鑑賞できます。
 crontab -l
      
      





PSこの記事が、あるプロジェクトから別のプロジェクトにデータを転送するという同様の問題に直面している人々に役立つことを願っています。



Andrey Voronkov、 Evrone.com



All Articles