PostgreSQL の pgbench みたいな SQL Server 用ベンチマークツール

実際に発行する SQL を使ったベンチマークSQL Server でやりたいので、 使えそうなツールを探してみた。

必要な機能は次の通り。

  • ODBC 接続
  • ファイルから SQL を読み込んで実行
  • サーバーとデータベースを指定できる
  • 回数を指定して同じ SQL を繰り返し実行できる
  • 並列数を指定して同じ SQL 同時に複数実行できる

PostgreSQL なら pgbench があるのに。 同じようなツールが SQL Server に標準で付いていないのかな。

標準ではないけど、ベンチマークに使える Microsoft 製のツールは見つけた。

このユーティリティに含まれている ostress で、pgbench と同じようなことができた。

例えば、「Test.sql ファイルから SQL を読み込んで、ローカルの SQL Server Express にある TESTDB データベースに 10 回実行」する場合、 こんな感じ。

ostress -E -S"(local)\SQLEXPRESS" -r10 -d"TESTDB" -i".\Test.sql"

GRAMERCY NEWYORK

大丸のグラマシーニューヨークが気になっていたので、ちょっと奮発して買ってみた。

この店はニューヨークチーズケーキが売りなんだけど、目をつけていたのは生ケーキ。いやもうね、ショーケースの中で存在感があったんでね。

パリパリのチョコで挟まれたケーキなんて初めて。ケーキ本体はもちろん美味かったけど、それよりも横のチョコが美味かった。こいつだけ食べたいくらい。

ショートケーキも気になっていた。というかこちらが本命。

生クリームが軽くて、結構大きいのに一気にフォークが進んだ。中のイチゴは少なめ。ただ、代わりのイチゴクリームが良いアクセントになってた。

関連ランキング:ケーキ | 西鉄福岡駅(天神)天神南駅天神駅

理性捨てて props 全部渡していたら react-router でハマった

react-router で遷移先のコンポーネントに props を渡すには React.cloneElement を使う。

tnakamura.hatenablog.com

このとき、理性捨てて

this.props.children && React.cloneElement(this.props.children, this.props)

という風に props をまるごと渡していたら、 Route を二段入れ子にしていた場合に children を上書きしてしまうみたいで、 コンポーネントの描画が再起しまくって死亡した。

this.props.children && React.cloneElement(this.props.children, {
  users: this.props.users,
  posts: this,props.posts,
})

みたいにして children は除外しないとダメ。

Swagger definition からクライアントとサーバーのソースコードを生成してみた

前回は簡単な Web API の Swagger definition を書いただけで終わってしまったけど、 ここからが本題。

やりたいのは、Swagger definition からクライアントとサーバーのソースコード生成すること。 そのためのツールとして swagger-codegen が提供されている。

http://swagger.io/swagger-codegen/swagger.io

お試しなので、Homebrew や mvn を使わずに直接 jar をダウンロードしてしまおう。

http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.1.5/swagger-codegen-cli-2.1.5.jar

前回の Swagger definition を sample_api.yml として保存し、 まずは API クライアントを生成してみる。

java -jar swagger-codegen-cli-2.1.5.jar generate \
  -i sample_api.yml \
  -l csharp \
  -o SampleApiClient

を実行すると、RestSharp を使った API クライアントのソースコードが生成された。 一部抜粋すると、こんな感じ。

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using RestSharp;
using IO.Swagger.Client;
using IO.Swagger.Model;

namespace IO.Swagger.Api
{
    // ...(IDefaultApi の定義は省略)...

    /// <summary>
    /// Represents a collection of functions to interact with the API endpoints
    /// </summary>
    public class DefaultApi : IDefaultApi
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultApi"/> class.
        /// </summary>
        /// <returns></returns>
        public DefaultApi(String basePath)
        {
            this.Configuration = new Configuration(new ApiClient(basePath));
        }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultApi"/> class
        /// using Configuration object
        /// </summary>
        /// <param name="configuration">An instance of Configuration</param>
        /// <returns></returns>
        public DefaultApi(Configuration configuration = null)
        {
            if (configuration == null) // use the default one in Configuration
                this.Configuration = Configuration.Default; 
            else
                this.Configuration = configuration;
        }

        /// <summary>
        /// Gets the base path of the API client.
        /// </summary>
        /// <value>The base path</value>
        public String GetBasePath()
        {
            return this.Configuration.ApiClient.RestClient.BaseUrl.ToString();
        }

        /// <summary>
        /// Sets the base path of the API client.
        /// </summary>
        /// <value>The base path</value>
        [Obsolete("SetBasePath is deprecated, please do 'Configuraiton.ApiClient = new ApiClient(\"http://new-path\")' instead.")]
        public void SetBasePath(String basePath)
        {
            // do nothing
        }
    
        /// <summary>
        /// Gets or sets the configuration object
        /// </summary>
        /// <value>An instance of the Configuration</value>
        public Configuration Configuration {get; set;}

        /// <summary>
        /// Gets the default header.
        /// </summary>
        /// <returns>Dictionary of HTTP header</returns>
        [Obsolete("DefaultHeader is deprecated, please use Configuration.DefaultHeader instead.")]
        public Dictionary<String, String> DefaultHeader()
        {
            return this.Configuration.DefaultHeader;
        }

        /// <summary>
        /// Add default header.
        /// </summary>
        /// <param name="key">Header field name.</param>
        /// <param name="value">Header field value.</param>
        /// <returns></returns>
        [Obsolete("AddDefaultHeader is deprecated, please use Configuration.AddDefaultHeader instead.")]
        public void AddDefaultHeader(string key, string value)
        {
            this.Configuration.AddDefaultHeader(key, value);
        }
   
        
        /// <summary>
        /// ユーザー一覧取得 ユーザー一覧を取得します。
        /// </summary>
        /// <param name="page">ページ番号</param> 
        /// <returns>List&lt;User&gt;</returns>
        public List<User> UsersGet (int? page = null)
        {
             ApiResponse<List<User>> response = UsersGetWithHttpInfo(page);
             return response.Data;
        }

        /// <summary>
        /// ユーザー一覧取得 ユーザー一覧を取得します。
        /// </summary>
        /// <param name="page">ページ番号</param> 
        /// <returns>ApiResponse of List&lt;User&gt;</returns>
        public ApiResponse< List<User> > UsersGetWithHttpInfo (int? page = null)
        {
            
    
            var path_ = "/users";
    
            var pathParams = new Dictionary<String, String>();
            var queryParams = new Dictionary<String, String>();
            var headerParams = new Dictionary<String, String>(Configuration.DefaultHeader);
            var formParams = new Dictionary<String, String>();
            var fileParams = new Dictionary<String, FileParameter>();
            String postBody = null;

            // to determine the Accept header
            String[] http_header_accepts = new String[] {
                "application/json"
            };
            String http_header_accept = Configuration.ApiClient.SelectHeaderAccept(http_header_accepts);
            if (http_header_accept != null)
                headerParams.Add("Accept", Configuration.ApiClient.SelectHeaderAccept(http_header_accepts));

            // set "format" to json by default
            // e.g. /pet/{petId}.{format} becomes /pet/{petId}.json
            pathParams.Add("format", "json");
            
            if (page != null) queryParams.Add("page", Configuration.ApiClient.ParameterToString(page)); // query parameter
            
            
            
            

            
    
            // make the HTTP request
            IRestResponse response = (IRestResponse) Configuration.ApiClient.CallApi(path_, Method.GET, queryParams, postBody, headerParams, formParams, fileParams, pathParams);

            int statusCode = (int) response.StatusCode;
    
            if (statusCode >= 400)
                throw new ApiException (statusCode, "Error calling UsersGet: " + response.Content, response.Content);
            else if (statusCode == 0)
                throw new ApiException (statusCode, "Error calling UsersGet: " + response.ErrorMessage, response.ErrorMessage);
    
            return new ApiResponse<List<User>>(statusCode,
                response.Headers.ToDictionary(x => x.Name, x => x.Value.ToString()),
                (List<User>) Configuration.ApiClient.Deserialize(response, typeof(List<User>)));
            
        }


        // ...(以下 Web API を呼び出すメソッドが続く)...
    }
}

次はサーバー側。 README によると、ASP.NET Core のプロジェクトを生成できるようだ(名前は ASP.NET 5 のままになっているけど)。

java -jar swagger-codegen-cli-2.1.5.jar generate \
  -i sample_api.yml \
  -l aspnet5 \
  -o SampleApiServer

を実行したら、aspnet5 には対応してないっていうエラーになった。 …ASP.NET Core 対応が入ったの 1 ヶ月前だけど、2.1.5 がリリースされたのは 1 月 7 日だった。 含まれてなかったのか。

mvn でビルドするしかないかと思っていたら、 Swagger Editor でも ASP.NET Core のソースコードを生成できるではないですか。

メニューをクリックするとソースコードのアーカイブをダウンロードできた。 一部抜粋すると、こんな感じ。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Newtonsoft.Json;
using Swashbuckle.SwaggerGen.Annotations;
using IO.Swagger.Models;

namespace IO.Swagger.Controllers
{ 
    /// <summary>
    /// 
    /// </summary>
    public class DefaultApiController : Controller
    { 

        /// <summary>
        /// ユーザー一覧取得
        /// </summary>
        /// <remarks>ユーザー一覧を取得します。</remarks>
        /// <param name="page">ページ番号</param>
        /// <response code="200">OK</response>
        [HttpGet]
        [Route("/users")]
        [SwaggerOperation("UsersGet")]
        [SwaggerResponse(200, type: typeof(List<User>))]
        public IActionResult UsersGet([FromQuery]int? page)
        { 
            string exampleJson = null;
            
            var example = exampleJson != null
            ? JsonConvert.DeserializeObject<List<User>>(exampleJson)
            : default(List<User>);
            
            return new ObjectResult(example);
        }


        /// <summary>
        /// ユーザー登録
        /// </summary>
        /// <remarks>ユーザーを 1 件登録します。</remarks>
        /// <param name="name">ユーザー名</param>
        /// <response code="200">OK</response>
        [HttpPost]
        [Route("/users")]
        [SwaggerOperation("UsersPost")]
        [SwaggerResponse(200, type: typeof(User))]
        public IActionResult UsersPost([FromForm]string name)
        { 
            string exampleJson = null;
            
            var example = exampleJson != null
            ? JsonConvert.DeserializeObject<User>(exampleJson)
            : default(User);
            
            return new ObjectResult(example);
        }


        /// <summary>
        /// ユーザー取得
        /// </summary>
        /// <remarks>ユーザーを 1 件取得します。</remarks>
        /// <param name="userId">ユーザー ID</param>
        /// <response code="200">OK</response>
        [HttpGet]
        [Route("/users/{userId}")]
        [SwaggerOperation("UsersUserIdGet")]
        [SwaggerResponse(200, type: typeof(User))]
        public IActionResult UsersUserIdGet([FromRoute]string userId)
        { 
            string exampleJson = null;
            
            var example = exampleJson != null
            ? JsonConvert.DeserializeObject<User>(exampleJson)
            : default(User);
            
            return new ObjectResult(example);
        }


        /// <summary>
        /// ユーザー更新
        /// </summary>
        /// <remarks>ユーザーを 1 件更新します。</remarks>
        /// <param name="userId">ユーザー ID</param>
        /// <param name="name">ユーザー名</param>
        /// <response code="200">OK</response>
        [HttpPut]
        [Route("/users/{userId}")]
        [SwaggerOperation("UsersUserIdPut")]
        public void UsersUserIdPut([FromRoute]string userId, [FromForm]string name)
        { 
            throw new NotImplementedException();
        }


        /// <summary>
        /// ユーザー削除
        /// </summary>
        /// <remarks>ユーザーを 1 件削除します。</remarks>
        /// <param name="userId">ユーザー ID</param>
        /// <response code="200">OK</response>
        [HttpDelete]
        [Route("/users/{userId}")]
        [SwaggerOperation("UsersUserIdDelete")]
        public void UsersUserIdDelete([FromRoute]string userId)
        { 
            throw new NotImplementedException();
        }
    }
}

サーバー側は Swashbuckle に依存していた。 アクションの定義だけはあるけど、中身は実装してないに等しい。 せめて partial メソッドとか使っていればいいのに。

デモ版 Swagger Editor で生成できるソースコードに不満が無い場合は、 わざわざ swagger-codegen をインストールしなくてもよさそうだ。

ソースコードを生成するテンプレートをいじりたいときや、生成ロジックをカスタマイズしたいときに、 swagger-codegen を使うことになりそう。 とりあえず ASP.NET Core のテンプレートはカスタマイズが必要だな。

Web API の Swagger definition を書いてみた

Single Page Application(SPA) の開発を効率化できないか検討している。 具体的にはクライアント側と Web API 側を平行に開発したい。

Web API のインタフェースを定義しておいて、 Web API が形になるまではスタブを使ってクライアント側を開発すれば、 平行に開発できそうだ。 これは誰でも思いつくやり方。

そこで白羽の矢が立ったのが Swagger。

http://swagger.io/swagger.io

Swagger を使ってみた記事はネットでたくさん見つかったけど、 その多くが既存の Web API から Swagger を使ってドキュメントを生成するものだった。 やりたいのは Web API の Swagger definition を書いて、 Swagger を使ってドキュメントとソースコードの生成。

試しに、ユーザーの CRUD を行う Web API の Swagger definition を書いてみた。

swagger: '2.0'
info:
  title: Sample API
  version: "1.0.0"
basePath: /api/v1
produces:
  - application/json
paths:
  /users:
    get:
      summary: ユーザー一覧取得
      description: ユーザー一覧を取得します。
      parameters:
        - name: page
          description: ページ番号
          in: query
          required: false
          type: integer
      responses:
        200:
          description: OK
          schema:
            type: array
            items:
              $ref: '#/definitions/User'
    post:
      summary: ユーザー登録
      description: ユーザーを 1 件登録します。
      parameters:
        - name: name
          description: ユーザー名
          in: formData
          required: true
          type: string
      responses:
        200:
          description: OK
          schema:
            $ref: '#/definitions/User'
  /users/{userId}:
    get:
      summary: ユーザー取得
      description: ユーザーを 1 件取得します。
      parameters:
        - name: userId
          description: ユーザー ID
          in: path
          required: true
          type: string
      responses:
        200:
          description: OK
          schema:
            $ref: '#/definitions/User'
    put:
      summary: ユーザー更新
      description: ユーザーを 1 件更新します。
      parameters:
        - name: userId
          description: ユーザー ID
          in: path
          required: true
          type: string
        - name: name
          description: ユーザー名
          in: formData
          required: false
          type: string
      responses:
        200:
          description: OK
    delete:
      summary: ユーザー削除
      description: ユーザーを 1 件削除します。
      parameters:
        - name: userId
          description: ユーザー ID
          in: path
          required: true
          type: string
      responses:
        200:
          description: OK
definitions:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string

慣れるまでは Swagger Editor で書くのがいい。入力補完が効くので結構快適。 自分はインストールが面倒なのでライブデモを使った。

editor.swagger.io

Swagger Editor を使うと、Spec を編集するとリアルタイムでドキュメントのプレビューが更新される。 実際にプレビューに表示されるドキュメントはこんな感じ。

f:id:griefworker:20160309111411p:plain

『Butterflies』買った

BUMP OF CHICKEN のニューアルバム『Butterflies』を買った。 収録曲の半分がシングルという豪華な内容。

『Hello, world!』はここ数年のバンプの曲で一番好き。 久しぶりに疾走感溢れるナンバーで、何度もリピートしている。 Hello World といったらプログラミング入門の定番だけど、 それを取り入れた PV も個人的にツボ。

シングルとしても発売された『Butterfly』はテクノっぽくて、 今までの彼らの曲には無かった新しい音。 バンドとして新しいことに挑戦し続ける意思を感じた。

1st トラックの『GO』は聴くたびにどんどん好きになっていった。 特に2番の希望溢れる歌詞がお気に入り。 未来がとても素晴らしい日になるように、 行動を起こさなければいけないという、 焦りにも似た思いが込み上げてきた。

全体の感想としては、 GO→Hello, world!→Butterfly の流れでぐっと心がつかまれて、 そのまま最後まで駆け抜けていった。 時間を感じないアルバムだった。

Butterflies(通常盤)

Butterflies(通常盤)

signtool を使ってファイルに複数のデジタル署名が添付されていることを確認する

signtool は /as オプションを使うことで、1つのファイルに複数のデジタル署名を添付できる。

添付したデジタル署名の一覧はファイルのプロパティから確認できるが、 アルゴリズムとタイムスタンプだけでいいなら

signtool verify /pa /all <ファイルパス>

を実行すれば

File: <ファイルパス>
Index  Algorithm  Timestamp    
========================================
0      sha1       None         
1      sha256     None         

Successfully verified: <ファイルパス>

という風に表示できる。

ちゃんとデジタル署名が添付されているかチェックしたいファイルが多い場合に使えるかもしれない。

…と思ってたけど、遅い。 遅すぎる。 検証をしているわけで、時間がかかるのはわかるんだけど。 もっと速い方法はないものか。 とりあえずメモはしておく。