達人に学ぶDB設計徹底指南

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ


「初心者で終わりたくないあなたへ」という副題ですが、基本的には初心者が手っ取り早く基礎を固めるための書籍のように感じました。データベーススペシャリストの午前の解説書のような正規化理論やインデックスなど基本的なテーマにページをさいていますので、。このあたりの知識がある人にとっては1時間くらいあれば読める内容で多少物足りないかもしれません。
個人的に興味を持ったのは以下のような話題でしょうか。

  • サロゲートキーではなくナチュラルキーをしようすべき
  • group byでアドホックキーを使う
  • sequenceとIDENTITY

ORMのようなフレームワークとの相性もあってか、最近はサロゲートキーが全盛という気がするのでナチュラルキーを使え、という話は自分の中でそれぞれのメリット・デメリットを再考するきっかけになりました。

IIJmio高速モバイル/D

スマートフォンの通信環境として、いままで日本通信のb-mobile Fairを使っていたのですが、期限が切れたのを機に、IIJmio高速モバイル/Dのファミリーシェア1GBプランに変更してみました。

ファミリーシェア1GBプランは通信量1GBまでは2940円/月で、特徴的なのがSIMが3枚まで使えるということです。家族で共有してもよし、自分で複数枚使ってもよしですが、うちは妻が私のお下がりのGalaxy Sに入れて使ってます。家族がスマホを使っているような家庭では、かなり通信費が節約できるのではないでしょうか。ただし2台持ち前提ですが。1GBというのもこれまでもb-mobile Fairの1GBを使い切るのに3ヶ月くらいかかっていたので、私には全然問題無いです。

以下、速度です。多少時間帯が違いますが、端末はXperia acroで同じ場所で測定した結果です。IIJmioの方が速いですね。体感的にもそんな感じがします。IIJmioの方はXi対応ですが、私は検証できる端末がありません。
b-mobile Fair

IIJmio高速モバイル/D

継続的デリバリー

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化


ワンクリックで本番環境へのディプロイやリリースが可能な状態を常に維持して開発を行うということが目標ですが、そこに至るために必要な要素というかプラクティスが多いため、実現することはかなりしんどいな、というのが印象です。ただ、これくらいはやっていかないと、自信を持って、常にリリース可能な製品は作れないというのもわかります。本書は

  • 構成管理(VCS, Maven
  • TDD
  • 受け入れテストの自動化
  • ディプロイ自動化
  • CI等各種ツールの習熟

などを熟知した上で、より効率的な継続的デリバリーを実現のためには、、ということだと思います。私自身まったくそのレベルには至っていないのですが、いくつか印象に残ったことを書いておきます。その他にもDVCSやデーベースリファクタリングやリリース方法など盛りだくさんです。
少し気になる点としては、全体的に文章が多くて、特に前半は同じ事が繰り返し述べられている印象がありました。

ブランチを使わない

VCSを使ってある程度大きな機能追加やリファクタリングを行う場合には、ブランチを切って作業を行うことがありますが、本書は一貫して否定的です。メインラインにマージする時間が遅れるほど問題が起きやすく、大規模な変更を分割して小さくしたうえでインクリメンタルにメインラインにコミットせよということです。

受け入れテストのレイヤー化

私も苦労していますが、、自動の受け入れテストをUIベッタリに書いてしまうと(webであればhtmlのid等をハードコーディングする)、テストコードの変更コストが大きくなりすぎるので、UIを抽象化したレイヤを設けるべき。SeleniumのPageObjectモデルのような考えだと思います。

旧バージョンに戻すとき

何かの理由で旧バージョンに戻すときには、旧バージョンをディプロイし直すか、そもそも旧バージョンを残しておいて切り替えるべき。バージョンを戻すための手作業はダメ。ディプロイもステージング環境と本番とで同じ手順にしておけば、何回も繰り返していることなので安全。

Gitによるバージョン管理

Gitによるバージョン管理

Gitによるバージョン管理


git関連の本は入門gitを持っているのですが、そろそろ業務でもgitを使おうかというタイミングなのでもう一冊『Gitによるバージョン管理』を読んでみました。gitを使うシチュエーションとして、

という順に、ストーリー仕立ての導入とそこでよく使うgitコマンドの解説という構成になっています。VCSの本は、だいたいこんな感じが多いですが、導入とコマンド解説がわりとしっかり分かれていて、参照しやすいです。ストーリー仕立ての中にコマンド解説が紛れているとリファレンス的な使い方をしづらいので。
チームで使う場合のワークフローも紹介されていていますが、管理者がセントラルリポジトリを管理して、さらに個人用のリモートリポジトリを作ってというのは、結構しんどい気がします。。私自身一人で使ったことないので想像ですが、git flowを試してみようかと思っています。

VBScriptでAmazon SES経由でメールを送信する

結構前からですが、Amazon Simple Email Service(SES)でSMTP経由でのメール送信ができるようになりました。それまではGMailSMTPサーバを使用したり、SESを使用する場合はPerlのスクリプト経由で送信していたのですがだいぶ便利になります。
以下、VBScriptでSESのSMTPサーバを使用してメールを送信するスクリプトです。実際に使用するためにはSESのコンソールでのアカウントの作成等が必要です。

Play framework2.0を使ってみた

ちょうどPlay framework 2.0が正式にリリースされたましたが、1ヶ月くらい前(RC2くらいのころ)に、Scalaの方を少し試してみたときのメモや感想です。とりあえず、Play Scalaのチュートリアル(‘Yet Another Blog Engine’)を、2.0で写経してみました。もともとScala版のチュートリアルは、CRUD(いわゆるスカッフォールディング)などのモジュールがないため、Java版のチュートリアルにある後半部分(管理画面や認証機能)がないのですが、今回も同じところまでです。
コードは以下においてありますが、私はScalaについては本は読んだけど実際にまとまった量のコードは書いたことがない初心者なので、Scalaのコードとしては酷いレベルだと思います。
kenichiro22/yabe-play20-scala

Play 2.0からの仕様変更やサポートされなくなった機能の影響などで、チュートリアルのコードのうち以下のような部分は変更しています。

Bootstrap

Bootstrapクラスに書いていた初期化処理は、play.api.GlobalSettinsを継承したクラスに書く。

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    if (app.mode == Mode.Dev) {
      // 開発環境での初期データの登録
      InitialData.insert()
    }
  }
}

Fixtures

FixturesというYAMLからデータを読み込む機能がなくなっています。Scalaのコードで書いてもYAMLとそんなにかわらないということのようです。

Seq(
  User(Id(1), "bob@gmail.com", "secret", "Bob", false),
  User(Id(2), "jef@gmail.com", "secret", "Jef", false)
).foreach(User.create)

Anorm

1.2のScalaモジュールや2.0の途中までは、モデルクラスでMagic[T]を継承しておくとinsert/updateや基本的なselect文は自動生成していましたがこれはなくなったようです。例えばone-to-manyの集約なんかもScalaのコレクションAPIを使うということのようです。いちいち基本的なSQLを書くのは面倒なので、別のORMを使うか自前で自動生成部分を書く必要がありそうです。

テンプレートヘルパー

テンプレートはこれまでのGroovyベースからScalaベースのものに変更されています。コントローラーや静的リソースにアクセスするためのヘルパーメソッドがかなり変わっています。@action(controllers.Application.***) は @routes.Application.*** になっていて、@asset は@routes.Assets.at になってます。

Flashスコープ

Flashスコープの使い方は以下のような感じです。

  def postComment(postId: Long) = Action { implicit request =>
    Ok(〜).flashing("success" -> "Success!")
  }

Controllerで↑のようにしておいて、テンプレート↓で受け取る。

@(/* 通常のテンプレート引数 */)(implicit flash: Flash)

Captcha

チュートリアルでcaptchaを使っているのですが、2.0ではCaptcha関連のクラスがまだないようだったので、JCaptchaというライブラリを使いました。sbtで依存ライブラリの管理を行います。

Job

Jobというクラスを使ってスケージュール実行を行なっていましたが、AkkaというScalaのアクターライブラリを使う方針のようです。前はCron形式のスケジュール指定ができたと思いますが、この辺もいまは自前でしょうか。

Eclipseを使う

Playは各種IDE用の設定ファイルを作成してくれる機能があるので、Eclipseでインポートすることは簡単です。
ただし、コントローラからテンプレートの呼び出しの部分で型チェックが行われるのですが、Eclipseからテンプレートを変更しても自動でコンパイルされないため、コントローラでコンパイルエラーになってしまいます。また、コマンドラインからコンパイルしてもEclipseでうまく反映されないことがありました。
このへんはIDEのサポートを期待したいです。特にIntelliJ IDEA。

感想

私が使ったのはRCなのでリリース版とは異なる部分があるかもしれません。ただ、現時点でPlay 1.2の後継として期待するのは厳しいです。Scalaで書き直した結果、硬派というか、あまり至れり尽くせりな感じはありません。ScalaならまだしもJavaで2.0を選択するのは、ちょっと早いかなと思います。これもPlay 1.2の完成度がそれだけ高かったということだと思いますので、今後に期待したいと思います。

AWS SDK for Rubyでインスタンスのイメージ(AMI)を登録する

未だにEC2インスタンスのバックアップというかスナップショットからのリストアのベストプラクティスというのがわかりません。VMwareでのスナップショットと同じ感覚で操作したいのですが。。
EBSボリュームのスナップショットから復元する場合には、以下のような手順になると思います。

  1. スナップショットからボリュームを作成
  2. インスタンスと同一AMIでインスタンスを作成
  3. インスタンスからルートボリュームをデタッチ
  4. 新しいボリュームをルートボリュームとしてアタッチ

単一ボリュームであればそれほど問題ないのですが、複数のEBSボリュームをアタッチしている場合に手順が煩雑です。この処理を行うスクリプトやツールを作成すればよいのかもしれません。
また、別の方法として、インスタンスごとイメージ(AMI)を作成して登録しAMIを世代管理すればよいのではと思いスクリプトを書いてみた、、のですが、どうもうまく動かないので放置状態になってました。せっかくなのでここにあげておきます。
AMIから新しいインスタンスを立ち上げた場合には、インスタンスIDが変わってしまうので、別の処理でインスタンスIDに依存した部分があったりすると、それはそれで面倒です。そもそも、うまく説明できないのですがインスタンスの一個に依存するような利用・運用がEC2の特性にそぐわないのではないかという気もします。

# -*- coding: utf-8 -*-

require 'rubygems'
require 'yaml'
require 'aws-sdk'
require 'date'

ID_TAG="backup"

unless ARGV.size == 2
  puts "Usage: #{$0} <target_instance> <count>"
  exit 1
end

instance_id = ARGV[0]
count = ARGV[1].to_i
unless count > 0
  puts "Count must be greater than 0 !"
  exit 1
end

puts "Create image of #{instance_id} with #{count} generations."

config = YAML.load(File.read("config.yml"))
AWS.config(config)

ec2 = AWS::EC2.new
instance = ec2.instances[instance_id]
unless instance.exists?
  puts "No such instance: #{instance_id}"
  exit 1
end

# Create new image
image_name = "#{instance.id}-#{DateTime.now.strftime("%Y%m%d%H%M%S")}"
image_desc = "Backup image(#{instance.tags['Name']}) at #{DateTime.now}"
new_image = instance.create_image(image_name, {:description => image_desc, :no_reboot => true})

# Add tag for indentifying backuped images
new_image.tags << ID_TAG

# wait for complete
puts "waiting for new image: #{new_image.name}"
begin
  sleep 1
  new_image = ec2.images[new_image.id]
end until new_image.state != :pending

if new_image.state == :failure
  puts "create image failed: #{new_image.state_reason}"
  exit 1
end

# Remove old images
ec2.images.with_owner(:self).  
  filter("name", "*#{instance.id}*").
  filter("state", "available").
  tagged(ID_TAG).
  sort_by{|i| i.name}.reverse_each. # sort descending by datetime
  each_with_index {|i, idx|
    if idx >= count
      puts "#{i.name} -> deregister"
      i.deregister
    else
      puts "#{i.name} -> hold"
    end  
  }