まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2020/07

(このブログエントリは自作DBMS Advent Calendar 2020 の 12/3 にエントリーしています)


偉そうなブログタイトルになってしまいました、申し訳ない。


業務でも個人サービスでもブロックチェーンを使う機会があります。業務で携わることで世の中のお客様がどういった用途でどのようにブロックチェーンを使おうとしているのか、という貴重な情報を得ることができると同時に、企業内利用ゆえの様々な制約に直面することもあります。また個人サービスでもブロックチェーンを使うことで業務での必要十分な範囲にとらわれない(自分が挑戦してみたい)実装にチャレンジすることもできています。いろいろ恵まれた立場であることを実感しています。

一方で、この2つの立場でブロックチェーンを活用していると、「自分が理解してるブロックチェーン」と「実際のブロックチェーンプラットフォーム」との間に存在しているギャップが気になることもあります。このギャップは自分のブロックチェーンに対する理解がまだまだ足りないことに起因している可能性が大きいこともわかっていますが、それはつまり「ブロックチェーンは難しいもの」という先入観にも原因があるのかもしれません。

何を言いたいのかと言うと、自分のようなアプリケーション開発者の視点から考えるブロックチェーンはこういうものであってほしい(こういうものであってくれると使いやすいし、管理しやすい)んだけど、現実はそうではない、ということに何度か直面するのでした。具体的にいくつか挙げるとこのようなことです:


0 「ブロックチェーン」としての定義を満たしている

これは要件というよりも、後述する「ブロックチェーン」という用語への共通理解のための項目です。例えば日本では一般社団法人日本ブロックチェーン協会このような定義をしています。この中の「広義のブロックチェーン」で考えられるものが(広い意味での)ブロックチェーンであり、ここに定義された条件を満たしていれば、仮にサーバーノードを管理する権限を持っているような立場の人であっても、その中身を改ざんすることは困難になる、というものです。この「改ざんが困難」な特徴によって(法律などでルールを作らなくても、技術的に)記録内容の正当性を保証できるようになるもの、それがブロックチェーンだと理解しています。

で、特に開発者の視点ではブロックチェーンがここから下の要件を満たすものであってもらえるといろいろ嬉しいなあ、、と思っていまして、それを3つほど列挙させていただきます。


1 可能な限りブロックチェーンを意識せずにアプリケーション開発ができる

上述したようにブロックチェーンに関する最小限の知識はアプリケーション開発者が理解しているべきであると思いますが、一方でアプリケーション開発者がブロックチェーンを意識して開発すべきであるとは思いません。極端な言い方ですが、ブロックチェーンの存在すら知ることなく「開発者がデータベースを使ってアプリケーションを開発したら、データベースへのトランザクションは全て自動的にブロックチェーンに記録されて、改ざんが困難になったり、改ざんされてもすぐバレる」ようにできると理想的だと考えています。 要はブロックチェーンというインフラ部分をアプリケーション開発者の責任範囲から可能な限り分離したい、という意見です。


2 プライベートネットワークからもブロックチェーンに参加できる

ブロックチェーンネットワーク内に存在するノード間では台帳情報を分散管理します。したがって全てのノードがパブリックなネットワークに存在していて自由に同期をとることができることが理想ではあります。

一方、実際に企業のサービスがブロックチェーンネットワークに参加する場合、パブリックネットワークでの運用を前提とする時点で懸念が生じることがあります。比較対象として相応しいかどうかわかりませんが、例えば企業内で使っているデータベースをパブリッククラウド上に移行することですら問題になるのが現実で、そんな中で台帳情報を共有するブロックチェーンはパブリックでもオッケー、となるわけがありません。わがままにも聞こえますが「ネットワークはパブリックで構築してもいいが、うちが用意するノードはプライベートな状態で運用したい」のが本音となっているケースも散見します。

このわがままにも聞こえる懸念に対して「ブロックチェーンだからパブリックネットワークでないといけないのか?」という観点で考えるとどうでしょう? ある程度中身まで理解しているブロックチェーン技術者にとっては当然のようにパブリックネットワーク運用前提で考えてしまうかもしれませんが、ブロックチェーンを活用したいと考えている経営者からは厄介な制約に感じられてしまうものかもしれませんし、上述の「1 可能な限りブロックチェーンを意識せずにアプリケーション開発ができる」ようになると、アプリケーション開発者としてもネットワーク・トポロジーの制約を意識せずに開発できるようになるのが理想と考えています。 開発者と経営者、どちらもブロックチェーンを活用したいという想いは同じなのに、協力しあえないのはあまりに不幸な話と思えるため、それならなんらかの制約(※)が加わるにせよ、開発者側から歩み寄る形でプライベートネットワーク内からもブロックチェーンネットワークに参加して同期を取る方法を(技術的に不可能でないのだとしたら)検討する価値はあるのではないか、と考えています。そしてブロックチェーンそのものにそういった機能がはじめから付与されていれば歩み寄りも最小限で済むため理想的ではないかと思っています。

※具体的にはブロックチェーンをプライベート型やコンソーシアム型にせざるを得なくなる、といった制約です。


3 ブロックチェーンに全てのトランザクションが記録されるのであれば、例えばノードの引っ越し時やデータ破損時などにブロックチェーンからリストアできるはず

実は僕がブロックチェーンの存在や概要を知って、真っ先に思いついたブロックチェーンのメリットがこれでした。そもそも開発するサービス全体のうち、ブロックチェーンには何を(どの部分の情報を)記録するのかという問題があることは理解しています。例えばユーザー情報はブロックチェーンには記録せずに運用するけど、お金の動きに関わる部分だけはブロックチェーンに記録する、という考え方です。その意味ではサービス全体の情報がブロックチェーンに格納されるわけではないことは理解しておく必要があります。

でもブロックチェーンに記録されている情報に関してはそこに全トランザクションが記録されているわけだから、仮にデータ部分が破損したケースであっても、ブロックチェーンを最初からたどることで理論上は元通りに戻せるはず(つまりブロックチェーンからリストアできるはず)、だと思ったのでした。これができると運用者は楽だし、運用者が困っている面倒な部分の開発も楽になります。

ところが実際にそのようなリストア機能をもったブロックチェーンは自分の知る限りでは存在していません。もちろん実際のデータベースとの紐付け情報が必要だったり、どのサービスで使うデータに関するトランザクションなのか、といった情報も含めてブロックチェーンに記録されていないと実現できないことは理解できます。ブロックチェーン側のデータフォーマット仕様にも関わる話になってしまうと後からの変更や対応は難しいかもしれないなあ、といった事情もあるのでしょう。

この「ブロックチェーンからリストア」の実現について、色々面倒な制約があってできないのか、それともあまり重要視されていないのかわかりません。ただ自分のような開発者・運用者の視点からはこれがブロックチェーン側の機能の一部として実現できたらバックアップへの不安という心理的な負担を軽くことのできる開発プラットフォームが実現できて、それは開発にブロックチェーンを採用するメリットにもなり得るのではないか、、、と感じていたのでした。



そして、実はいま上述した3点を含む「開発者視点で考えた、使いやすいブロックチェーン」を実装中です。「開発者視点」と言いつつ、あくまで「自分の視点」ではありますけど(笑)。簡単に特徴を列挙するとこのような感じ:
  • 名前は HATOYA 、デフォルトの実行ポート番号は 4126
  • 表向きは REST API でデータベースやデータベース内のドキュメントを CRUD(Create/Read/Update/Delete 加えて Search も) 可能な JSON データベースシステム
  • データベースやドキュメントへの変更トランザクションは全て内蔵ブロックチェーンに自動記録
  • 複数ノードでブロックチェーンネットワークを作ると、参加するノードのブロックチェーン情報は1つにマージされる。つまりブロックチェーンネットワークで1つのブロックチェーンを共有管理する(データベース情報は共有しない)
  • ブロックチェーンネットワークにはプライベートネットワークからも参加可能(ただしその場合は push 型の同期処理となる)
  • サーバー引っ越し時や、データ破損時などにブロックチェーンからデータベースをリストアすることができる

なおスケーラビリティやアベイラビリティ、セキュリティといった非機能要件はまだ不十分な上、機能実装も現時点ではあくまで予定としているものではあるのですが、上述3点については実現にも目処が立っています:
20200724



現在は複数ノード環境での動作テストやデバッグ、このブロックチェーンを使ったツールやサンプルアプリなどを開発するのが中心の段階に至っています(つまり上述の3点の特徴含めてある程度は動く状態になっています):
2020072400


近い将来になんらかの形で公開予定です。本来アプリケーション開発とは利用者の観点を重要視するべきで、そこを開発者観点で作ることに異論を唱える人が存在するであろうことも理解しています。ただブロックチェーンってそもそもエンドユーザーが意識するべきものではないし、「ブロックチェーンの利用者」が誰なのかを考えると、それは「開発者」であるという視点があってもいいのかな、と。そう考えた時の答の1つになれればいいと思っています。あと深く勉強しなくてもすぐに実装が開始できるので、PoC 的な用途には向いているんじゃないかなあ、とも。

まだ動くものを公開していないので感想を持ちにくいということも理解していますが、こういった考え方で作られるブロックチェーンへの賛同や反論などありましたら、傷が浅いうちに(笑)ぜひ聞いておきたいです。コメント等でご意見いただければと。


(追記 公開しました)
HATOYA の Docker イメージを公開
HATOYA をマルチノード運用して、ブロックチェーン情報を分散管理する

以前にとある人から
 (例えば仮想マシン環境と比較して)コンテナ環境のデメリットはなんですか?
と質問され、一瞬返答に詰まってしまったことがありました。メリットを考えたことはあったけど、積極的にデメリットを考えたことがなく、即答できませんでした。ちゃんと正しく理解していないとなかなか難しい質問だと思っています。

改めて時間のある時に考えてみるといくつか思いつきます。まあ「デメリット」といえるかどうかはともかく、「VMでできてコンテナでできないこと」はいくつかあります。

その一つが "cron" ジョブだと思っています。特定時刻とか、何分おきにとか、実行タイミングのスケジュールを決めた上で実行する機能です。例えば kubernetes であれば CronJob を使って実現するなど、厳密には「コンテナで実現できない」わけではないのですが、そのコンテナ環境に合わせた対応が必要になるのはそれはそれでデメリットになりえますよね。

一方、アプリケーション開発レベルでは、これらのスケジュールジョブを併用することでアプリケーションとしての必要な機能を実現することは珍しくありません。1分毎にどこかからデータを取得して更新するとか、毎日○時に自動的にバックアップを取得するとかといった場合です。それらの機能が必要なアプリケーションをコンテナ環境で動かす可能性がある中で実装する場合、どういった方法を検討する必要があるでしょうか?


その答えの1つが「アプリケーションレベルで(アプリケーションの機能の一部として) cron ジョブを実装する」方法です。Node.js アプリケーションの場合は Node-Schedule ライブラリを使うと簡単に実現できそうだったので、その内容を以下で紹介します:
2020071200



Node-Schedule は Node.js で使えるライブラリで、cron ライクなスケジュールジョブを比較的簡単に(setTimeout とかを意識することなく)実現できます。またスケジュールの定義フォーマットは cron のものと互換性があるので、crontab に1行追加する感覚で、アプリケーション内にスケジュールジョブを追加・更新・削除できるものです。アプリケーションの中でスケジュールジョブを定義できるので、アプリケーションの実行環境(実機とか、VM とか、コンテナとか、・・)を意識する必要もありません。

例を1つ記述しておきます。以下のコードで1分おきにコンソールにメッセージを表示するウェブアプリケーションが作成できます(Node-Schedule に関係している部分のみ赤字):
// app.js
var schedule = require( 'node-schedule' );
var express = require( 'express' );
var app = express();

//. 毎分実行
schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});


app.get( '/', function( req, res ){
  res.write( JSON.stringify( { status: true }, null, 2 ) );
  res.end();
});


var port = process.env.PORT || 8080;
app.listen( port );
console.log( 'server started on ' + port );

上記コードから赤字部分を抜くと、ごくシンプルな Node.js + Express のウェブアプリケーションになるのがわかると思います。つまり Node-Schedule に関係しているのは赤字部分の実質4行だけです。

で、その赤字部分で何をしているのかというと、まず先頭行で require() して Node-Schedule ライブラリのモジュールを呼び出します:
// app.js
var schedule = require( 'node-schedule' );

そして scheduleJob() メソッドを使ってジョブを(イメージとしては cron に)登録します:
//. 毎分実行
schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});

この第一パラメータは cron に登録する時に指定する時刻フォーマットと互換性のある文字列表現を使います。'* * * * *' は「毎分実行」を意味しています。

そして第二パラメータには該当時刻になったら実行するコールバック関数を指定します。上の例では 'running a task every minute' とコンソールに表示するだけの内容にしていますが、実際にはここに crontab の最後に指定するコマンドを登録することになります。これで1分ごとに 'running a task every minute' という文字列がコンソールに表示され続けるジョブが登録できたことになります。


なお、一度登録したジョブをキャンセルするには登録時の scheduleJob() メソッドの実行結果オブジェクトを受け取り、そのオブジェクトの cancel() メソッドを実行します:
//. 毎分実行
var job = schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});

  :

job.cancel(); //. 登録したジョブをキャンセル

ジョブの実行条件や実行内容を更新する場合は一度キャンセルしてから再登録することで実現できます。


最近のクラウド環境は PaaS 化が進み、どういうコンテナ環境を使っているのかよくわからないことがあるかもしれません。アプリケーションを Node.js で記述するという条件はありますが、この方法で実装していればコンテナ環境に依存しないスケジュールジョブが実現できそうです。


Node.js + Express 環境でウェブアプリケーションを動かす際のリクエストタイムアウト値(デフォルトは2分)を変更する方法を調べました。

Node.js はシングルスレッドなので、あまりタイムアウト値を長くしすぎるとそれはそれで支障がでることも考慮する必要があります。その上でタイムアウト値を変更するには以下のようにします:
var express = require( "express" ),
    app = express();

  :
  :

var app_server = app.listen( 8080 );
app_server.timeout = 1000 * 60 * 3;   //. 3分

ポート番号(上例では 8080)を指定して listen() を実行した結果の返り値(app_server)を取得し、その timeout プロパティ値をミリ秒単位で指定するだけです。


くどいようですがもう一度。Node.js はシングルスレッドなので、この値を大きく設定しすぎると、リクエスト処理に待ち行列が発生しやすくなるので要注意を。


このページのトップヘ