その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

CompletionStage<T>な変数を同期的に処理したいんですけど〜ってとき

Javaの話です.

CompletionStage<T>を返却するようなメソッドがあって,それを同期的に処理したい (単体テストを書くというシーンが最も多いでしょう) ということが生きているとあります.しかしCompletionStageget()のようなメソッドが生えていないので同期的に処理することができません.thenAccept()を使おうにも,例えばJUnitのテストケースではすっぽ抜けてしまってうまく扱えません (逆にテストケース以外ではうまく働くでしょう).

そんなときにどうするかというと,手っ取り早い方法はCompletableFuture<T>にキャストしてしまうことでしょう.すると,オブジェクトにget()が生えてくるので同期的に値を取り扱うことが可能となります.

例:

final CompletionStage<String> completionStage = getSomething();
final CompletableFuture<String> future = (CompletableFuture<String>) completionStage;
final String got = future.get(); // ここで同期的に取り扱える

最初からCompletableFutureで値を返却してほしいですね.以上です.

技術書典7 (く39D) にて「ATコマンドかるた」を頒布します!!!

技術書典7,SORAZINE技術組(く39D*1 にて「ATコマンドかるた」を頒布いたします.

techbookfest.org

「ATコマンドとはなにか?」という疑問については同時に頒布される SORAZINE 技術組 Vol.2019F 中の,おそらく今のところ日本で最も詳しいATコマンドの解説文章をご覧いただければと思いますが *2,その文中から抜粋しますと,

AT コマンドは AT という文字列で始まる、モデムを制御したりするためのテキストベースのコマンド群です.

というものです.ホラ,皆さん生きているとAT+COPSとかAT+CGDCONTとかよく見たり喋ったりするじゃないですか? ソレのかるたを頒布するという話です.

こんな感じ:

f:id:moznion:20190920162109j:plain

これがATコマンドふだで……

f:id:moznion:20190920162156j:plain

これが機能ふだです.

たのしい48種類のATコマンドのふだをご用意してございます! (なお,コマンドふだと機能ふだは左上の通し番号で対応が付いています)

ATコマンドふだを読み上げて機能ふだを取るもよし,機能ふだを読み上げてATコマンドふだを取るもよし,ニヤニヤ眺めるのもよしと三方よしな仕上がりとなっております! これで皆さんも遊びながらにしてATコマンドを暗記できます,やりましたね!!!

f:id:moznion:20190920161953j:plain

f:id:moznion:20190920163632j:plain

このようなかわいいパッケージでお出迎え.

ところで,いまのところこのかるたの印刷代を全て建て替えてるので売れないと年を越せません!!!!!!
技術書典7,SORAZINE技術組(く39D)でお待ちしています!!! よろしくお願いします……


【おわび】

一部ATコマンドふだに誤植がございまして,そちらは大変申し訳ありませんが人力加筆による修正を加えております

(誤)

f:id:moznion:20190920144627j:plain

AT+CCACT

(正: 修正済み)

f:id:moznion:20190920144712j:plain

AT+CGACT

以上です.

*1:よみがながtypoっているのはご愛嬌

*2:ATコマンド学習されたい向きには超おすすめです

Kyoto.なんか #5で「AWS上に構築する メンテ容易なElasticsearch System」というタイトルで話してきました

表題の通りKyoto.なんか #5で話してきました.もうかれこれ3週くらい前の話なのですね……筆不精がたたっております.

kyoto-nanka.connpass.com

内容としてはここ数年ElasticsearchをAWS上で運用しているのでそのアーキテクチャのご紹介という感じの発表をしたという次第です.現状上手いこと回っております.

この発表の後にElasticの@johtaniさんからのメンションで,クラスタまたぎのレプリケーションがElasticsearch 6.7以降サポートされていることを教えていただきました.ありがとうございます!

それにつけても最近の願いはAWS Elasticsearch Serviceにauto reshard機能が入ることですね,現場からは以上です.

builderscon 2019見本市 (土曜12時から) で自作ガジェット展示します

id:uzulla, id:mackee_wらと共に自作のE-バッジを展示します!
というかこのブログの内容ママです!!

ここには本物があります.
ぜひお立ち寄りください!!!

ところでチップのデータシートを見ながらあれこれコードを書いて試しているんですが,どうやっても一部機能を実装することが出来ないでいます,これはなんなんだ……当日お立ち寄りの方でお詳しい向きがあれば是非教えていただきたく思います!

よろしくお願いいたします.

tcpdumpで今すぐキャプチャ内容を全部ファイルにflushしてくれ!! というとき

tcpdumpはキャプチャした内容をバッファに蓄積してバッチ的にflushするので,うっかりプロセスを即断するとキャプチャしていた内容をdrainできないことがままあります.困りますね.

www.tcpdump.org

Using the SIGUSR2 signal along with the -w flag will forcibly flush the packet buffer into the output file.

というわけでここに書いてあるとおり,SIGUSR2tcpdumpのプロセスに対して発行してあげるとその時点のバッファの内容をファイルに吐き出します (ただし -w フラグによって出力先ファイルを指定している,すなわちファイル書き出しモードにしている必要がある).

If the -w option is specified, make the saved raw packet output ``packet-buffered''; i.e., as each packet is saved, it will be written to the output file, rather than being written only when the output buffer fills.

あるいは -U (--packet-buffered) フラグを -w フラグとともに利用するというのも良いでしょう.ただ,この際はバッファをバイパスして直接ファイルに書き込むような挙動となるので,tcpdumpdaemonとして常に動かしているような環境ではパフォーマンスの問題が発生する可能性があるので注意が必要そうです.


なお,-B オプションを使ってバッファサイズを0にするとどうなのか……というとこれは

tcpdump: invalid packet buffer size 0

と出て動きません.なるほど〜,そりゃそうですね.

TypeScriptを使ってアプリケーションの設定ファイルを書く

アプリケーションの設定ファイルと言えばjsonyamlなんかで書くことが多いと思うんですが,最近はTypeScriptで設定ファイルを書いております.このような感じ:

export interface ApplicationConfig {
  readonly listenPort: number;
  readonly timeout: number;
  readonly apiBaseUrl: string;
  readonly loggerOptions: LoggerOptions;
}

こういう感じで設定のinterfaceを定義しておいて

export class ProductionConfig implements ApplicationConfig {
  readonly listenPort = 5963;
  readonly timeout = 5000;
  readonly apiBaseUrl = "http://internal-api.example.com";
  readonly loggerOptions = {
    level: 'info',
  };
}

// TypeScript (JavaScript) 内で使うなら以下のようにしておくと便利そう (例A)
const productionConfig = new ProductionConfig();
export { productionConfig };

というふうに実際の設定を記述するという感じです.


メリットとしては,

  • 「何が設定項目なのか」が定義により明らかになる
  • 設定漏れが基本的に無い
    • これはinterfaceのサポートによるもの
  • 型のサポートがある
    • 設定の構造化ができる
  • 継承が使える
    • というかTypeScriptの言語機能を使ったコードが書ける

といったあたりが挙げられると思います.

interfaceの定義によって,「どういった項目が設定されるべきなのか」が「型情報も含めて」明示できるというのは利点の一つと言って良いと思います.

また,ウェブアプリケーションだと例えば本番環境用の設定,ステージング環境用の設定,開発環境用の設定……というふうに複数の設定が存在していることがままあると思うのですが,それぞれの環境のためにinterfaceを満足した設定を記述 (実装) してあげるだけで,漏れが無い (つまりそれぞれが等価な振る舞いをする) 設定を安全に用意することができるようになります.
当然のことながら,設定の実装クラスがinterfaceを実装しきらなかった場合はコンパイルエラーが出るので堅牢です.

型のサポートが得られるというのは言うまでもないですね.安全! 型定義の無い設定を取り扱う際に苦労することがままありましたからね……
あと自分で定義したclassについてもpropertyの型にすることができるので便利です.

個人的には継承が使えるというのは便利ポイントの一つだと思っていまして,例えば「基本的な動作はまったく同じなんだけど一部の機能だけ有効 (あるいは無効) にしたい」みたいなことがあったときに継承は便利に使えるかな〜と思っています.例えばこのような感じ:

export interface ApplicationConfig {
  readonly port: number;
  readonly tcpdumpEnabled: boolean;
}

export class ProductionConfig implements ApplicationConfig {
  readonly port = 5963;
  constructor(readonly tcpdumpEnabled: boolean = false) {
  }
}

export class ProductionInspectorConfig extends ProductionConfig{
  constructor() {
    super(true);
  }
}

このようにしておくと,基本的には ProductionConfig と基本的には同じ設定でありながらも, tcpdumpEnabledtrue にした ProductionInspectorConfig をお手軽に作れることとなります.
もちろん通常のソフトウェアにおける継承と同じで,is-aとして取り扱える (に等しい) 状況以外で使うと良くないことが起きると思うしそもそも継承を使うと複雑さが容易に増すので,そこは用法用量を守って……という感じでしょう.というかこれはTypeScriptの言語機能を設定記述に使えるという話に過ぎませんね.


あとはTypeScript (JavaScript) 内で使うのならば個別の設定のインスタンスをexportしてしまうか (上記の例A),あるいはinterface側に例えば以下のようなコードを書いて,それによって得られたインスタンスをexportしてしまえば便利そうです:

export interface ApplicationConfig {
  ...
}

const appConfig: ApplicationConfig = (() => {
  const nodeEnv = process.env.NODE_ENV;
  switch (nodeEnv) {
    case 'local':
      return new LocalConfig();
    case 'staging':
      return new StagingConfig();
    case 'production':
      return new ProductionConfig();
    default:
      throw new Error('invalid NODE_ENV: ' + nodeEnv);
  }
})();
export { appConfig };

また,TypeScript以外でこの設定を利用したい,つまり単なる設定記述言語としてこれを利用したい場合は生成した設定インスタンスJSON.stringify()に食わせてしまえば他の処理系でも取り扱えるJSON形式の設定が得られるのでそのようなスクリプトを書くとポータブル.良いですね.良かった良かった.


と,このような感じでした.TypeScriptは書き味が比較的ライトなので,設定ファイル用途にも書きやすくて良いですね.

systemdで管理しているserviceがopenできるfile descriptorの上限数を増やす

systemdのバージョン

# systemd --version
systemd 237

TL;DR

ServiceLimitNOFILEを設定すると良い.他の特定のリソースについての制限もどうようにかけることが可能です.

ドキュメントは man systemd.exec で参照できます (ウェブ上はこれが正しいリソース? http://man7.org/linux/man-pages/man5/systemd.exec.5.html).

挙動の検証

現状確認

テストのためにfileを開きまくるperlのscript.ついでにprocのlimit (/proc/{PID}/limitsの中身) も出力する:

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw/say/;

open my $fh, '<', "/proc/$$/limits";
say do { local $/; <$fh> };

my @fhs;
for (my $i = 0; $i <= 2000; $i++) {
    my $filename = "/tmp/test.${i}";
    open my $fh, ">", $filename or die "$filename: $!";
    push @fhs, $fh;
}

特に何も考えずにsystemdのservice fileを書いてみます.

[Unit]
Description=test

[Service]
Type=oneshot
WorkingDirectory=/tmp/test

ExecStart=/usr/bin/perl run.pl

[Install]
WantedBy=multi-user.target

で,実行してみます

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            1024                 1048576              files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

/tmp/test.1020: Too many open files at run.pl line 13, <$fh> line 1.
test.service: Main process exited, code=exited, status=24/n/a
test.service: Failed with result 'exit-code'.
Failed to start test.

Too many open filesで死にます.Max open filesは1024のようですね.ulimit -nで表示される現状のデフォの値っぽい.

LimitNOFILEを設定してみる
[Unit]
Description=test

[Service]
Type=oneshot
WorkingDirectory=/tmp/test

ExecStart=/usr/bin/perl run.pl

LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

LimitNOFILE=65536をserviceの設定に追加してみます.それでscriptを実行:

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            65536                65536                files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

Max open filesが65536になってますね.おかげで実行にも失敗しない! 良かった良かった.

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            10                   10                   files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us
/tmp/test.6: Too many open files at run.pl line 13, <$fh> line 1.
test.service: Main process exited, code=exited, status=24/n/a
test.service: Failed with result 'exit-code'.
Failed to start test.

試しにLimitNOFILE=10にしてみた様子.逆方向にもちゃんと絞れていることがわかります.