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
カテゴリー:02.Article タグ:

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 AB :: MySQL 5.1 リファレンスマニュアル :: 12.5.1.3 GRANT 構文

カテゴリー:02.Article タグ:

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'
カテゴリー:02.Article タグ:

現在のワークフローの問題点

ワークフロー

  • 企画担当者が要件を固める
  • 企画担当者が要件を元に機能/仕様を企画書に落とし込む
  • 企画書を顧客に提出し承認を得た後PGに渡す
  • PGが機能/仕様の精査および企画担当者へのヒアリングを行い要件を導出する(企画担当者が固めた要件とは若干色合いが変わる場合が多い)
  • PGが導出した要件を元に、PGが機能/仕様を検討する
  • 顧客が了解した機能/仕様をベースにマージ可能な部分をPGが導出した機能/仕様で上書きする
  • 出来上がったモノは顧客が了解した機能/仕様のほぼ100%、PGが導出した機能/仕様の30%程度を満たす

問題点

  • 企画担当者の要件には隠れた要件が眠っている場合がある
  • 企画担当者の機能/仕様は隠れた要件と矛盾する場合がある
  • 顧客の承認が出るまでPGは要件/機能/仕様を知らない
  • 顧客が了解した機能/仕様が覆る可能性はない
  • 機能/仕様のマージ不可能な部分は生産性の低下という形でPGに振りかかるが、そのことが問題になることはまずない
カテゴリー:01.Diary

Detect Mobile Browser

概要

モバイルアクセスを判別するコードスニペットを吐いてくれるWebサービス

サイト

http://www.moongift.jp/2009/12/detect_mobile_browser/

カテゴリー:21.Library タグ:

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
カテゴリー:02.Article タグ:

nokogiri

概要

スクレイピング用HTML/XMLパーサ

リポジトリ

http://github.com/tenderlove/nokogiri

カテゴリー:21.Library タグ:

yav

概要

リアルタイム入力チェックライブラリ

リポジトリ

http://yav.sourceforge.net/

カテゴリー:21.Library タグ:

munin

概要

かなりオートマチックなリソース管理ツール

サイト

http://munin.projects.linpro.no/

カテゴリー:11.Application タグ:

ProjectPlus

概要

TextMateの使い勝手を改善してくれるTM Bundle

サイト

http://ciaranwal.sh/projectplus</a

カテゴリー:11.Application タグ:

IETester

概要

IE5.5, IE6, IE7, IE8のレンダリングエンジンを切り替えられるテストツール

サイト

http://www.my-debugbar.com/wiki/IETester/HomePage

カテゴリー:11.Application タグ:

SDoc

概要

Rails Searchable API Doc にも使われているドキュメントジェネレータ

リポジトリ

http://github.com/voloko/sdoc

カテゴリー:21.Library タグ:

Rails Searchable API Doc がパワーアップしていた

検索が使いやすいRails Searchable API DocがRubyにも対応していた!

Rails Searchable API Doc
Ruby Searchable API Doc

Authlogic, Haml, Hpricot, RSpec などの周辺ライブラリにも対応
公式サイトで表示するパッケージをカスタマイズできます
なぜかトップページが表示できませんが、railsapiでググってキャッシュから表示すればいけました

カテゴリー:02.Article タグ:

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 {} \;
カテゴリー:03.Round Up タグ:

VMware Fusion 3 Upgrade のプロモクーポンがあった

promo code

dsktp20sale

入れたら$8くらいやすくなった

カテゴリー:01.Diary

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"

きれいにマージされていますね!

カテゴリー:02.Article タグ:

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
カテゴリー:02.Article タグ:

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

環境が違うので比較できないけれど、実メモリ使用量は大分減ったぽい

カテゴリー:02.Article タグ:

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
カテゴリー:02.Article タグ:

gemのリポジトリにgemcutterを加える

githubがgemを配信してくれなくなるので、最近はgemcutterを使うらしい


$ gem sources -a http://gemcutter.org/
カテゴリー:02.Article タグ:

Stocksで日経平均とドル/円為替レートを表示する

Stocks とは、Mac OS X の dashboard に標準でついてくる 株価表示 widget
個別の株価だけでなく、各国の主要な株価指数や為替レートが表示可能
iPhone 版も同様

^DJI ダウ平均
^225 日経平均
^KS11 KOSPI
USDJPY=X ドル/円
EURJPY=X ユーロ/円
USDKRW=X ドル/ウォン
カテゴリー:02.Article タグ:

iStat Pro

概要

Macのステータスモニタ

サイト

http://www.islayer.com/apps/

カテゴリー:11.Application タグ:

Window.Growl

概要

Growlっぽいエフェクトを表示する

サイト

Window.Growl

カテゴリー:21.Library タグ:

JSLitmus

概要

JavaScript性能評価ツール

カテゴリー:21.Library タグ:

10 Minute Mail

概要

捨メルアドサービス

サイト

10 Minute Mail

カテゴリー:11.Application タグ:

Another HTML-lint gateway

概要

HTML祭典サービス

サイト

Another HTML-lint gateway

カテゴリー:11.Application タグ:

CSS Validation Service

概要

CSSバリデーションサービス

サイト

CSS Validation Service

カテゴリー:11.Application タグ:

W3C Markup Validation Service

概要

HTMLバリデーションサービス

サイト

W3C Markup Validation Service

カテゴリー:11.Application タグ:

Sexy Lightbox

概要

セクシーなLightbox

リポジトリ

http://www.coders.me/ejemplos/sexy-lightbox-2/

カテゴリー:21.Library タグ:

Chorizo!

概要

セキュリティスキャナ

サイト

http://chorizo-scanner.com/

カテゴリー:11.Application タグ: