RDSの"リードレプリカのマスター昇格機能"を使ってDDLオペレーションを実行する方法
AWSのRDSに、
ついにリードレプリカをマスターに昇格する機能が追加されました。
【AWS発表】Amazon RDS for MySQL - リードレプリカのマスター昇格機能を追加!
さて何故(なにゆえ)にこのような機能があるのかというと、
以下3つのケースが同記事にて紹介されています。
- DDLオペレーションの実行
- テーブルのシャーディング
- 障害回復機能を実装する
"障害回復機能を実装する"ケースは、マスターで障害が発生した際に、
リードレプリカをマスターに切り替えることで復旧させる方法です。
既存のデータ復旧機能である"Multi-AZ 配備"に比べて良い点は、
非同期なので書き込みにおけるパフォーマンス劣化が防げたり、
ホットスタンバイとして寝かしておくのではなく
リードレプリカとして読み込み用として利用しておけたりする事でしょうか。
もちろん、同記事にあるようにこれは"非同期"のため、
障害時にデータの整合性が失われる可能性があるので、注意が必要です。
"テーブルのシャーディング"は、
同時多発的にマスター用途のインスタンスを作成するってところですかね。
さて肝心の"DDLオペレーションの実行"です。
そもそも何が問題なの?というと、
サービスやアプリケーションで利用中のテーブルに対して
ADD COLUMNやADD INDEX等のテーブルスキーマの変更を行うと、
該当のテーブルはロックされるため後続のクエリ全てがwait状態になること、
また実行中はある程度のマシンリソースを使うことにより、
稼働に影響を及ぼす可能性があるためです。
特にレコード数の多い大きなテーブルでは顕著に現れます。
サイズ、マシンリソースによってはひとつの変更に
数十分、数時間かかる可能性もあるでしょう。
そこで昇格機能を使うことで、
事前に伝搬中のリードレプリカ上でスキーマ変更を行い、
更新を一時的に止め、リードレプリカをマスターに昇格させ、
アプリケーションの向き先を昇格したマスターへ切り替える、
そんなことが可能なのです。
これなら昇格にかかる時間(数分)と
アプリケーションの向き先変更時間だけで、
数時間もかかる作業を行うことができるのです。
まず下準備です。
RDSのコンソール画面のNavigationにあるDB Parameter Groupsから、
新しいパラメータセットを作ります。
リードレプリカに対して変更処理を行うため、
デフォルトのパラメータで設定されている
書き込み制限が外れたセットが必要なためです。
適当な名前を付けて作成しておきます。
Edit Parametersから必要な値の変更をします。項目はread_onlyです。
元々は自動でリードレプリカの場合は読み込み専用になるようになっています。
これを0にしておきます。
インスタンス一覧に戻って、
Instance Actionsからリードレプリカを作成します。
作成できたら、リードレプリカを選択した上でActionsからModifyを選択し、
パラメータの設定セットを先ほど作った書き込みが可能な
DDLオペレーション用のものに変えます。
再起動が完了したら、
早速リードレプリカに対してスキーマ変更を実施しましょう。
サービスで利用していないのでどんな負荷がかかろうが問題ないですね。
変更中は伝搬が遅延する可能性がありますが、
変更中も変更後も問題なく伝搬は続行しているはずです。
これで準備は整いました。
あとは更新を一時的に停止し、スキーマ変更済みのリードレプリカを
マスターに昇格しましょう。
その後アプリケーションの向き先を新マスターに切り替えるだけです。
あっという間にスキーマ変更済みのDBへと切り替えることができました。
実は書き込み可能なリードレプリカにしているのでマスターに昇格しなくても、
向き先を変更してそのまま利用することが可能です。
しかしバックアップ等はマスターにしかない機能なので、
必ずこのタイミングで昇格した方が無難ですね。
今の一連の作業はもちろんAPI経由でも実施可能なので、
Rails等のマイグレーションのタスクに組み込んでしまえば、
rake db:migrate
こんなコマンド一発で
スキーマ変更からDSN切り替えまでやれちゃうかもですねぇ、、
末恐ろしきRDS、、!
AWSのRDSは外部のMySQLとレプリケーションできない -マスターユーザの権限を見て分かったこと-
※ 2013/09/08 added
ついにレプリケーションの機能が付いたようです!
hereAmazon Web Services ブログ: 【AWS発表】 オンプレミスのMySQLデータをAmazon RDSへ移行する(その逆も)
RDSのインスタンスを新規に作成する際に
合わせて作成するマスターユーザの権限を見てみた。
マスターユーザというのは、
ココでMaster Usernameとしていれたもの。
権限が、以下のような感じ。
Host: % User: root Password: *<HASHPASSWORD> Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: N Process_priv: Y File_priv: N Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: N Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: N Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: N ssl_type: ssl_cipher: x509_issuer: x509_subject: max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: authentication_string:
サーバそのものに影響を及ぼすようなFILE権限であったり、
SHUTDOWN権限がなく、
MySQL接続によるインスタンス停止ができなくなっていたりする。
※コンソール画面やAPIからはもちろん停止可能。
さらにSUPER権限がないため、
外部のMySQL(例えばec2上のMySQLやVPC先の自前サーバ上のMySQLなど)
とのレプリケーション設定である
mysql> CHANGE MASTER TO …
上記のようなCHANGE MASTER文を発行出来ない。
つまり、外部MySQLとのスレーブとしてのRDS利用は不可能だ。
さらに、Repl_slave_privもないことから、
外部からのレプリケーション利用のためのユーザも作成できないので、
外部MySQLとのマスターとしてのRDS利用も無理。
RDSには読み出し専用のスレーブ”リードレプリカ”を作成する機能があるので、
そちらを利用しておくんなましってことだろう。
とはいえ、外部とのレプリケーションが実現出来たとしても、
サーバに入って、バイナリーログ等をいじいじいじくれないのなら、
おそらくレプリケーション運用は困難を極めること必死だ。
※ほんとはRDSへの移設にレプリケーションを利用したかったのだけどね。
ちなみにマスターユーザはCreate user権限もGrant権限もあるので
新規にユーザを作成することは可能だけど、
Grantは自身の持つ権限しか付与できないので、
上記の制約はどんな後発のユーザでも当てはまる。
**
あと全然関係ないけど、
rdsadmin@localhostというALL PRIVILEGESを有したユーザがいるのだが、
※おそらくセッション数等を取得してる監視などのためのユーザ。
なぜか、
mysql> drop user rdsadmin; ERROR 1396 (HY000): Operation DROP USER failed for 'rdsadmin'@'%'
なぜかこの人を消し去れない。。
マスターユーザにはCreate user権限があるので可能な気がするんだけど。。
いや、もちろん消し去る必要はないんだけど。。
どうやってんのかなって。。
まあどうでもいいことなんだけど。。
OmniAuth(OAuth2.0)でproxyを使う方法
YammerのOAuthで認証するアプリが作りたいんだけど、
proxyが必要なネットワーク内でかつ、
環境変数の”http_proxy”が使えないときのメモ。
※そんなマニアックスな人が世の中にどれだけいるか甚だ疑問ですが、、
結果的に、
Rails.application.config.middleware.use OmniAuth::Builder do provider :yammer , ENV["YAMMER_CONSUMER_KEY"], ENV["YAMMER_CONSUMER_SECRET"], :client_options => {:connection_opts => { :proxy => "http://proxy.sample.com:80" }} end
こんな感じでいけました。
deviseだと、
config/initializers/devise.rb
config.omniauth :yammer, ENV["YAMMER_CONSUMER_KEY"], ENV["YAMMER_CONSUMER_SECRET"], :client_options => {:connection_opts => { :proxy => "http://proxy.sample.com:80" }}
こんな感じです。
※OAuth2.0仕様です。
仕組みとしては、
各OAuthプロバイダのライブラリ(ここではomniauth-yammer)が、
omniauth-oauth2ライブラリのOAuth2クラスを継承していて、
そこからoauth2ライブラリの接続用Clientクラスを生成するのですが、
その際に引き渡すオプション名がclient_optionsとなっていて、
さらに、
そのClientクラスがhttpクライアントライブラリである
faradayのインスタンスを生成する際に引き渡すオプション名がconnection_optsという顛末でした。
とりあえず通って良かったね!
Macbook Air 2012(mid) にvim-rubyを入れた
Macbook Air 2012(mid) が手に入ったので、いつものごとくvim-rubyを入れました。
mba:~ kazuki.yunoue$ vi --version | grep ruby -python3 +quickfix +reltime -rightleft +ruby/dyn +scrollbind +signs
あら、対応してる。最近から?
以前の通り、
git clone http://github.com/vim-ruby/vim-ruby.git cd vim-ruby git checkout vim7.3 rake package
直接ソースからgemのパッケージを作成してinstallしようとすると、
mba:vim-ruby kazuki.yunoue$ rake package rake aborted! ERROR: 'rake/gempackagetask' is obsolete and no longer supported. Use 'rubygems/package_task' instead.
rakeのバージョンとの親和性が良くないのか怒られる。
仕方がないので、ココのpathogen.vim方法と思わせてManuallyに、
https://github.com/vim-ruby/vim-ruby/wiki/VimRubySupportA
./bin/vim-ruby-install.rb
これで~/.vim/下に必要なファイルがコピーされるので、
vi ~/.vimrc
に、
set tabstop=2 set shiftwidth=2 set expandtab set autoindent set nocompatible syntax on filetype on filetype indent on filetype plugin on
とか書いてしまえば、完了だ!
ActiveRecordモデルの独自で作成したメソッドもjson(もしくはxml)で出力したい
シチュエーション的には、
class Book < ActiveRecord::Base attr_accessible :title, :author, :price def price_with_tax return self.price*1.05 end end
こんな感じでモデルに独自のメソッド(price_with_tax)がある場合、
# GET /books/1 # GET /books/1.json def show @book = Book.find(params[:id]) respond_to do |format| format.html # show.html.erb format.json { render json: @book} end end
上記のような自動で生成されるコントローラーでは、
http://localhost:3001/books/1.json
{"author":"aaa","created_at":"2013-01-04T07:49:50Z","id":1,"price":1000,"title":"aaa","updated_at":"2013-01-04T07:49:50Z"}
jsonに独自メソッドは出てきません。
そこでコントローラーを、
# GET /books/1 # GET /books/1.json def show @book = Book.find(params[:id]) respond_to do |format| format.html # show.html.erb format.json { render json: @book, :methods => [:price_with_tax]} end end
上記のようにjsonないしxmlに:methodsオプションで該当の独自メソッドを追加してあげると、
{"author":"aaa","created_at":"2013-01-04T07:49:50Z","id":1,"price":1000,"title":"aaa","updated_at":"2013-01-04T07:49:50Z","price_with_tax":1050.0}
出てきた!
これはActiveRecordのto_jsonないしto_xmlメソッドの、
オプションですので、
1.9.3-p194 :001 > book=Book.find(1) Book Load (5.7ms) SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT 1 [["id", 1]] => #<Book id: 1, title: "aaa", author: "aaa", price: 1000, created_at: "2013-01-04 07:49:50", updated_at: "2013-01-04 07:49:50"> 1.9.3-p194 :003 > book.to_json => "{\"author\":\"aaa\",\"created_at\":\"2013-01-04T07:49:50Z\",\"id\":1,\"price\":1000,\"title\":\"aaa\",\"updated_at\":\"2013-01-04T07:49:50Z\"}" 1.9.3-p194 :002 > book.to_json :methods => [:price_with_tax] => "{\"author\":\"aaa\",\"created_at\":\"2013-01-04T07:49:50Z\",\"id\":1,\"price\":1000,\"title\":\"aaa\",\"updated_at\":\"2013-01-04T07:49:50Z\",\"price_with_tax\":1050.0}" 1.9.3-p194 :004 >
このようにも可能!です!見にくっ!
CoffeeScriptでsetTimeoutを書く
超ピンポイントトピックですが、迷ったので、、
setTimeout ->
console.log("ok")
, 1000
改行+インデント揃えてからのカンマ+ミリ秒だそうですー。
ありがとうございます!