Linodeでタイムゾーンが定期的にリセットされる件
おそらくLinodeでFedoraを使う時の問題。
LinodeでFedoraをインストールすると、デフォルトのタイムゾーンがESTになるので、
ローカルのタイムゾーンとしてJSTを設定します。
cp /usr/share/zoneinfo/Japan /etc/localtime
ただしこれだけだと、なにかタイミングでLinode初期値のESTにリセットされてしまう。
原因は不明だが定期的にリセットされているもより。
追加で以下のような設定を追加する必要があるっぽい。
/etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=false
ARC=false
grant all privileges on db_name.* で全ての権限が与えられない件
MySQLにはグローバル権限とデータベース権限があってはまったよというお話。
WEBアプリでDBを利用する場合、そのアプリ用のユーザとデータベースを作るという運用をしている。
アプリ用ユーザはアプリ用データベースへのみ全アクセスが可能となる。
GRANT文にするとこんな感じ。
GRANT all privileges ON pcyp_stats.* TO pcyp_stats@localhost IDENTIFIED BY PASSWORD '*DBC899EA52D209F038B442AE9379496CAFA8AB1B';
これで該当データベースに対しては全ての操作が可能かと思っていたが、不完全だった。
flush table を実行するとRELOAD権限がないといわれてしまう!
マニュアルを熟読したところMySQLの権限はグローバル権限とデータベース権限に分類でき、一部のコマンドはグローバル権限が必要ということらしい。
グローバル権限を付与するにはGRANT文のON構文を次のように指定する。
ON *.*
ON構文はデータベースのスコープを指定してるんだと思っていたよ。
MySQLのMergeテーブルとRailsのrakeタスクでログ蓄積テーブルを管理する
システムログのように、過去のエントリは更新されないが、日々追加され膨大な量を保存しておかなければならないデータを効果的に管理する方法
要件
・月ごとにテーブルを分ける
・過去のデータは圧縮しておく
・各月のデータに1つのテーブルから透過的にアクセスできるようにする
・ローテートの処理は無停止かつ自動で行う
実装
MySQLのMergeテーブルとRailsのrakeタスクを使って実装
■ テーブル構成
snapshots / Mergeテーブル
snapshots_base / ログの一時保存テーブル
snapshots_200906, snapshots_200907, … / 過去ログテーブル
■ 処理の流れ
・新着のログは全てsnapshots_baseに追加する
・月の終わりに当月の過去ログテーブルを作成し、snapshots_baseから過去ログテーブルにデータを移動する
・過去ログテーブルを圧縮する
・Mergeテーブルを再定義し、全ての過去ログテーブルにアクセス出来るようにする
コード
db/migrate/20090608060048_create_snapshots.rb
class CreateSnapshots < ActiveRecord::Migration
def self.up
###########################################################################
# MERGE TABLE
execute(<<-SQL)
CREATE TABLE `snapshots` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`revision` int(11) NOT NULL,
`key` varchar(128) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`contact` varchar(255) DEFAULT NULL,
`tag` varchar(255) DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`comment` varchar(255) DEFAULT NULL,
`listener` int(11) DEFAULT NULL,
`bitrate` int(11) DEFAULT NULL,
`format` varchar(255) DEFAULT NULL,
`broadcasted_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`yp` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_snapshots_on_yp_and_revision_and_key` (`yp`,`revision`,`key`),
KEY `index_snapshots_on_tag` (`tag`),
KEY `index_snapshots_on_revision` (`revision`)
) ENGINE=MRG_MyISAM UNION=(snapshots_base) INSERT_METHOD=LAST DEFAULT CHARSET=utf8;
SQL
###########################################################################
# SKELTON
execute(<<-SQL)
CREATE TABLE `snapshots_base` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`revision` int(11) NOT NULL,
`key` varchar(128) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`contact` varchar(255) DEFAULT NULL,
`tag` varchar(255) DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`comment` varchar(255) DEFAULT NULL,
`listener` int(11) DEFAULT NULL,
`bitrate` int(11) DEFAULT NULL,
`format` varchar(255) DEFAULT NULL,
`broadcasted_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`yp` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_snapshots_on_yp_and_revision_and_key` (`yp`,`revision`,`key`),
KEY `index_snapshots_on_tag` (`tag`),
KEY `index_snapshots_on_revision` (`revision`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
SQL
end
def self.down
drop_table :snapshots
drop_table :snapshots_base
end
end
lib/tasks/app.rake
namespace :app do
desc "freeze snapshots table."
task(:freeze_snapshots => :environment) do
YYYY, MM, DD = ENV['DATE'][0..3], ENV['DATE'][4..5], '01'
TARGET_DATE = "#{YYYY}-#{MM}-#{DD}"
MERGE_TABLE_NAME = 'snapshots'
BASE_TABLE_NAME = 'snapshots_base'
FREEZE_TABLE_NAME = "snapshots_#{YYYY}#{MM}"
FREEZE_TABLE_PATH = lambda {
datadir = ENV['DATADIR'].blank? ? '/var/lib/mysql' : ENV['DATADIR']
dbdir = ActiveRecord::Base.connection.current_database
datadir + '/' + dbdir + '/' + FREEZE_TABLE_NAME + '.MYI'
}.call
UNION_TABLES = lambda {
tables = Array.new
result = ActiveRecord::Base.connection.execute(<<-SQL)
show tables like 'snapshots_______'
SQL
result.each{|row| tables << row }
tables << FREEZE_TABLE_NAME << BASE_TABLE_NAME
tables.join(', ')
}.call
unless ENV['USER'] == 'root'
puts 'invalid user'
raise
end
unless Date.valid_date?(YYYY.to_i, MM.to_i, DD.to_i)
puts 'invalid date'
raise
end
ActiveRecord::Base.connection.execute(<<-SQL)
CREATE TABLE #{FREEZE_TABLE_NAME} LIKE snapshots_base;
SQL
ActiveRecord::Base.connection.execute(<<-SQL)
INSERT INTO #{FREEZE_TABLE_NAME}
SELECT * FROM #{BASE_TABLE_NAME}
WHERE created_at BETWEEN '#{TARGET_DATE}'
AND adddate('#{TARGET_DATE}', INTERVAL 1 MONTH)
;
SQL
ActiveRecord::Base.connection.execute(<<-SQL)
DELETE FROM #{BASE_TABLE_NAME}
WHERE created_at BETWEEN '#{TARGET_DATE}'
AND adddate('#{TARGET_DATE}', INTERVAL 1 MONTH)
;
SQL
ActiveRecord::Base.connection.execute(<<-SQL)
FLUSH TABLES;
SQL
system "myisampack #{FREEZE_TABLE_PATH}"
system "myisamchk -rq --analyze --sort-index #{FREEZE_TABLE_PATH}"
ActiveRecord::Base.connection.execute(<<-SQL)
ALTER TABLE #{MERGE_TABLE_NAME} UNION=(#{UNION_TABLES});
SQL
ActiveRecord::Base.connection.execute(<<-SQL)
OPTIMIZE TABLE #{BASE_TABLE_NAME};
SQL
end
end
実行コマンド例
sudo rake app:freeze_snapshots DATE=200906 RAILS_ENV=production
メモ
rakeタスクを作ったのは初めてだったのでメモ。
Railsの実行環境を設定する。
引数をハッシュにして、値に :environment を渡すだけでRailsの実行環境で動作するようになる。
task(:freeze_snapshots => :environment) do
rakeコマンドは引数も取れるが、環境変数経由でパラメータを渡す方法が主流っぽい?
YYYY, MM, DD = ENV['DATE'][0..3], ENV['DATE'][4..5], '01'
現在のワークフローの問題点
ワークフロー
- 企画担当者が要件を固める
- 企画担当者が要件を元に機能/仕様を企画書に落とし込む
- 企画書を顧客に提出し承認を得た後PGに渡す
- PGが機能/仕様の精査および企画担当者へのヒアリングを行い要件を導出する(企画担当者が固めた要件とは若干色合いが変わる場合が多い)
- PGが導出した要件を元に、PGが機能/仕様を検討する
- 顧客が了解した機能/仕様をベースにマージ可能な部分をPGが導出した機能/仕様で上書きする
- 出来上がったモノは顧客が了解した機能/仕様のほぼ100%、PGが導出した機能/仕様の30%程度を満たす
問題点
- 企画担当者の要件には隠れた要件が眠っている場合がある
- 企画担当者の機能/仕様は隠れた要件と矛盾する場合がある
- 顧客の承認が出るまでPGは要件/機能/仕様を知らない
- 顧客が了解した機能/仕様が覆る可能性はない
- 機能/仕様のマージ不可能な部分は生産性の低下という形でPGに振りかかるが、そのことが問題になることはまずない
RailsでRoutingErrorをログ出力しない
Passengerを使っているとアクセスログが別途Apacheのログに吐き出されるので、
Railsさんにはアプリケーションエラーだけをはいてもらいたいものです。
Railsでアクセスログっぽいログを吐かないようする方法は次の通り。
application_controller.rb に以下の記述を追加
protected
EXCEPTIONS_NOT_LOGGED = ['ActionController::UnknownAction', 'ActionController::RoutingError']
def log_error(exc)
super unless EXCEPTIONS_NOT_LOGGED.include?(exc.class.name)
end
IETester
概要
IE5.5, IE6, IE7, IE8のレンダリングエンジンを切り替えられるテストツール
サイト
Rails Searchable API Doc がパワーアップしていた
検索が使いやすいRails Searchable API DocがRubyにも対応していた!
Rails Searchable API Doc
Ruby Searchable API Doc
Authlogic, Haml, Hpricot, RSpec などの周辺ライブラリにも対応
公式サイトで表示するパッケージをカスタマイズできます
なぜかトップページが表示できませんが、railsapiでググってキャッシュから表示すればいけました
Command Line Snippet
文字列置換
# カレントディレクトリから拡張子がphpのファイルを全てリストアップし、「cohakim.wordpress.com」を「cohakim10.wordpress.com」に一括置換する
$ find . -name '*.php' -exec grep -l "cohakim.wordpress.com" {} \; | xargs sed -i.bak 's/cohakim.wordpress.com/cohakim2010.wordpress.com/g'
文字コード置換
# 拡張子が「.thtml」のファイルをリストアップし、文字コードをUTF-8に変換する
find -name '*.thtml' | xargs nkf --overwrite -w
最近更新されたファイルを見つける
# 過去1日に更新されたファイルをリストアップし、ファイルの詳細情報を表示する
find -mtime -1 -type f -exec ls -l {} \;
VMware Fusion 3 Upgrade のプロモクーポンがあった
promo code
dsktp20sale
入れたら$8くらいやすくなった
PitとConfigatronの連携が想像以上に便利だった件
Pitを使うとパスワードを別ファイルに分離できて便利ですが、読み込んだデータをハッシュで返すので階層が深いデータを定義するとマージするときにちょっと不便だったりします
configatronはハッシュのツリー構造をうまいことラップしてくれるので、設定をよりDRYに書くことができます
config/config.yml にベースとなる設定を記述します。
パスワードなど共有したくない設定はエントリのみ用意して、値はPitで上書きします。
config/config.yml
development:
twitter:
cp:
login: 'pcyp_cp'
passwd: <your twitter password>
Pitに上書きする項目を記述します。
~/.pit/contactor.yaml
twitter:
cp:
passwd: 'xxxxx'
config/config.ymlを読み込んだ後、Pitで差分の設定を読み込み、ベースの設定にマージします。
config/initializer/load_config.rb
configatron.configure_from_yaml("#{RAILS_ROOT}/config/config.yml", :hash => Rails.env)
Pit.switch('contactor')
configatron.configure_from_hash(:twitter=>Pit.get("twitter"))
結果
p configatron
configatron.twitter.cp.login = "pcyp_cp"
configatron.twitter.cp.passwd = "xxxxx"
きれいにマージされていますね!
capistranoをアップデートしたらhookの書き方が変わっていた件
before
task :after_symlink do
invoke_command "chmod 744 -R #{current_path}/script"
end
after
after 'deploy:symlink' do
invoke_command "chmod 744 -R #{current_path}/script"
end
fedora 12 で Ruby Enterprise Edition をコンパイルする
概要
メモリ使用量を結構削減できるという Ruby Enterprise Edition
なんと\オープンソース!/
1.8.7ベースのものが最近公開されたというのでインストールしてみる
ソースファイルの取得
ここからソースをダウンロードして、適当なディレクトリに展開する
Ruby Enterprise Edition
patchを当てる(fedora 12の場合)
fedora 12 だと Ruby のコンパイルでこけるのでpatchを当てる
http://redmine.ruby-lang.org/issues/show/2022
patchの当て方を知らなかったので見よう見まねで頑張った!
下記のようなdiffの結果を適当なファイル名で保存(ruby-enterprise-1.8.7-2009.10/ext/openssl/ossl.c.patch)
Index: ext/openssl/ossl.c
===================================================================
--- ext/openssl/ossl.c (revision 24721)
+++ ext/openssl/ossl.c (working copy)
@@ -93,5 +93,5 @@ ossl_x509_ary2sk(VALUE ary)
#define OSSL_IMPL_SK2ARY(name, type) \
VALUE \
-ossl_##name##_sk2ary(STACK *sk) \
+ossl_##name##_sk2ary(STACK_OF(type) *sk) \
{ \
type *t; \
@@ -103,5 +103,5 @@ ossl_##name##_sk2ary(STACK *sk) \
return Qnil; \
} \
- num = sk_num(sk); \
+ num = sk_##type##_num(sk); \
if (num < 0) { \
OSSL_Debug("items in sk < -1???"); \
@@ -111,5 +111,5 @@ ossl_##name##_sk2ary(STACK *sk) \
\
for (i=0; i<num; i++) { \
- t = (type *)sk_value(sk, i); \
+ t = sk_##type##_value(sk, i); \
rb_ary_push(ary, ossl_##name##_new(t)); \
} \
(以下略)
patch というコマンドにファイルを渡せば勝手にマージしてくれるぽい
# patch < ext/openssl/ossl.c.patch
なんか一部当たらないっぽいけど動いたので放置
インストーラの実行
あとはインストーラを実行すれば勝手にインストールしてくれる
デフォルトだと /opt/ruby-enterprise-1.8.7-2009.10/ にインストールされる
# ruby ruby-enterprise-1.8.7-2009.10/installer.rb
既存環境の置き換え
gem list で既存のインストール済みgemを取得しておく
# gem list
*** LOCAL GEMS ***
actionmailer (2.3.4)
actionpack (2.3.4)
activerecord (2.3.4)
(以下略)
/usr/bin/ruby, /usr/bin/gem にシンボリックリンクをはる
既存のものは適当な名前にリネームしておく
# mv /usr/bin/ruby /usr/bin/ruby186
# mv /usr/bin/gem /usr/bin/gem186
# ln -s /opt/ruby-enterprise-1.8.7-2009.10/bin/ruby /usr/bin/ruby
# ln -s /opt/ruby-enterprise-1.8.7-2009.10/bin/gem /usr/bin/gem
installer.rb で指示されたとおり、httpd.conf の mod_passenger の設定を書き換える
root@localhost# cat /etc/httpd/conf.d/passenger.conf
LoadModule passenger_module /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7/ext/apache2/mod_passenger.so
PassengerRoot /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7
PassengerRuby /opt/ruby-enterprise-1.8.7-2009.10/bin/ruby
結果
ruby 1.8.6
---- Passenger processes -----
PID VMSize Private Name
------------------------------
1928 11.2 MB 0.3 MB /usr/lib/ruby/gems/1.8/gems/passenger-2.2.5/ext/apache2/ApplicationPoolServerExecutable 0 /usr/lib/ruby/gems/1.8/gems/passenger-2.2.5/bin/passenger-spawn-server /usr/bin/ruby /tmp/passenger.1895
1929 17.1 MB 5.3 MB Passenger spawn server
12178 48.0 MB 29.6 MB Passenger ApplicationSpawner: /var/www/app/contactor/current
### Processes: 3
### Total private dirty RSS: 35.22 MB
ruby enterprise edition 1.8.7
---- Passenger processes -----
PID VMSize Private Name
------------------------------
11783 6.9 MB 0.2 MB /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7/ext/apache2/ApplicationPoolServerExecutable 0 /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7/bin/passenger-spawn-server /opt/ruby-enterprise-1.8.7-2009.10/bin/ruby /tmp/passenger.11755
11784 23.7 MB 6.3 MB Passenger spawn server
12065 56.2 MB 2.3 MB Passenger ApplicationSpawner: /var/www/app/contactor/current
12095 56.2 MB 3.2 MB Rails: /var/www/app/contactor/current
### Processes: 4
### Total private dirty RSS: 12.04 MB
環境が違うので比較できないけれど、実メモリ使用量は大分減ったぽい
passengerのメモリ使用量を調べる
コマンド一発で調べられるっぽい
# passenger-memory-stats
---- Passenger processes -----
PID VMSize Private Name
------------------------------
11783 6.9 MB 0.2 MB /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7/ext/apache2/ApplicationPoolServerExecutable 0 /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.7/bin/passenger-spawn-server /opt/ruby-enterprise-1.8.7-2009.10/bin/ruby /tmp/passenger.11755
11784 23.7 MB 6.3 MB Passenger spawn server
12065 56.2 MB 2.3 MB Passenger ApplicationSpawner: /var/www/app/contactor/current
12095 56.2 MB 3.2 MB Rails: /var/www/app/contactor/current
### Processes: 4
### Total private dirty RSS: 12.04 MB
gemのリポジトリにgemcutterを加える
githubがgemを配信してくれなくなるので、最近はgemcutterを使うらしい
$ gem sources -a http://gemcutter.org/
Stocksで日経平均とドル/円為替レートを表示する
Stocks とは、Mac OS X の dashboard に標準でついてくる 株価表示 widget
個別の株価だけでなく、各国の主要な株価指数や為替レートが表示可能
iPhone 版も同様
| ^DJI | ダウ平均 |
| ^225 | 日経平均 |
| ^KS11 | KOSPI |
| USDJPY=X | ドル/円 |
| EURJPY=X | ユーロ/円 |
| USDKRW=X | ドル/ウォン |