![](https://habrastorage.org/storage1/c38556da/5951caf5/acc48a4f/975f43d5.png)
初期状況
深刻なリファクタリングを行わずに3年以上にわたっていくつかの段階で記述されたロード済みプロジェクトがあります。そのため、コードが膨張し、使用されているテクノロジーがかなり古くなっています。 新しいものすべてについて、プロジェクトを最初から書き直すことにしました。
古いプロジェクト:
- Rails 2.3.4(後で2.3.12にアップグレード+バンドラーによる依存関係制御)
- MySQL 5
- Sphinx、遅延ジョブ、AR sendmailer、インターロック+キャッシング用のmemcached、その他は少しずつ
新しいプロジェクト:
- Rails 3.1.0.rc5(現在)
- Postgresql 8.4(おそらく9以降)
- ささいなことについて話すのはまだ早すぎますが、Solr、Redis、 resqueが想定されています
主な難点は、データベースコンテンツと、新しいプロジェクトの古いデータベースアーキテクチャから移行する機能との同期です。 多くのオプションが検討されましたが、最終的に選択されたものは、レコードIDを保存し、レコードタイムスタンプを保存し、新しいオプションを追加し、既存のものを更新する自動コンテンツ同期システムを作成しました。 そして、これらすべてにより、既存のデータベースのスキーマを厳密にコピーする必要はありません 。
宝石legacy_migrations
半完成したソリューションを検索するプロセスで、Rails 2.3.xの時代に書き戻された便利なgemが見つかりました(ror2ruグループの助けなしではありません)。 良いスタートでしたが、テストプロセス中に重大な欠点が発見されました。
- idレコードは保存されませんでした(彼はid = 1で始まる行にデータベース内のオブジェクトを積み重ねました)
- 一時レコードスタンプは保存されませんでした(updated_at、created_at)
- rakeタスクを再実行すると、テーブル内のデータが新しいIDで複製されました
特定された欠点を解消するために、このフォークを作成しました。 これが何であるかをよりよく理解するために、私はあなたがここに従うことができると書きました 。
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 。