また、 専用掲示板 を用意しましたので、こちらのほうもご利用ください。
「まごの手」は確かに便利な物なんですが、たまには「直接手で」掻きむしる あの感覚も忘れたくない、そんな事ってないですか?、いやあって欲しい...。
という訳で、いまだに「COBOL + VSAM(or VSAS)索引順編成」、「 C + C-ISAM」 等の言葉に反応してしまうそこのあなたにこそ「tyserv」は有るのです。あっ、 でもそれ以外の人もつかってね...。
ex. (/etc/rc.d/rc.local etc)
if [ -x /etc/rc.d/rc.tyserv ]; then
. /etc/rc.d/rc.tyserv -d /home/tyserv/rundir1
fi
ユーザ:パスワード:アクセス権限(*)
を設定する。特に、デフォルトのパスワードは必ず変えて下さい。
chmod 600 /home/tyserv/tyserv/etc/passwd を実行する。
(*) アクセス権限
RVJ_MAX, RBJ_MAX は設計ミス、バグ等によりジャーナルがディスク領域 を食い潰す事を回避するために導入したパラメータです。
また、perl用のアクセスライブラリ tyserv/sample/perl/tyserv.pl 及び open-cobol用インターフェース tyserv/sample/open-cobol/tycob.c を用意してます。 使い方は tyserv/sample/perl 及び tyserv/sample/open-cobol 以下のプログラ ムを参照して下さい。
各コマンドのトークンの区切り文字はタブ(0x09)です。
ステータス1 ステータス2 項目名1=値1 項目名2=値2 ... 項目名n=値n
の形式で返ります(perl等のスクリプト言語からの利用を念頭にこのような 仕様にしました)。区切り文字はタブです。
CONDITIONには以下の条件が指定できます
さらなる情報は tyserv/doc/*, tyserv/sample/perl/*.pl を参照して下さい。
$ telnet localhost 20000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. start_tran user user <-Input OK TRANSACTION START <-Response put smp1 id=1111 name=Taro salary=150000 <-Input OK INSERTED <-Response put smp1 id=2222 name=Jiro salary=200000 <-Input OK INSERTED <-Response put smp1 id=3333 name=Goro salary=250000 <-Input OK INSERTED <-Response commit <-Input OK COMMITED <-Response get smp1 pkey eq 1111 <-Input OK FOUND id=1111 name=Taro salary=150000 <-Response getnext smp1 pkey <-Input OK FOUND id=2222 name=Jiro salary=200000 <-Response getnext smp1 pkey <-Input OK FOUND id=3333 name=Goro salary=250000 <-Response update smp1 2222 name=Jirorin <-Input OK UPDATED <-Response get smp1 pkey eq 2222 <-Input OK FOUND id=2222 name=Jirorin salary=200000 <-Response delete smp1 2222 <-Input OK DELETED <-Response get smp1 pkey eq 2222 <-Input NG NOTFOUND <-Response rollback <-Input OK ROLLBACKED <-Response get smp1 pkey eq 2222 <-Input OK FOUND id=2222 name=Jiro salary=200000 <-Response end_tran <-Input OK TRANSACTION END <-Response Connection closed by foreign host.
#! /usr/bin/perl
#---------------------------------------------------------
# ソケット通信ライブラリ読み込み
use IO::Socket;
#
# tyservアクセスライブラリ読み込み
require 'tyserv.pl';
#---------------------------------------------------------
# ユーザ名設定
$user = "manager";
#
# パスワード設定
$passwd = "manager";
#
# ホスト名設定
$host = "localhost";
#
# ポート番号設定
$port = 20000;
#---------------------------------------------------------
# ソケットハンドル取得
$handle = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $host,
PeerPort => $port);
#
# ソケットハンドルの自動フラッシュ設定
$handle->autoflush(1); # so output gets there right away
#---------------------------------------------------------
# 更新トランザクション開始
($sts1, $sts2) = &ty_start_tran($handle, $user, $passwd);
#---------------------------------------------------------
# レコード内容初期化
%rec = ();
#
# レコード内容設定
$rec{id} = "9999";
$rec{name} = "Mr. X";
$rec{salary} = 900000;
#
# レコード書き込み
($sts1, $sts2) = &ty_put($handle, "smp1", \%rec);
#---------------------------------------------------------
# レコード内容初期化
%rec = ();
#
# レコード読み込み
($sts1, $sts2, %rec) = &ty_get($handle, "smp1", "pkey", "eq", ["9999"]);
#---------------------------------------------------------
# レコード項目更新
$rec{salary} *= 10;
#
# レコード更新
($sts1, $sts2) = &ty_update($handle, "smp1", ["9999"], \%rec);
#---------------------------------------------------------
# レコード削除
($sts1, $sts2) = &ty_delete($handle, "smp1", ["9999"]);
#---------------------------------------------------------
# トランザクション終了
($sts1, $sts2) = &ty_end_tran($handle);
#---------------------------------------------------------
# プログラム終了
exit 0;
IDENTIFICATION DIVISION.
PROGRAM-ID. sample1.
ENVIRONMENT DIVISION.
*------------------------------------------
* 作業領域定義
*------------------------------------------
WORKING-STORAGE SECTION.
*------------------------------------------
* 定数定義
*------------------------------------------
01 C-NULL PIC X(1) VALUE LOW-VALUE.
01 C-TAB PIC X(1) VALUE X'09'.
*------------------------------------------
* ワーク定義
*------------------------------------------
*
* sock_* 関係インターフェース項目
*
01 HOST.
02 FILLER PIC X(9) VALUE 'localhost'.
02 FILLER PIC X(1) VALUE LOW-VALUE.
01 PORT.
02 FILLER PIC X(5) VALUE '20000'.
02 FILLER PIC X(1) VALUE LOW-VALUE.
01 FD-SOCK.
02 FILLER PIC X(5) VALUE SPACE.
02 FILLER PIC X(1) VALUE LOW-VALUE.
01 SEND-DATA.
02 FILLER PIC X(1024) VALUE SPACE.
02 FILLER PIC X(1) VALUE LOW-VALUE.
01 RECV-DATA.
02 FILLER PIC X(1024) VALUE SPACE.
02 FILLER PIC X(1) VALUE LOW-VALUE.
*
* get_* 関係インターフェース項目
*
01 STAT1 PIC X(2) VALUE SPACE.
01 STAT2 PIC X(1024) VALUE SPACE.
01 G-ID PIC X(4).
01 G-NAME PIC X(20).
01 G-SALARY PIC 9(7).
*------------------------------------------
* 処理開始
*------------------------------------------
PROCEDURE DIVISION.
*
MAIN-EN.
*
* ソケットオープン
*
CALL 'sock_open' USING HOST PORT FD-SOCK.
*
* 更新モードトランザクション開始
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'start_tran' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'user' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'user' DELIMITED BY SIZE
C-NULL DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* 挿入
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'put' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'smp1' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'id=9999' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'name=Mr. X' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'salary=0900000' DELIMITED BY SIZE
C-NULL DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* 検索
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'get' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'smp1' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'pkey' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'eq' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'9999' DELIMITED BY SIZE
C-NULL DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* 項目(SALARY)を更新
*
INITIALIZE G-ID G-NAME G-SALARY.
CALL 'get_value' USING RECV-DATA 'id' G-ID.
CALL 'get_value' USING RECV-DATA 'name' G-NAME.
CALL 'get_value' USING RECV-DATA 'salary' G-SALARY.
COMPUTE G-SALARY = G-SALARY * 10.
*
* 更新
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'update' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'smp1' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
G-ID DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'salary=' DELIMITED BY SIZE
G-SALARY DELIMITED BY SIZE
C-NULL DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* 削除
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'delete' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'smp1' DELIMITED BY SIZE
C-TAB DELIMITED BY SIZE
'9999' DELIMITED BY SIZE
C-NULL DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* トランザクション終了
*
INITIALIZE SEND-DATA RECV-DATA STAT1 STAT2.
STRING 'end_tran' DELIMITED BY SIZE
INTO SEND-DATA.
CALL 'sock_send_recv' USING FD-SOCK SEND-DATA RECV-DATA.
CALL 'get_status' USING RECV-DATA STAT1 STAT2.
*
* ソケットクローズ
*
CALL 'sock_close' USING FD-SOCK.
MAIN-EX.
STOP RUN.
*------------------------------------------
* 処理終了
*------------------------------------------
END_TRAN, ABORT_TRAN を発行せずにクライアントを終了した場合は ABORT_TRAN 扱いとなります。
| STS1 | STS2 |
|---|---|
| OK | COMMITED |
| OK | COMMITED BUT RECOVERY JOURNAL OVERFLOW |
| OK | DELETED |
| OK | FOUND |
| OK | INSERTED |
| OK | NORMAL SHUTDOWN |
| OK | ROLLBACKED |
| OK | SWAPRVJ TO full_path_of_recovery_journal |
| OK | TRANSACTION ABORT, ROLLBACKED |
| OK | TRANSACTION END |
| OK | TRANSACTION END BUT RECOVERY JOURNAL OVERFLOW |
| OK | TRANSACTION START |
| OK | UPDATED |
| NG | ACCESS DENIED(ip address) |
| NG | CAN NOT OPEN DATABASE db_status=xxxx |
| NG | CAN NOT READ SOCKET |
| NG | DUPLICATE |
| NG | NO RECOVERY JOURNAL |
| NG | NO ROLLBACK JOURNAL |
| NG | NOT GRANTED |
| NG | NOTFOUND |
| NG | RECOVERY JOURNAL OVERFLOW |
| NG | REQUIRE START_TRAN OR START_TRAN_NJ OR START_TRANM |
| NG | ROLLBACK JOURNAL OVERFLOW |
| NG | SOCKET READ ERROR |
| NG | SOCKET READ ERROR, ROLLBACKED |
| NG | STATUS=xxxx |
| NG | UNKNOWN CONDITION |
| NG | UNKNOWN FUNCTION |
| NG | UNKNOWN ITEM |
| NG | UNKNOWN KEY |
| NG | UNKNOWN RECORD |
1 database typhoondb {
2 data file "smp1.dat" contains smp1;
3 key file "smp1.pk" contains smp1.smp1_pkey;
4 key file "smp1.s1" contains smp1.smp1_skey1;
5 record smp1 {
6 char id[4 + 1];
7 char name[20 + 1];
8 char salary[7 + 1];
9 primary key smp1_pkey { id asc };
10 alternate key smp1_skey1 { name asc, id asc };
11 }
12 }
1 データベース定義の開始文
database データベース名 {
o データベース名は「typhoondb」に限る
2 データ格納ファイル指定文
data file "ファイル名" contains テーブル名;
o ファイル名は「テーブル名.dat」に限る
3 主キー格納ファイル指定文
key file "ファイル名" contains テーブル名.テーブル名_pkey;
o 主キーはテーブルに1個のみ存在できる
o ファイル名は「テーブル名.pk」に限る
4 副キー格納ファイル指定文
key file "ファイル名" contains テーブル名.テーブル名_skey連番;
o 副キーはテーブルに複数個存在できる
o ファイル名は「テーブル名.s連番」に限る
o 連番は1から順に副キー毎に符番する
5 テーブル定義開始文
record テーブル名 {
6 項目定義文
7 項目定義文
8 項目定義文
char 項目名[項目長 + 1];
o 属性は「char」のみ指定できる
9 主キー定義文
primary key テーブル名_pkey { 項目名 並び順[, 項目名 並び順] ... };
o 主キーはテーブルに1個のみ存在できる
o 主キーはレコードをユニークに特定できる項目で構成しなければならない
o 並び順は昇順の場合は「asc」、降順の場合は「desc」
10 副キー定義文
alternate key テーブル名_skey連番 { 項目名 並び順[, 項目名 並び順] ... };
o 副キーはテーブルに複数個存在できる
o 副キーは重複を許す。但し、重複キーを持つレコードの並び順が不定なのは
不安なので(^^;、主キーの構成項目を末尾に追加することを推奨します
o 連番は1から順に副キー毎に符番する
11 テーブル定義の終了
}
12 データベース定義の終了
}
tyimport(typhoon-1.11.0に付属のコマンド)が作成する 定義ファイル(typhoondb.imp)にはデータベース(typhoondb)に 含まれる全てのテーブルのインポート定義が含まれています。
typhoondb.imp を typhoondb.テーブル名.imp にコピーし、該 当テーブルに関する定義のみを残すように編集します。tyimp は typhoondb.テーブル名.imp を定義ファイルとして読み込み ます。
foo.txt(各項目をタブで区切ったCSVファイル)を読み込み、デー タベース(typhoondb)に含まれるテーブル(smp1)にインポートを 行う。foo.txtの各行は必ずインポート定義ファイル(typhoondb.smp1.imp) に定義されている項目と同じ数のフィールドを含んでいなければならない。
tyexport(typhoon-1.11.0に付属のコマンド)が作成する 定義ファイル(typhoondb.exp)にはデータベース(typhoondb)に 含まれる全てのテーブルのエクスポート定義が含まれています。
typhoondb.exp を typhoondb.テーブル名.exp にコピーし、該 当テーブルに関する定義のみを残すように編集します。tyexp は typhoondb.テーブル名.exp を定義ファイルとして読み込み ます。
データベース(typhoondb)に含まれるテーブル(smp1)のデータを foo.txt(各項目をタブで区切ったCSVファイル)に出力します。
ex. テーブル `smp1' が有る場合 OBJS は以下のように定義されています。
OBJS = tyserv_smp1.o
そこにテーブル `smp2' を追加する場合 OBJS の定義は以下のように
なります。
OBJS = tyserv_smp1.o \
tyserv_smp2.o
オブジェクトの名称は "tyserv_" + table_name + ".o" でないといけ ません。
サーバは自動的にバックグラウンドで実行されます。
でもこれが業務システムの利用するデータベースとなると、最低で も1日1回のバックアップが必要でしょう。
しかし1日1回のバックアップではディスククラッシュ等のタイミ ングによって最悪24時間前の状態戻すのが精いっぱいで、その後 の入力データに関しては入力伝票をかき集めて、もう一度手作業で 入力しなおさなければなりません。業務システムに関して言えばこ れは
です。そこで tyserv では直近のバックアップ + リカバリジャーナ ルを利用してクラッシュの直前の状態にデータベースを復元するツー ル tyrecover.sh を用意しています。
tyrecover.sh の利用はバックアップのタイミングと密接に関係して きます。以下にバックアップ運用例と障害時の復旧例を示します。
00:00 23:00 00:00 23:00 00:00
tyserv +---------------+ +----------------+ +---...
cold起動 停止 cold起動 停止
backup +------+ +-----+
開始 終了 開始 終了
この例では毎日23:00〜00:00の1時間でバックアップを取っています。
09 18 09 18 09 18 09 18 09 18
tyserv +----+ +----+ +----+ +----+ +----+...
cold 停止 warm 停止 warm 停止 cold 停止 warm 停止
起動 起動 起動 起動 起動
backup +-+
この例の場合バックアップを取った直後の起動に限り、cold起動 して る点に気をつけて下さい。また、毎回の停止時にリカバリジャーナルだ けでもバックアップしておくようにするとさらに安全です。さらにジャー ナルだけ別ドライブにするのも安全です。ディスククラッシュと 同時にリカバリジャーナルまでふっ飛ぶと、どうしようもないですから (^^;。
計画停止 起動 ダウン 復旧 計画停止 起動
tyserv ...---+ +-------* - - - - - +------+ +----...
backup +-----+ | | +-----+
障害発生 |
| |
restore +-----+ |
recover +-----+
そこでtyserv-2.xではデータベースに含まれるテーブルを幾つかのグループ に分けて、そのグループ毎に別々のサーバを起動することで排他の 粒度を細かくできるように拡張を行いました。
「TABLE_LIST=テーブル名」が存在しない場合、従来と同様に全ての テーブルにアクセスできます。
注意する点として、別の「tyserv.conf」に同じテーブルを指定する事は 可能ですが、その場合そのテーブルに対する更新トランザクションの アトミック性が保証されなくなりますので決してそのような指定は行わないで 下さい。
また「SOCKET_PORT=ポート番号」は各「tyserv.conf」でユニークになるように 設定して下さい。
...とはいえ、複数の人間で開発してるとこういうプロジェクトルールが 守られないケースも発生して来ますよね(^^;。そこで、とりあえずですが サンプルディレクトリの tyserv.pl に複数トランザクションを一括統一した順序で 扱うための関数を追加しました。いかにも場当たり的な物ですが perl で アプリケーションを開発される場合には御活用下さいませ。
$ tysearch -h <-- ヘルプの表示 usage : tysearch table start_count rec_count key_name key_value ... environment value : TYPHOON_DIR $ tysearch smp1 1 3 pkey 0003 <-- テーブルsmp1のpkeyが0003以上のレコードに位置付け、1件目から3レコードをstdoutに出力する OK FOUND id=0003 name=Kiyoshi Sakamoto salary=0510000 OK FOUND id=0004 name=Masaharu Sawada salary=0470000 OK FOUND id=0005 name=Masahiko Ito salary=0300000 $
本コマンドはユーザ tyserv 以外でも実行できます。
$ tytran.sh -h <-- ヘルプの表示
usage : tytran.sh [-s server|--server server] [-p port|--port port] [-n|--ngexit] [-v|--verbose]
execute transaction in batch mode
option : -s server connect to server. default localhost.
-p port connect to port. default 20000.
-n, --ngexit exit when NG status
-v, --verbose verbose mode
$ vi trancmd.txt
#
# 先頭が `#' はコメント行
#
START_TRAN USER USER
GET smp1 pkey eq 0001
UPDATE smp1 0001 salary=9999999
COMMIT
END_TRAN
$ tytran.sh <trancmd.txt
特に、既にtyphoon-1.11.0を利用したアプリケーションを作成されてる場合は 本パッチの利用は避けられたほうが良いでしょう。