C#で非同期に処理するTask間のメッセージ通信

はじめに

C#でTaskを使用した非同期処理をする場合に複数スレッド間でメッセージのやりとりをする方法をまとめます。

実行環境

事前準備

MVVM Light ToolkitのMessengerを使用するのでNuGetでインストールします。 f:id:hiroki-sawano:20180907062443p:plain

プロキシの設定が必要な場合はNuGet.Configに次の記述を追加します。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="http_proxy" value="http://~~" />
    <add key="http_proxy.user" value="user_name" />
  </packageSources>
</configuration>

サンプルプログラムの構成

ここではTaskATaskBが並列実行している中で、TaskAがとあるイベントを契機にTaskBにメッセージを送信し、TaskBはメッセージを処理し終えたことをTaskAに通知するプログラムを想定します。 f:id:hiroki-sawano:20180907073634p:plain

実装

まずはじめにProgram::Main()で2つのTaskrun()します。

class Program
{
    static void Main(string[] args)
    {
        TaskA taskA = new TaskA();
        TaskB taskB = new TaskB();

        var tokenSource = new CancellationTokenSource();
        var token = tokenSource.Token;

        var tasks = new[]
        {
            Task.Run(() => taskA.run(token), token),
            Task.Run(() => taskB.run(token), token)
        };
        Task.WaitAll(tasks);
    }
}

続けてTask側ですが、各Taskのコンストラクタではメッセージ受信時に起動したいメソッドをMessenger.Default.Registerで登録しています。
TaskAResponseMessage型のインスタンスMessenger.Default.Sendされたときに、TaskA::onReceivedMessage(ResponseMessage res)を実行します。
TaskBRequestMessage型のインスタンスMessenger.Default.Sendされたときに、TaskB::onReceivedMessage(RequestMessage req)を実行します。
Messenger.Default.SendはメッセージをMessengerに登録している全タスクにブロードキャストしてしまうので、メッセージの型を変えることでメッセージの送信先を制御しています。

class TaskA
{
    public TaskA()
    {
        Messenger.Default.Register<ResponseMessage>(this, onReceivedMessage);
    }
    public void run(CancellationToken token)
    {
        // do something
        RequestMessage req = new RequestMessage();
        req.msg = "TaskA's message.";
        Messenger.Default.Send<RequestMessage>(req);
    }
    private void onReceivedMessage(ResponseMessage res)
    {
        Console.WriteLine("TaskB sent the following message : " + res.msg);
    }
}

class TaskB
{
    public TaskB()
    {
        Messenger.Default.Register<RequestMessage>(this, onReceivedMessage);
    }

    public void run(CancellationToken token)
    {
        //do something
    }

    private void onReceivedMessage(RequestMessage req)
    {
        Console.WriteLine("TaskA sent the following message : " + req.msg);
        ResponseMessage res = new ResponseMessage();
        res.msg = "TaskB's message.";
        Messenger.Default.Send(res);
    }
}

class RequestMessage
{
    public string msg { get; set; }
}

class ResponseMessage
{
    public string msg { get; set; }
}

さいごに

これでひとまずやりたいことはできましたが、より良い方法が見つかったら追記します。

LINE Messaging APIを使用したbotの作成

はじめに

LINE Botを作成してみたので本エントリにまとめます。

Herokuをはじめる

botアプリケーションはHerokuにデプロイするため、 こちらの手順に従ってHerokuアカウントの登録、Heroku CLIのインストールと使い方を学びます。

Herokuはアプリケーションをクラウドでビルド、実行、運用できるPaaSです。 devcenter.heroku.com

LINE Developersコンソールでチャネルを作成

LINEが提供するMessaging APIを使用したbotを作成するためには、 開発者アカウントの登録とLINEプラットフォームを使用するために必要な"チャネル"を作成する必要があります。 手順はこちらの記事にまとまっています。 qiita.com

次のようにチャネルの作成ではアプリ名を'My bot'としました。 f:id:hiroki-sawano:20180504073716p:plain

Herokuにはmy-bot-201805としてsample-spring-boot-echoをデプロイしました。 f:id:hiroki-sawano:20180504073733p:plain

これでLINEからMy botにメッセージを送ると、送信したメッセージがMy botから返ってきます。 f:id:hiroki-sawano:20180504073748p:plain

サンプルbotの修正

ここまででEchoサンプルボットの動作確認ができました。
続けて少しだけサンプルアプリsample-spring-boot-echoに手を加えてみます。

まず、Herokuからmy-bot-201805リポジトリをgitでクローンします。

$ heroku git:clone -a my-bot-201805
Cloning into 'my-bot-201805'...
warning: You appear to have cloned an empty repository.

しかし、上記に示すように空のリポジトリをクローンした、と警告されます。 この事象についてはこちらのヘルプに載っていました。
どうやらsample-spring-boot-echoのREADMEで行なったように、 Herokuボタンを使用したHeroku API経由のデプロイだとこのような結果になるようです。
Why do I see a message 'You appear to have cloned an empty repository' when using `heroku git:clone`? - Heroku Help

同ヘルプページのResolutionに従ってHerokuボタンのリンクからgitリポジトリのURLを取得し、git remote addgit pullすれば良いです。

$ cd my-bot-201805/
$ git remote add origin https://github.com/line/line-bot-sdk-java
$ git pull origin master

それではbotに修正を加えます。
以下の通り、EchoApplication::handleTextMessageEvent()に"How are you?"というメッセージを受け取った場合には"I'm great!"と応答するコードを追加します。

EchoApplication.java

@EventMapping
public TextMessage handleTextMessageEvent(MessageEvent<TextMessageContent> event) {
    System.out.println("event: " + event);
    if (event.getMessage().getText().equals("How are you?")){
        return new TextMessage("I'm great!");
    } else {
        return new TextMessage(event.getMessage().getText());
    }
}

変更を反映します。

$ git add .
$ git commit -am "Make it more interactive"
$ git push heroku master

LINEからメッセージを送ると期待通りに動作することが確認できました。 f:id:hiroki-sawano:20180504081944j:plain

さいごに

bot作成は思った以上に簡単にできることがわかりました。なかなか面白いです。
実用的なbotアプリの実装に向けてこれからアイディアを捻出してみようと思います。

Vimのmakeで任意の名前のMakefileを指定する

はじめに

Vimmakeコマンドは便利ですが、任意の名前のMakefileをオプションで指定できそうもありません (カレントディレクトリのMakefileを探しにいきます)。
そのため所属するプロジェクトでMakefileの格納先ディレクトリや命名規則が規定されている場合は不便です。

そこで本エントリではこの問題の解決策をまとめます。

問題

Cobolプログラムで説明します。
次に示す例では、cobファイルとMakefileを格納しているディレクトリが異なり、 Makefileには<PROGRAM-ID>.makといった名称をつけるルールが定められています。

$ tree
.
├── make
│   └── hello.mak
└── source
    └── hello.cob

hello.cob

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLO.
       PROCEDURE DIVISION.
       MAIN SECTION.
           DISPLAY 'HELLO WORLD!!'.
           STOP RUN.

hello.mak

hello.o    :    hello.cob
  cobc -x --free hello.cob

このような環境においてhello.cobVimで編集中にmakeしたい時、../make/hello.makを指定する方法を考えます。

対応策

ただ単にmakeしたいだけであれば、:!make -f <makefile_name>を実行すれば良いです。
しかしこの場合、Quickfixリストが機能しないため良い解決策とは言えないでしょう。

そこでmakeprgを使用します。
makeprgを使用することで:makeを実行した時に発行されるコマンドを任意に設定することができます。

次の設定を.vimrcに追加します。
ノーマルモードでF5を押下した時にCBLCOMP関数を呼び出す(1行目)
makeprgmake -f ../make/<cob_file_name>.makを組み立てて設定(4行目〜8行目)
makeコマンドを実行(9行目)

.vimrc

001 nnoremap <F5> :call CBLCOMP()<CR>
002 
003 function! CBLCOMP()
004  let &makeprg='make -f '
005  let &makeprg.=expand('%:p:h:h')
006  let &makeprg.='/make/'
007  let &makeprg.=expand('%:t:r')
008  let &makeprg.='.mak'
009  exec ':make'
010 endfunction

これで、Vim上でF5を押すだけで編集中のプログラムに対応したMakefileを指定してmakeを実行し、make結果をQuickfixリストで確認できるようになりました。

f:id:hiroki-sawano:20180501235608p:plain
makeで構文エラーを検出した場合

Cygwinの個人的セットアップ(GNU Global + Pygments + Vim)

はじめに

自宅ではMacですが職場ではWindows7なのでターミナルで操作したいときはCygwinを使うようにしてます。 本エントリではCygwinをインストールする際に、まず始めに実施する個人的セットアップをまとめます。

Cygwinのインストール

以下のURLからCygwinインストーラーをダウンロードしてインストールします。 https://cygwin.com/install.html

インストールの過程では最低限以下のパッケージを選択します(あくまで個人的に必要なものです)。

  • gcc-core
  • libncurses-devel
  • make
  • python3
  • python3-pygments
  • vim
  • wget
  • git

パッケージ管理ツールの整備

Cygwinインストーラでもパッケージの管理が可能ですが、ターミナル上で利用可能なツール「apt-cyg」をインストールします。

wgetのプロキシ設定(必要な場合のみ)

/etc/wgetrc

 82 # You can set the default proxies for Wget to use for http, https, and ftp.
 83 # They will override the value in the environment.
 84 https_proxy = ...
 85 http_proxy = ...
 86 ftp_proxy = ...
 87 proxy_user = ...
 88 proxy_passwd = ...
 89
 90 # If you do not want to use proxy at all, set this to off.
 91 use_proxy = on

apt-cygのインストール

$ wget https://raw.githubusercontent.com/transcode-open/apt-cyg/master/apt-cyg
$ chmod 755 apt-cyg
$ mv apt-cyg /usr/local/bin/

apt-cygでは次のようなコマンドでパッケージの管理ができます。

  • パッケージを検索
$ apt-cyg searchall <package_name>
  • インストール済みのパッケージを検索
$ apt-cyg search <package_name>
  • パッケージをインストール
$ apt-cyg install <package_name>

GNU Globalのインストール

以下のURLからglobal-6.6.2.tar.gz(2018/2/20現在最新)をダウンロードします。 http://www.gnu.org/software/global/download.html

Cygwin上で以下のコマンドを実行し、GNU Globalをインストールします。

$ tar xvf global-6.5.6.tar.gz
$ cd global-6.5.6
$ ./configure
$ make
$ make install

プラグインvimにインストール

$ mkdir -p ~/.vim/plugin
$ cp /usr/local/share/gtags/gtags.vim ~/.vim/plugin/gtags.vim

.vimrcにGtags用のショートカットを追加

.vimrcは後述するリポジトリから取得するため普段省略しますが、既に作成済みの.vimrcがある場合はGtags用に以下のマッピングを追加します。

~/.vimrc

map <C-g> :Gtags
map <C-h> :Gtags -f %<CR>
map <C-j> :GtagsCursor<CR>
map <C-n> :cn<CR>
map <C-p> :cp<CR>

PygmentsとGNU Globalの連携

以下のコマンドを実行します。

$ cp /usr/local/share/gtags/gtags.conf ~/.globalr

.globalrを以下の通り編集します。

~/.globalr
変更前

default:\
         :tc=native:

変更後

default:\
        :tc=native:tc=pygments:

(参考)gtagsの使用方法

解析対象のソースファイルを含むディレクトリ(最上位ディレクトリで良い)で以下のコマンドを実行することで タグファイルを作成できます。

$ gtags -v

以下のファイルが同ディレクトリに作成されていることが確認できます。
* GPATH
* GRTAGS
* GTAGS

これで、vimから変数名などにカーソルを合わせて<C-j>で参照関係を検索し、 <C-n>で次のファイルに移動、<C-p>で前のファイルに移動可能になります。

(参考)htagsの使用方法

htagsコマンドをタグファイルがあるディレクトリで実行することで、コードをWebブラウザで確認するためのhtmlファイルを出力できます (オプションは適宜変えて下さい)。

$ htags -safno -v --html-header=./encode_meta.html

日本語文字の文字化けを防ぐためにmetaタグの指定を--html-headerオプションで行います。 「./encode_meta.html」には以下の通り文字コードを指定します。

<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />

ドットファイルの取得

ドットファイルはgitで管理しているのでcloneします。

必要に応じて次の通りプロキシの設定を行います。

$ git config --global http.proxy http://<username>:<userpsw>@<proxy>:<port>
$ git config --global https.proxy https://<username>:<userpsw>@<proxy>:<port>

~/.gitconfig

[http]
        proxy = http://<username>:<userpsw>@<proxy>:<port>
[https]
        proxy = http://<username>:<userpsw>@<proxy>:<port>

続けて以下のリポジトリをcloneして、setup.shを実行します。 github.com

$ cd ~
$ git clone https://github.com/hiroki-sawano/dotfiles.git
$ sh ~/dotfiles/setup.sh

HiRDBで表のマトリクス分割

はじめに

データベースシステムのデータ格納方式は、性能・信頼・運用性等を考慮した上で決定する必要があります。 本エントリではこの目的において、HiRDBでデータの物理配置を制御する「表のマトリクス分割」を試してみた結果をまとめます。

表の横分割の種類

HiRDBでは表を分割して格納する場合に、大きく次の分割方法を選択できます。 このうち、今回は3点目のマトリクス分割でキーレンジ分割+FIXハッシュ分割を行います。

  • キーレンジ分割
    キーとなる値の格納条件指定または境界値指定で表を分割する。
  • ハッシュ分割
    ハッシュ関数を使用して、均等に表を複数のRDエリアに格納する。
  • マトリクス分割※
    第1次元分割列で境界値指定のキーレンジ分割をし、分割されたデータをさらに第2次元分割列で分割する。第2次元分割列には次の分割方法が指定できる。
    (a) 境界値指定のキーレンジ分割
    (b) フレキシブルハッシュ分割
    (c) FIXハッシュ分割
    ※HiRDB Advanced High Availabilityが必要

想定するシステム構成

次のように複数のRDエリアが異なるディスクのHiRDBファイルシステム領域に割り当てられている場合に、 第1次元のキーレンジ分割によって、ディスク障害時の影響範囲を局所化し(信頼性向上)、 第2次元のFIXハッシュ分割でI/Oの並列処理を可能とする(性能向上)ことが狙いです。 f:id:hiroki-sawano:20180212192455p:plain

  • FES(フロントエンドサーバ):SQLの解析/最適化処理やBESへの指示などを担うサーバ
  • BES(バックエンドサーバ):FESからの指示に従ってデータベースへのアクセスや排他制御、演算処理などを行うサーバ
  • RD_T1~4:ユーザ用RDエリア(テーブル用)
  • RD_I1~4:ユーザ用RDエリア(インデクス用)

マトリクス分割の確認

それでは実際にマトリクス分割を行う表を定義し、行挿入時に指定のRDエリアがデータ格納先として使用されることを確認します。

実行環境

  • Red Hat Enterprise Server Linux 7(64-bit)/バージョン7.3
  • HiRDB Server with Additional Function Version 9
    • HiRDB/Parallel Server Version 9.65
  • HiRDB Advanced High Availability Version 9

テーブル定義の作成

列"row_type"でキーレンジ分割し、列"id1"と列"id2でFIXハッシュ分割します。
キーレンジ分割での境界値の指定は、PARTITIONED BY MULTIDIMに続けて昇順に行います。 以下の例では'10'と指定してあるため、row_typeが'10'以下の行と'10'を超過する行がそれぞれ異なるRDエリアに分割されます。
FIXハッシュ分割では、分割キーに「キー値の偏りが少ない」、「キーの値に重複が少ない」列を選択します。 また、ハッシュ関数は複数用意されていますが、リバランス表でない場合最も均等にハッシングされるHASH6を選びます。

この定義によって次の通り動作することが期待できます。
<row_type <= '10'の場合> RD_T1とRD_T2に格納
<row_type > '10'の場合> RD_T3とRD_T4に格納

MATRIX_TEST.sql

CREATE TABLE "MATRIX_TEST" (
  "id1"          INTEGER      NOT NULL,
  "id2"          INTEGER      NOT NULL,
  "row_type"     CHAR(2)      NOT NULL,
  "row_value"    VARCHAR(100)
)
 PARTITIONED BY MULTIDIM
 ("row_type" (('10')),
 FIX HASH HASH6 BY "id1","id2")
                   IN ((RD_T1,RD_T2),
                       (RD_T3,RD_T4))
 PRIMARY KEY ("id1", "id2", "row_type")
                   IN ((RD_I1,RD_I2),
                       (RD_I3,RD_I4))
;

次の通り作成したテーブル定義のSQLを実行します。

$ pddef < ./MATRIX_TEST.sql
KFPA12000-I Processing of SQL completed
$ pdsql
COMMAND ?     +----2----+----3----+----4----+----5----+----6----+----7----+
tables;
 TABLE_SCHEMA                   TABLE_NAME                     N_COLS N_INDEX CREATE_TIME
 ------------------------------ ------------------------------ ------ ------- --------------
 xxxxxxx                        MATRIX_TEST                         4       1 20180212190159

データの挿入と表分割の確認

次のデータをMATRIX_TESTに挿入しながら、経過をDB状態解析機能(pddbst)で確認します。

SELECT * FROM MATRIX_TEST;
 id1          id2          row_type row_value
 ------------ ------------ -------- -----------------------------------------------------
            1            1 10       ABCD
            1            2 10       ABCD
            2            2 20       ABCD
            2            1 20       ABCD
KFPX27010-I            4 rows selected

作業開始前の状態確認

テーブル用ユーザRDエリアの状態確認を行っていきます。
pddbstの-tオプションで解析対象のテーブル'MATRIX_TEST'を指定します。 この時点ではどのRDエリアの使用率も0%であることがわかります(記載内容はコマンド実行結果の一部抜粋)。

$ pddbst -t MATRIX_TEST
 RD Area Name   : RD_T1
  Server        : bes1
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T2
  Server        : bes2
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T3
  Server        : bes3
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T4
  Server        : bes4
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0

row_type'10'の挿入(1行目)

RD_T1が使用されている様子が確認できます。

$ pdsql
COMMAND ?     +----2----+----3----+----4----+----5----+----6----+----7----+
INSERT INTO MATRIX_TEST VALUES (1,1,'10','ABCD');
KFPX27010-I            1 rows inserted
…
$ pddbst -t MATRIX_TEST
 RD Area Name   : RD_T1
  Server        : bes1
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T2
  Server        : bes2
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T3
  Server        : bes3
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T4
  Server        : bes4
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0

row_type'10'の挿入(2行目)

2行目の挿入ではハッシュ分割されてRD_T2が使用されました。

$ pdsql
COMMAND ?     +----2----+----3----+----4----+----5----+----6----+----7----+
INSERT INTO MATRIX_TEST VALUES (1,2,'10','ABCD');
KFPX27010-I            1 rows inserted
…
$ pddbst -t MATRIX_TEST
 RD Area Name   : RD_T1
  Server        : bes1
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T2
  Server        : bes2
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T3
  Server        : bes3
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T4
  Server        : bes4
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0

row_type'20'の挿入(1行目)

キー値を'20'にするとRD_T4にデータが格納されました。

$ pdsql
COMMAND ?     +----2----+----3----+----4----+----5----+----6----+----7----+
INSERT INTO MATRIX_TEST VALUES (2,1,'20','ABCD');
KFPX27010-I            1 rows inserted
…
$ pddbst -t MATRIX_TEST
 RD Area Name   : RD_T1
  Server        : bes1
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T2
  Server        : bes2
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T3
  Server        : bes3
          Used(Full)       Used(      Full)        Sum
  Segment   0%(  0%)          0(         0)          0
  Page      0%(  0%)          0(         0)          0
 RD Area Name   : RD_T4
  Server        : bes4
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20

row_type'20'の挿入(2行目)

次のrow_type'20'の行は、RD_T3に挿入されました。

$ pdsql
COMMAND ?     +----2----+----3----+----4----+----5----+----6----+----7----+
INSERT INTO MATRIX_TEST VALUES (2,2,'20','ABCD');
KFPX27010-I            1 rows inserted
…
$ pddbst -t MATRIX_TEST
 RD Area Name   : RD_T1
  Server        : bes1
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T2
  Server        : bes2
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T3
  Server        : bes3
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20
 RD Area Name   : RD_T4
  Server        : bes4
          Used(Full)       Used(      Full)        Sum
  Segment 100%(  0%)          1(         0)          1
  Page      5%(  0%)          1(         0)         20

MacにPostgreSQLインストールとSQL実行計画の確認

はじめに

最近は業務でDBMSをよく扱うことからデータベース関連の書籍を読んでいます。
SQL実践入門」は性能に目を向けた内容で面白かったです。

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

本書籍ではSQLの実行計画を読み解くことでSQLをチューニングしています。 紹介されている内容を実際に試してみるため、MacBookに本書籍で説明に用いているPostgreSQLをインストールすることにしました (業務で扱っているのはHiRDBですが、基本的な考え方はどのDBMSでも通用するはずなのであまり気にしないことにします)。

PostgreSQLのインストール

Homebrewでインストールします。インストールしてない場合は次のコマンドでインストールしてください。

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

続けてPostgreSQLをインストールして、'initdb'でデータベースクラスタ*1を作成します。

$ brew install postgresql
$ initdb /usr/local/var/postgres -E utf8                                                                                                                    
$ postgres --version
 postgres (PostgreSQL) 10.1

postgresサーバの起動

PGDATA環境変数でデータの物理的な配置を指定します。

~/.bash_profile

export PGDATA=/usr/local/var/postgres

'pg_ctl'でサーバを起動します。 -Dオプションを使用するとデータベースファイルの場所を指定することができます。 以下の例では省略しているため.bash_profileのPGDATA環境変数で指定した内容で動作します。

$ pg_ctl -l /usr/local/var/postgres/server.log start
waiting for server to start.... done
server started

データベースの作成

新規作成するデータベースの名称と所有者とするユーザを指定して'createdb'を実行します。

$ createdb example-db -O hiroki_sawano
$ psql -l
                                          List of databases
    Name    |     Owner     | Encoding |   Collate   |    Ctype    |        Access privileges        
------------+---------------+----------+-------------+-------------+---------------------------------
 example-db | hiroki_sawano | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres   | hiroki_sawano | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0  | hiroki_sawano | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/hiroki_sawano               +
            |               |          |             |             | hiroki_sawano=CTc/hiroki_sawano
 template1  | hiroki_sawano | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/hiroki_sawano               +
            |               |          |             |             | hiroki_sawano=CTc/hiroki_sawano
(4 rows)

データベースへの接続とSQLの実行

'psql'(PostgreSQL対話的ターミナル)でデータベースに接続します。
試しに'numbers'表を作成し、複数の行を適当に挿入しました。

$ psql -U hiroki_sawano example-db
psql (10.1)
Type "help" for help.

example-db=# create table numbers(num integer primary key);
CREATE TABLE
example-db=# insert into numbers values(1);
INSERT 0 1
example-db=# insert into numbers values(3);
INSERT 0 1
example-db=# insert into numbers values(4);
INSERT 0 1
example-db=# insert into numbers values(7);
INSERT 0 1
example-db=# insert into numbers values(8);
INSERT 0 1
example-db=# insert into numbers values(9);
INSERT 0 1
example-db=# insert into numbers values(12);
INSERT 0 1
example-db=# select * from numbers;
 num
-----
   1
   3
   4
   7
   8
   9
  12
(7 rows)

SQL実行計画の確認

EXPLAINコマンドに続けてSQLを実行することで実行計画を確認できます。
WHERE句を省略したSELECT文ではSeq Scanで全行が順にスキャンされ、 WHERE句に主キー'num'への条件を付与したSELECT文ではインデクスが効いてIndex Only Scanされている様子が確認できます。

example-db=# explain
example-db-# select * from numbers;
                        QUERY PLAN                         
-----------------------------------------------------------
 Seq Scan on numbers  (cost=0.00..35.50 rows=2550 width=4)
(1 row)

example-db=# select * from numbers where num = 3;
 num 
-----
   3
(1 row)

example-db=# explain
example-db-# select * from numbers where num = 3;
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Index Only Scan using numbers_pkey on numbers  (cost=0.15..8.17 rows=1 width=4)
   Index Cond: (num = 3)
(2 rows)

参考

MacにPostgreSQLをインストール - Qiita
PostgreSQL: Documentation: 10: PostgreSQL 10.2 Documentation

*1:1つのサーバインスタンスで管理されるデータベースの集合

Synology DiskStation DS215jにSpring bootアプリをデプロイ

はじめに

自宅で常時稼働しているSynologyのDS215jに先日構築した'roomba_client'をデプロイし、
ネットワーク上の各デバイスからいつでも簡単にルンバを操作できるようにしました。

hiroki-sawano.hatenablog.com

環境

DS215jにSSH接続

DS215jにSSH接続するためには設定を変更する必要があります。
Control Panelで右上の「Advance Mode」を押下します。

f:id:hiroki-sawano:20180106043316p:plain

遷移先の画面で「Terminal & SNMP」を選択します。 f:id:hiroki-sawano:20180106043408p:plain

Enable SSH Serviceにチェックを入れ、Portにポート番号を指定します。 f:id:hiroki-sawano:20180106043430p:plain

以上で、ターミナル等からSSH接続が可能になります。

$ ssh <user_name>@<ds215j_ip_address> -p <port_number>

ビルド

DS215jにデプロイするSpring bootのFully Executable Jarを作成するため、
build.gradleに次の設定を追加し、gradle buildします。

build.gradle

springBoot {
  executable = true
}
$ gradle build

デプロイとサービス登録

DS215にビルドしたroomba_client.jarを配置します。

$ ls <deploy_destination>
roomba_client.jar

Upstartでサービスを管理するため/etc/initにroomba-client.confを作成します。
これでroomba-clientがdaemon化され、システム起動時に自動で立ち上がるようになりました。 /etc/init/roomba-client.conf

description "roomba_client daemon"
author "Hiroki Sawano <hiroki.sawano.2512@gmail.com>"
start on runlevel [2345]
stop on runlevel [016]
respawn # attempt service restart if stops abruptly
exec <java_installation_path>/java -jar <deploy_destination>/roomba_client.jar

動作確認

roomba-clientを起動します。 ここでは手動で起動しています。

$ initctl list | grep roomba-client
roomba-client stop/waiting
$ sudo start roomba-client
roomba-client start/running, process 11518
$ sudo tail -f /var/log/upstart/roomba-client.log
…
…jp.gr.java_conf.hs.roomba.client.Roomba  : Connected to Roomba
…
…j.g.j.hs.roomba.client.RoombaClient      : Started RoombaClient in 67.775 seconds (JVM running for 71.916)

Webブラウザからアプリケーション(<ds215j_ip_address>:8080)にアクセスすると正常起動していることが確認できます。

f:id:hiroki-sawano:20171203174800p:plain