« 2008年12月 | トップページ | 2009年3月 »

2009年2月28日 (土)

Java SE 7 は .NET アセンブリの夢を見るのか

Sun JDK/JRE の素朴なクラスローダーの仕組みですと、多数の外部ライブラリに依存した、アプリケーションの配布はつらいと思うことは、確かにしばしばあります。

一方、2009年夏にリリース予定のJava SE 7は、モジュール型の開発/ディプロイメントを可能にする。これらの機能はJSR 294(Improved Modularity Support in the Java Programming Language)やJSR 277(Java Module System)をベースにしたものだ。JSR 277の目標は、JAM(Java Application Modules)フォーマットによるアプリケーション・パッケージングを促すことにある。

Java SE 7をベースとするJDK 7は、OSGi(Open Services Gateway initiative:サービス管理プラットフォーム仕様)に準拠したBundles(バンドル)をサポートする。「Java SE 7用のJavaモジュール・システムとOSGiバンドル間の相互運用性を定義する新仕様が完成した。これにより、OSGiバンドルを使うアプリケーションをJDK 7で作成し、それをそのままJDK 7で実行できるようになる」と、Sunのクライアント・ソフトウェア・グループでチーフ・アーキテクトを務めるダニー・カワード(Danny Coward)氏は説明する。

モジュラリティが特徴の次期Java SE、OSGi Bundlesをサポートへ (Computerworld.jp)

JAM やら、 OSGi Bundle やらは、 Microsoft の .NET アセンブリに相当するものと考えても良いのですかね。

アセンブリは .NET Framework アプリケーションのビルドブロックであり、配置、バージョン管理、再利用、アクティブ化のスコープの指定、およびセキュリティアクセス許可の基本単位となります。アセンブリは、相互に連携して 1 つの論理的な機能単位を形成するように構築された型やリソースの集合です。

共通言語ランタイムのアセンブリ (MSDN)

Java は、 機能面で .NET に先行されていると思うことが多いですね。コミュニティ・ベースで仕様を決めている Java と、 Microsoft 1社で仕様を決めている .NET。クロスプラットフォームの Java と、 Windows Only の .NET ということで、色々速度差が出てくるのでしょうが。

とにかく、 OSGi Bundle がサポートされるようになる、ということで、OSGi Framework の仕様書を読んでみたり。

The OSGi(tm) Alliance was founded in March 1999. Its mission is to create open specifications for the network delivery of managed services to local networks and devices. The OSGi organization is the leading standard for next-generation Internet services to homes, cars, mobile phones, desktops, small offices, and other environments.

OSGi Service Platform Core Specification (OSGi Service Platform Release 4, OSGi Alliance Specifications)

ネットワーク経由でコードを配布する、というのは、 Java の当初のコンセプトからあったわけですが。

The Java platform was initially developed to address the problems of building software for networked consumer devices. It was designed to support multiple host architectures and to allow secure delivery of software components. To meet these requirements, compiled code had to survive transport across networks, operate on any client, and assure the client that it was safe to run.

A Bit of History (The Java(tm) Virtual Machine Specification Second Edition)

このコンセプトは、当面、忘れた方がいいんじゃないか という気がします。とりあえずは、「より良いクラスローダー」で良いんじゃないでしょうか。

| | コメント (0) | トラックバック (0)

2009年2月26日 (木)

CREATE TABLE 文の「差分」をとる

JJTree を使って、 CREATE TABLE 文の「差分」をとるプログラムを作ってみました。2つの CREATE TABLE 文から、両者間で追加・変更・削除されたカラムを抜き出して、 ALTER TABLE 文を作る、という Toy プログラムです。

>SqlDDLDiff test\file001_1.sql test\file001_2.sql
ALTER TABLE author DROP COLUMN a_bio;
ALTER TABLE author ALTER COLUMN a_fname TYPE varchar(10);
ALTER TABLE author ADD COLUMN a_lname varchar(20);
/* file001_1.sql */
create table author (
a_id numeric(10),
a_fname varchar(10), /* 20 -> 10 */
a_lname varchar(20), /* add */
a_mname varchar(20),
a_dob date,
--a_bio varchar(500), /* drop */
primary key (a_id) );

/* file001_2.sql */
create table author (
a_id numeric(10),
a_fname varchar(20),
--a_lname varchar(20),
a_mname varchar(20),
a_dob date,
a_bio varchar(500),
primary key (a_id) );

JJTree を使ってみて、構文木を作るツールが流行らない訳が、なんとなく分かりました。

Parser -> 構文木 (AST) -> アプリケーション・データ

とまあ、汎用のデータ構造を経てアプリケーション独自のデータへと変換する訳ですけど、これって無駄に感じるのですよね。

Parser -> アプリケーション・データ

Parser からアプリケーション・データを直接作ればいいじゃない、となる訳です。

でも、 SQL の場合は、汎用の構文木を作る意味がありそう。 RDBMS ごとに独自の「方言」がありますからね。特に、 CREATE TABLE 文 のような、 DDL 系は、 RDBMS 実装による違いが出やすいところです。汎用の構文木を間に挟むことで、 Parser を RDBMS ごとに差し替え可能にする、という風に使えそうですね。

| | コメント (0) | トラックバック (0)

2009年2月23日 (月)

基本設計と見積もり

基本設計というのは、費用・工期を見積もることができるレベルの設計といいます。 が、なぜか IT システムの世界では、基本設計の前に顧客に見積もりが提出され、「一括請負契約」が締結される、という不可思議なことが行なわれていたりします。「一括請負契約」が締結された後で、基本設計作業に入ることになるんですよね。

プラント・エンジニアリング業の方が書いた、プロジェクト・マネジメントの本を読むと、基本設計作業と並行して契約交渉が行なわれ、基本設計完了後、競争入札等で、契約が締結される、などと書かれています。基本設計の費用は、全部ベンダーの営業経費になるのかしら、と少し疑問に思っていたのですが、以下の話を読む限り、基本的にはそういうことみたいですね。

見積が無償なのは、むろん日本だけのことではない。しかし、私は海外で、ある欧米系石油メジャーに対する見積業務に従事していたときの経験を覚えている。案件は競争入札だった。大型プラントの見積作業は、それ自体が億の単位の費用を要する。ところで、その顧客は、応札者に対して、「入札要求仕様書(基本設計書)のベリフィケーション費用」という名目で、かなりの金額を支払う約束をした。

それだけではない。経済状況の乱変動に伴って、見積期間中に顧客の要求事項もあれこれと変化して、ついていくのがひどく大変な状況になった。すると、入札締切の直前に、顧客はベリフィケーション費用を50% 増額する、と宣言し、そのとおり実行したのだ。この時以来、見積はいつでも無料であるべしという感覚が、自分の中から抜け落ちてしまった。

お見積りは無料です (タイム・コンサルタントの日誌から)

IT システム業界の場合、売り手と買い手の同床異夢になっているように思います。

買い手は、「見積もりは無料」だと思っているし、売り手もそう思わせている。しかし、基本設計どころか、要求定義もろくにおこなってないわけですから、その見積もりは、極めて粗悪な代物です。

売り手としては、買い手から早く現金を引き出したいものですから、粗悪な見積もりでも、とにかく契約を結んでしまおうとするわけです。工期は短ければ、短いほど良い。今期の売上ノルマに追加できますからね。

買い手からみた場合は、おそらく、早くベンダーに責任を「丸投げ」してしまいたい、というのがあるのでしょう。見積もりが粗悪であろうがなんであろうが、「一括請負契約」である以上、ベンダーは「最後まで」責任を負うはず、ほぼ無限責任に近い責任が生じるはずだ、というところですかね。

これは、実のところ、買い手の幻想にすぎず、「請負」の対象が明確に定まらなければ、つまり、基本設計が完了していなければ、請負契約は法的に成立しないわけでして。契約を結んだと思っていても、法的には契約は成立していない、という奇妙な状態となります。

このような不安定な関係で、仕事が曲りなりにも成り立つのは、売り手と買い手との間に、一定の信頼関係がある場合だけなのでしょう。結局、長期の取引関係を前提に、ということになります。実際、「一見さん」との取引は、 9 割くらいの確率でトラブルに発展しているような感がありますね。

| | コメント (0) | トラックバック (0)

2009年2月22日 (日)

ソフトウエア工学とは何ぞや

私にとって、「ソフトウエア工学的なもの」の代表例は、オブジェクト指向だったりします。

オブジェクト指向は、少なくとも、「計算機科学的なもの」ではないと思いますね。計算機科学者がアルゴリズムを研究したり、定理を証明したり、学術論文を書いたりするのに、オブジェクト指向が役に立つとは思えませんし。

Wikipedia の Software engineering のページを見てみます。

Software engineering is the application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software, and the study of these approaches. That is the application of engineering to software.

Software engineering (Wikipedia, the free encyclopedia)

"Sub-disciplines" のところから少し辿ってみますと、 "agile or waterfall" (Software development process) 、 "versioning and source control”(Software configuration management) 、 "Refactoring tools" 、 "Source code generation tools"、 "Unified Modeling Language" (Computer-aided software engineering) などが挙がっていますね。

「ソフトウエア開発に関する何か」であれば、それは、計算機科学というよりは、ソフトウエア工学ではないですかね。従って、ソフトウエア開発の生産性が云々というのも、ソフトウエア工学でしょう。ソフトウエア開発の生産性を議論すること自体が無駄である、というのなら、それは確かにソフトウエア工学の否定かもしれません1


1.「生産性についての議論は非生産的である」という至言

| | コメント (0) | トラックバック (0)

2009年2月21日 (土)

JJTree 構文木を作るツール

コンパイラ・コンパイラが、構文木まで作ってくれれば良いのに、と前々から思っていたのですが。そういうツールが、ちゃんと存在しているのですね。

JJTree is a preprocessor for JavaCC (tm) that inserts parse tree building actions at various places in the JavaCC source. The output of JJTree is run through JavaCC to create the parser.

JavaCC (tm): JJTree Reference Documentation

おなじみの「電卓」の例は、こんな感じです。

>java Calc
1+2*3-4/5
CalcExpr
 AddExpr:+,-
  Literal:1
  MulExpr:*
   Literal:2
   Literal:3
  MulExpr:/
   Literal:4
   Literal:5

構文生成規則は、以下のように入力します。

/* Calc.jjt */
options {
  STATIC=false;
  MULTI=true;
}

PARSER_BEGIN(Calc)
public class Calc {
  public static void main(String args[])
    throws ParseException {

    while (true) {
      Calc parser = new Calc(System.in);
      if (parser.CalcExpr() == 1) {
        ((SimpleNode)parser.jjtree.rootNode()).dump("");
      }
    }
  }
}
PARSER_END(Calc)

SKIP:
{
  " "|"\r"
}

TOKEN :
{
    < ADDOP  : "+" >
   |< SUBOP  : "-" >
   |< MULOP  : "*" >
   |< DIVOP  : "/" >
   |< LP     : "(" >
   |< RP     : ")" >
   |< LITERAL: (["0"-"9"])+ >
   |< NL     : "\n" >
}

int CalcExpr() :
{ }
{
      AddExpr() <NL>
        { return 1; }
     | <NL>
        { return 0; }
     | <EOF>
        { return -1; }
}

void AddExpr() #void :
{ Token t;
  int i = 0;
}
{
    (
      MulExpr() (
        (t=<ADDOP>|t=<SUBOP>)
          { jjtThis.addValue(t.image); }
        MulExpr())*
    ) #AddExpr(>1)
}

void MulExpr() #void :
{ Token t;
  int i = 0;
}
{
    (
      PrmExpr() (
        (t=<MULOP>|t=<DIVOP>)
          { jjtThis.addValue(t.image); }
        PrmExpr() )*
    ) #MulExpr(>1)
}

void PrmExpr() #void :
{ }
{
      Literal()
    | <LP> AddExpr() <RP>
}

void Literal() :
{ Token t; }
{
    t=<LITERAL>
      { jjtThis.addValue(t.image); }
}

Ant ビルドファイル build.xml は、以下のとおり。

<?xml version='1.0' encoding='utf-8' ?>

<project name="calc" default="build" basedir=".">

  <property name="javacc.home" value="/path/to/javacc"/>

<target name="clean">
  <delete>
    <fileset dir="src">
      <include name="ASTAddExpr.java"/>
      <include name="ASTCalcExpr.java"/>
      <include name="ASTLiteral.java"/>
      <include name="ASTMulExpr.java"/>
      <include name="Calc.java"/>
      <include name="Calc.jj"/>
      <include name="CalcConstants.java"/>
      <include name="CalcTokenManager.java"/>
      <include name="CalcTreeConstants.java"/>
      <include name="JJTCalcState.java"/>
      <include name="Node.java"/>
      <include name="ParseException.java"/>
      <include name="SimpleCharStream.java"/>
      <include name="SimpleNode.java"/>
      <include name="Token.java"/>
      <include name="TokenMgrError.java"/>
    </fileset>
  </delete>
</target>

  <target name="jjtree-gen">
    <exec dir="."
      failonerror="true" executable="cmd" >
      <arg value="/C" />
      <arg value="jjtree -OUTPUT_DIRECTORY:src -NODE_CLASS:CalcNode src/Calc.jjt" />
    </exec>
  </target>

  <target name="javacc-gen">
    <javacc target="src/Calc.jj" outputdirectory="src"
      javacchome="${javacc.home}"/>
  </target>

  <target name="compile">
    <delete dir="bin"/>
    <mkdir dir="bin"/>
    <javac srcdir="src" destdir="bin">
    </javac>
  </target>

  <target name="build" depends="clean,jjtree-gen,javacc-gen,compile">
  </target>

</project>

JJTree のオプションで、 -NODE_CLASS:CalcNode として、構文木のノードを指定しています。

/* CalcNode.java */
import java.util.ArrayList;

public class CalcNode extends SimpleNode {

  ArrayList<Object> valueList = new ArrayList<Object>();

  public CalcNode(int i) {
    super(i);
  }

  public CalcNode(Calc p, int i) {
    super(p, i);
  }

  public void addValue(Object value) {
    valueList.add(value);
  }

  public String toString() {
    StringBuilder sb = new StringBuilder(super.toString());
    int i = 0;
    for (Object value : valueList) {
      if (i == 0) {
        sb.append(':');
      } else {
        sb.append(',');
      }
      sb.append(value);
      i++;
    }
    return sb.toString();
  }
}

| | コメント (0) | トラックバック (0)

2009年2月19日 (木)

「更新系」はどこへ?

正直、何を根拠にAPサーバで処理した方が良いなどと言ってるのか、いまだに分かりません。私がアホなんでしょうが、口汚く罵って煽ってみても、そういう主張の方のコメントが得られなかったのは残念です。炎上もさせられなかった(笑)。

炎上までいかなかったけれど、たくさんのご意見ありがとうございました(ベンチャー社長で技術者で)

私も「APサーバで処理した方が良い」という話はよくわからないのですが。

一連のお話を読んでいて、少し思ったのは。

ユーザーインターフェイスに必要なパラメータと戻りはダミーのストアドプロシージャの中に含まれています。更新系では、画面側の項目と、セッションIDやログインIDなどが必要になるかもしれませんが、戻りはエラーコードやエラーメッセージを返せば済みます。

更新系に必要なものは、システムの共通仕様として、あらかじめ、まとめておけばよいことです。

テーブル設計は実装の後に! (ベンチャー社長で技術者で)

と、当初は「更新系」、OLTP系の話が少し出ているのですけど。

当たり前ですが、

(クライアントへ)データ転送 > データスキャン >>>> 四則演算

つまり、DBで1回テーブルスキャンするのと、そのときに集計処理をついでに行うのはパフォーマンスには差が出ませんから、DBサーバにとって四則演算をどこかで肩代わりしても意味がないのです。

SQLを使うとスケーラビリティの問題が起きる? それはエコじゃないね (ベンチャー社長で技術者で)

後半では、完全に集計処理、OLAP系だけの話になってしまっている、というところです。

大量のユーザが、大量の更新をかけるような、OLTP系のシステムの場合、 DBサーバをスケール・アウトしたい、ということはあるのではないですかね。

Article_20090218

こんな感じで、OLTP系処理に対しては、DBサーバを複数台用意して、DBサーバ1台あたりの書き込み負荷を下げる。OLAP系処理に対しては、もう一台、集計用のDBサーバを用意して、そこで集計をすると。

まあ、別にOLTP系処理で、 テーブルを直接書きにいかずに、ストアド・プロシージャー経由でも、全く問題ない と思います。ですので、別に結論は変わらないのですけどね。

ただ、OLTP系の場合は、「スケーラビリティをあげるために、処理は極力DBサーバで行うべき」は言いすぎかもしれません。当たり前ですが、 DBのデータ使わない処理まで、DBサーバでやる必要はないですから ね。 「生の入力データ」(例えば、HTML)を、そのまま DBサーバに渡して、DBサーバで解析・バリデートとか。

結局のところ、 データに対する処理は、そのデータのある場所に近いところで行なうのが良い、という話でしょう。昔から言われていることですが。

| | コメント (0) | トラックバック (0)

2009年2月14日 (土)

回線速度の計測

Java で簡単なプログラムを書いて、 LAN 回線速度の計測をしてみました。

最初に、サーバを立ち上げます。

>java -server SpeedTest server 9999

ローカル・ループバック・アドレスにデータを送ってみます。

>java -client SpeedTest client localhost 9999
30
131
420
851
4236
8493
42591
1.7476266666666668E7
6.403517557251909E7
9.986438095238096E7
9.857353701527615E7
9.901567516525024E7
9.87708465795361E7
9.847864572327487E7
8.231636109641485E7

一番最後の数字が、平均の bps 値です。おおよそ 100M bps に近い数字が出てます。

Windows クライアントから、 Linux サーバ (Vine 4.2) にデータを送ります。

>java -client SpeedTest client 192.168.0.125 9999
689
7018
33605
70920
344607
715351
3516913
760940.4934687953
1195298.945568538
1248119.029906264
1182826.8471517202
1217126.7559858041
1172656.2205127273
1192609.5413790445
1138511.1191389847

1Mbps くらいしか出てませんね。カーネル・パラメータのせいかな。後で調べる。

ソースコードは以下のとおりです。

import java.net.*;
import java.io.*;

public class SpeedTest {

  public static void main(String[] args) {
    if (args.length == 2 && args[0].toLowerCase().charAt(0) == 's') {
      try {
        startServer(Integer.parseInt(args[1]));
      } catch (IOException e) {
        e.printStackTrace();
      }
    } else if (args.length == 3 && args[0].toLowerCase().charAt(0) == 'c') {
      try {
        startClient(args[1], Integer.parseInt(args[2]));
      } catch (IOException e) {
        e.printStackTrace();
      }
    } else {
      System.out.println("usage: server port | client host port");
    }
  }

  public static void startServer(int port) throws IOException {
    final int STATUS_RCV = 0;
    final int STATUS_CMD = 1;
    final int STATUS_RST = 2;

    ServerSocket server = new ServerSocket(port);
    for (;;) {
      Socket sock = server.accept();
      System.out.println("accept: " +
        sock.getInetAddress().getHostAddress());

      BufferedInputStream in =
        new BufferedInputStream(sock.getInputStream());
      PrintWriter out =
        new PrintWriter(sock.getOutputStream(), true);

      int stat = STATUS_RCV;
      long startMs = System.currentTimeMillis();
      int ch;

      while ((ch = in.read()) != -1) {
        if (stat == STATUS_RST) {
          startMs = System.currentTimeMillis();
          stat = STATUS_RCV;
        }
        if (stat == STATUS_RCV && ch == '\\') {
          stat = STATUS_CMD;
        } else if (stat == STATUS_CMD) {
          if (ch == 's') {
            long ms = System.currentTimeMillis() - startMs;
            out.println(ms);
            System.out.println(ms);
            stat = STATUS_RST;
          } else if (ch == 'q') {
            break;
          } else {
            stat = STATUS_RCV;
          }
        }
      }
      out.close();
      in.close();

      System.out.println("quit: " + sock.getInetAddress().getHostAddress());
      sock.close();
    }
  }

  public static void startClient(String host, int port) throws IOException {
    final int[] sendSizes =
      {65536,     // 64KB
       1048576,   // 1MB
       5242880,   // 5MB
       10485760,  // 10MB
       52428800,  // 50MB
       104857600, // 100MB
       524288000  // 500MB
      };
    int[] recvMs = new int[sendSizes.length];

    Socket sock = new Socket(host, port);
    BufferedOutputStream out =
      new BufferedOutputStream(sock.getOutputStream());
    BufferedReader in = new BufferedReader(
      new InputStreamReader(sock.getInputStream()));

    for (int i = 0; i < sendSizes.length; i ++) {
      for (int j = 0; j < sendSizes[i] - 2; j++) {
        out.write('X');
      }
      out.write('\\');
      out.write('s');
      out.flush();
      recvMs[i] = Integer.parseInt(in.readLine());
      System.out.println(recvMs[i]);
    }
    out.write('\\');
    out.write('q');
    out.flush();
    in.close();
    out.close();

    double sendBpsSum = 0.0d;
    int sendBpsCount = 0;
    for (int i = 0; i < sendSizes.length; i++) {
      if (recvMs[i] > 0) {
        double sendBps = sendSizes[i] * 8000.0d / recvMs[i];
        System.out.println(sendBps);
        sendBpsSum += sendBps;
        sendBpsCount++;
      }
    }
    System.out.println(sendBpsSum / sendBpsCount);
  }

}

| | コメント (0) | トラックバック (0)

2009年2月11日 (水)

顧客とリスクについて話すのは難しい

見積もり2億のIP電話が820万円で構築できたという話について。

「年に1日コケても良ければシステム価格は1/10になります」とか「システムがコケても〜〜とやれば問題は起きません」ということが提案出来るベンダーは素晴しいし、それを受け入れることが出来るユーザは賢いだろう。でも、それはベンダーにとっては「ビジネスチャンスと信頼の両方を失なう」ことになりかねない。

おごちゃんの雑文 「2億円が820万」

ほぼ同じ感想を持ちました。

丁度、システム一式移設という話が来ていて、見積もりを作っていたのですけど。見積もり依頼は、「一枚ペーパー」すらなく、口頭で営業担当者が、ひとことみことで言われただけらしい。顧客側は、具体的なことは何も考えておらず、「とりあえず概算が知りたい」のだそうで。

実際の発注は、競争入札になるのでしょうけど。その前に、顧客側内部の手続きで、企画・稟議を書く必要があるので、予算がいくらになるか知りたい、という話でしょう。で、現システムの担当である私に、「ざっくり」教えてくれ、というわけです。

まずは、考えうるすべての可能性を考慮しつつ、可能な限り、必要な成果物・作業を洗い出しします。いわゆる、 WBS と呼ばれるものを作るわけです。

WBS が出来上がれば、次は、顧客側作業・ベンダー側作業、顧客が負うリスク・ベンダーが負うリスク、と項目を顧客とベンダーに振り分けて、ベンダー(つまり自社)に振り分けた項目に、見積もりの数字を入れていくわけです。見積もりの「前提条件」というやつですね。

いつものことながら、「前提条件」をどうするかで、頭を悩ませていました。システムの停止期間はどのくらい許されるのか、顧客側作業としてどこまでお願いできるのか、移設後の動作確認・検証はどこまでやるのか、などなど。

この辺は、顧客の事情・懐具合を探りつつ、数字を入れるわけです。

私の段階では、「見積もり」には、顧客側作業、顧客の負うリスク等の「顧客側負担」は、書かれているのですが。おそらく、部門上長・営業を経由して、顧客の手に渡ったときには、消えているでしょうね。「そんなことはとてもお客様にはいえない」、つまり、「ビジネスチャンスと信頼の両方を失なう」ことになる、というわけです。

顧客・ベンダー双方で、リスク負担と責任分岐点、それとコスト負担とのトレード・オフについて、冷静に話し合うことができれば、建設的なんですけどね。それが本来の契約交渉というものの姿であろうとも思うわけですが。これが、日本ではなかなか成り立たないのですよね。

| | コメント (0) | トラックバック (0)

Ruby-pg を Windows にインストール

以下の環境でインストールしました。

  • Windows XP SP3
  • ActiveRuby 1.8.7.7
  • PostgreSQL 8.3.5
  • Microsoft Visual C++ 2008 Express Edition

1. PostgreSQL 側の準備

以下が前提条件となります。

  • C ヘッダー (include), ライブラリ (lib) がインストールされている
  • bin にパスが通っている ( pg_config.exe を使うため )

2. ruby config.h を修正

MSC のバージョンが限定されているので、これをはずします。

*** ruby-1.8/lib/ruby/1.8/i386-mswin32/config.h
--- ruby-1.8/lib/ruby/1.8/i386-mswin32/config.h
***************
*** 1,2 ****
! #if _MSC_VER != 1200
  #error MSC version unmatch: _MSC_VER: 1200 is expected.
--- 1,2 ----
! #if _MSC_VER < 1200
  #error MSC version unmatch: _MSC_VER: 1200 is expected.

3. ruby rbconfig.rb を修正

色々修正します。

  • CFLAGS: -MD (msvcrt dll にリンク) を -MT (lib にリンク)
  • DLDFLAGS: -debug を -release
  • LIBRUBY_A: 存在しないのでコメントアウト
*** ruby-1.8/lib/ruby/1.8/i386-mswin32/rbconfig.rb
--- ruby-1.8/lib/ruby/1.8/i386-mswin32/rbconfig.rb
***************
*** 24,26 ****
    CONFIG["PATH_SEPARATOR"] = ";"
!   CONFIG["CFLAGS"] = "-MD -Zi  -O2b2xg- -G6"
    CONFIG["CPPFLAGS"] = "  "
--- 24,26 ----
    CONFIG["PATH_SEPARATOR"] = ";"
!   CONFIG["CFLAGS"] = "-MT -Zi  -O2b2xg- -G6"
    CONFIG["CPPFLAGS"] = "  "
***************
*** 70,72 ****
    CONFIG["OBJEXT"] = "obj"
!   CONFIG["DLDFLAGS"] = "-link -incremental:no -debug -opt:ref -opt:icf -dll $(LIBPATH)"
    CONFIG["ARCH_FLAG"] = ""
--- 70,72 ----
    CONFIG["OBJEXT"] = "obj"
!   CONFIG["DLDFLAGS"] = "-link -incremental:no -release -opt:ref -opt:icf -dll $(LIBPATH)"
    CONFIG["ARCH_FLAG"] = ""
***************
*** 89,91 ****
    CONFIG["RUBYW_INSTALL_NAME"] = "rubyw"
!   CONFIG["LIBRUBY_A"] = "$(RUBY_SO_NAME)-static.lib"
    CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll"
--- 89,91 ----
    CONFIG["RUBYW_INSTALL_NAME"] = "rubyw"
! #  CONFIG["LIBRUBY_A"] = "$(RUBY_SO_NAME)-static.lib"
    CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll"

4. ruby gem でインストール

以下から gem install を起動します。

  • スタート メニュー\プログラム\Microsoft Visual C++ 2008 Express Edition\Visual Studio Tools\Visual Studio 2008 コマンド プロンプト
Setting environment for using Microsoft Visual Studio 2008 x86 tools.

C:\Program Files\Microsoft Visual Studio 9.0\VC>gem install pg
Building native extensions.  This could take a while...
Successfully installed pg-0.7.9.2008.10.13
1 gem installed
Installing ri documentation for pg-0.7.9.2008.10.13...
Installing RDoc documentation for pg-0.7.9.2008.10.13...

C:\Program Files\Microsoft Visual Studio 9.0\VC>

5. 動作確認

rdoc を参考に、以下のようなテストスクリプトを書いて実行します。

# pg_test.rb

require 'pg'

conn = PGconn.open(
:dbname => 'DBT1',
:hostaddr => '192.168.0.125',
:user => 'pgsql')
res = conn.exec('SELECT $1::int AS a, $2::int AS b, $3::int AS c',[1, 2, nil])
res.each { |t| p t }

動いてますね。

>ruby pg_test.rb
{"a"=>"1", "b"=>"2", "c"=>nil}

| | コメント (0) | トラックバック (0)

2009年2月10日 (火)

DBサーバの性能

昨年末、約10年前のノートパソコンに、Vine Linux をインストールしていたのは、 OSDL Database Test Suite の Database Test 1 (DBT-1) を試してみようと思ったからだったりします。

テストデータベース (PostgreSQL 7.4) は出来上がったのですが。

DBT1=# \timing
Timing is on.
DBT1=# select count(*) from orders;
  count
---------
 2593048
(1 row)

Time: 31606.238 ms

260 万件程度の集計で、 32 秒かかるという遅さ。

ちなみに、某 Oracle データベースのサーバ機で、似たような集計を試してみますと。

SQL> set timing on
SQL> /

  COUNT(*)
----------
   3128119

経過: 00:00:00.17

まあ、10年前のノートパソコンと、最近のサーバ機を比べても勝負になるわけないのですけどね。

メモリ搭載量が、 192MB 対 5GB ですからね。 Oracle のサーバ機の方は、ディスクアクセスがほとんど発生していないのではないですかね。ディスクの性能も全然違うでしょうけど。おまけに、サーバ機の方は、 RAID 1 構成ですし。

| | コメント (0) | トラックバック (0)

2009年2月 8日 (日)

APサーバ追加でスケール・アウトできるのはユーザ数では?

以下の一連の話を、面白いと思いながら、読ませていただきました。

スケーラビリティが上がるというのは、「APサーバの増設が簡単」ということになろうかと思います。確かに簡単に追加できますが、それはエコじゃないね〜。

SQLを使うとスケーラビリティの問題が起きる? それはエコじゃないね (ベンチャー社長で技術者で)

クライアント・サーバ・システムで、「パフォーマンスがでない」という相談を受けて、プログラムを見せてもらうと、アプリケーション側で、データベースからフェッチしてきたレコード 数百万件を集計していた、というのはよくありますね。 SQL でやれば、 数秒で終わるような処理の典型例です。

毎回、DBサーバから、ネットワーク越しにレコードを受け取りつつ、アプリケーション側でループまわして集計してたら、そりゃ遅いですよね。

また、こうした集計処理というのは、単純に「APサーバの増設」では、スケール・アウトできないですよね。それこそ、 Google の MapReduce のように、集計処理を分散して実行する仕組みがないと。

よくある、 Web(AP)サーバ ・ DBサーバ での、Webアプリケーション構成では、Webサーバ、 APサーバ の増設でスケールアウトできるのは、 同時接続ユーザ数 であろうと思うわけです。

しかし、企業内システムで、 Google や Yahoo! のように、 数千万人のユーザ がアクセスしてくる、なんてことはないわけでして。大企業でも、システムのユーザ数は、せいぜい 1万人 くらいですからね。

私も SQL という言語自体はあまり好きではなかったりするのですが(CODASYL 風味なところが)。企業内システムでは、強大な威力を発揮するというのは、確かです。仕事では、 SQL (と Oracle PL/SQL) ばかり使っていますしね。

| | コメント (0) | トラックバック (0)

« 2008年12月 | トップページ | 2009年3月 »