ド・モルガンの法則は大事!大事!超大事!

「ド・モルガンの法則」ってなにそれ?おいしいの?って聞かれたことがあります。
おいしくはないのですが、プログラムを書く上では超大事!です。

ここでは、プログラムを読み書きするうえでド・モルガンの法則をどのように使えばいいかを紹介します。

ド・モルガンの法則とは?

(知っている人は、次の章まで読み飛ばして構いません)

ド・モルガンの法則とは、以下の規則性のことです。

!(A || B) は (!A && !B) に等しい
!(A && B) は (!A || !B) に等しい

(ここで、|| は OR, && は AND, ! は NOT )

確認してみましょう。

!(A || B) は (!A && !B) に等しい
 +-------+-------+-------+
 |       | True  | False |
 +-------+-------+-------+
 | True  | False | False |
 | False | False | True  |
 +-------+-------+-------+
!(A && B) は (!A || !B) に等しい
 +-------+-------+-------+
 |       | True  | False |
 +-------+-------+-------+
 | True  | False | True  |
 | False | True  | True  |
 +-------+-------+-------+

簡単ですね。

プログラミングで役立つ場面

その1: if-else

例えば、以下の処理を考えてみます。

private void logic(boolean a, boolean b){
    if (a && b) {
        // 処理1
    } else {
        // 処理2
    }
}

ここで、処理1が実行される条件が何かというと「a が true、かつ b が true のとき」
では、処理2が実行される条件は?

elseif が成立しないときに実行されます。
つまり、 「NOT(if 条件)」のときです。
「(a が true、かつ b が true のとき)ではないとき」

よくわかりませんね。
そこで、ド・モルガンの法則を使います。

!(a && b)
→ !a || !b

つまり、「a が false、または b が false のとき」

これならわかりやすいですね。

その2: if - else if

続いて、次の処理を考えてみます。

private void logic(boolean a, boolean b){
    if (a && b) {
        // 処理1
    } else if (b) {
        // 処理2
    }
}

ここで、処理2が実行される条件は?

「b が true のとき」ではないです。
else if はそれまでの if 条件が成立しないときに実行されるからです。

つまり、「NOT(if条件)かつ(else if 条件)」のときに実行されます。
「(a が true、かつ b が true のとき)ではないとき、かつ b が true のとき」

よくわかりませんね。
そこで、再びド・モルガンの法則を使います。

!(a && b) && b
→ (!a || !b) && b

さらに論理を展開します。

(!a || !b) && b
→ (!a && b) || (!b && b)
→ (!a && b) || false
→ !a && b

つまり、「a が false かつ b が true のとき」

最初から、以下のように書いてくれていればわかりやすかったのですが…。1

private void logic(boolean a, boolean b){
    if (b) {
        if (a) {
           // 処理1
        } else {
            // 処理2
        }
    }
}

その3: while

ループを考えています。

boolean a = true;
boolean b = true;
while (a && b) {
    // 処理(この中で、a, b を変更)
}

このループの継続条件は、 while 式の通り「a が true、かつ b が true のとき」

では終了条件は?
継続条件が満たされないとき、つまり NOT(継続条件)です。

!(a && b)
→ !a || !b

つまり、終了条件は「a が false、または b が false のとき」
else のときと、考え方は同じですね。

 まとめ

ソースコードには、「else の実行条件」や「 while の終了条件」など、直接書かれていない条件が存在します。
特に、いいコードほどこうした論理的に導きだせることは書かれていません。冗長になるからです。

それらを明確に理解しながら読むには、論理演算の法則を適用して処理をひとつづつ読み解いていくことが大事です。

その中でも、ド・モルガンの法則は役に立つことが多い法則です。
覚えておいて損はないと思います。

--
え、ド・モルガンの法則なんて知らなくてもコードは読めるし書ける? そういう人は、天才です。たまにいます。


  1. 最初は if だけがあって、あとから仕様変更で else if を継ぎ足すという経緯があると、最初に提示したようなコードになりがちです。

8進数リテラルはプログラミング言語ごとに異なる

8進数を表記する文法は、主に以下の3つがあります。 (二番目と三番目は分かりにくいですが ゼロ オー1 です)

これらのうち、各プログラミング言語がどの文法を採用しているかを表にまとめました。

言語 0 0o 0O
C × ×
PHP × ×
Perl × ×
Java × ×
Scala (~2.9) × ×
Scala (2.10~) × × ×
Kotlin × × ×
C# × × ×
Rust × ×
Swift × ×
Go
Ruby
Python2
Python3 ×
JavaScript1

考察

「8進数は 0123 って書くんだよ」というのは、もはや古いのかもしれません。


  1. ただし、0 のあとに 8 か 9 を含むなら10進数。参考:字句文法 - JavaScript | MDN

  2. octet (8個組) の o

SELECT文で本番環境を落としたお話

本番環境でやらかしちゃった人 Advent Calendarで、このパターンのやらかしはなかったのでキーボードを叩くことにしました。
番外編のつもりでお楽しみください。

この記事が、新たな障害発生を防ぐことにつながれば幸いです。

何をやったのか

ある日、ちょっとした調査のために本番データベースのデータを確認することになりました。
(個人情報が格納されているようなシステムではなかったので、必要であれば本番データベースへのアクセスが許されていました)

もしメンテナンスがあればそのタイミングでやればよかったのですが、直近では特に予定はないとのことでした。そのため、システムが動いている状態のまま作業をすることにしました。
ごく単純な SELECT を実行するだけのつもりだったので、システムに影響がないと判断したためです。

その際、万が一コピペをミスって更新系の SQL を実行してしまったら怖いので、念のためトランザクションをかけてからSQLを実行することにしました。
具体的には、psqlPostgreSQL のターミナル)で本番データベースに繋いで、以下の SQL を実行しました。

BEGIN;
SELECT * FROM user_setting WHERE xxx = 1;

結果はすぐに帰ってきました。確か2行程度だったと思います。

続けてさらに SQL を実行しようとしました。しかし、ここで同僚から「ソースコードでわからないところがあるんですが…」と声をかけられました。
こちらは急ぎの作業ではなかったので、ターミナルをそのままにして同僚の質問に回答することにしました。

そして約10分後…。

「システムがダウンしてるー!」

本番障害となりました。

何が悪かったのか

トランザクションをかけて SELECT 文を打ったお前が悪い」ということになりました。

何が起きていたのか

ログからシステムの動きを確認したところ、あるスレッドで user_setting テーブルをロックしようとしていたことが分かりました。具体的には、以下の SQL が発行されていました。

LOCK TABLE user_setting;

この SQL には、ロックモードの指定がありません。この場合、PostgreSQLACCESS EXCLUSIVE ロックが指定されたものとみなされます。
明示的ロック - PostgreSQL 9.4.5文書

この ACCESS EXCLUSIVE ロックは最も強いテーブルロックです。
SELECT 実行時に自動的に獲得される最も弱いテーブルロックである ACCESS SHARE ロックとも競合します。

ロックモードの競合

つまり、システムが LOCK TABLE 文によって user_setting テーブルの ACCESS EXCLUSIVE ロックを獲得しようとしましたが、私が先に SELECT 文によって ACCESS SHARE ロックを獲得していたことで、ロック解除待ちに入って処理が止まってしまいました。

さらに、このあと別のスレッドが user_setting テーブルに対し SELECT を実行しようとしていました。しかし、user_setting テーブルは ACCESS EXCLUSIVE ロックの獲得待ちが発生しているので、この SELECT 文も止まってしまっていました。

結果、一つのスレッドが LOCK TABLE で、多数のスレッドが SELECT で止まってしまい、データベースとのコネクションプールが枯渇。システムダウンに至りました。

どうすればよかったのか

二度と惨劇を起こさないために、以下の知見を得ました。

  • SELECT しかしないとはいえ、油断しない
  • トランザクションを開始したなら、放置しない
    • なるべく早く COMMITROLLBACK をする

また、私がシステムを設計する際に以下の点に気を付けるようになりました。

  • ロック粒度に注意する
  • LOCK TABLE は極力使用しない
    • やむを得ず LOCK TABLE を使用するなら、可能な限り弱いロックモードを使用する
      • その際は、LOCK TABLE との競合について周知する1

MySQL では

MySQL だとテーブルロックのロックモードには READ と WRITE の2種類があります。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.5 LOCK TABLES および UNLOCK TABLES 構文

このうち WRITE ロックは SELECT と競合します。
また、MySQL のテーブルロックはセッション単位です。ドキュメントにあるように、ROLLBACK しただけではロックが解除されないので注意が必要です。

  • トランザクションを (たとえば、START TRANSACTION で) 開始すると、現在のトランザクションはすべて暗黙的にコミットされ、既存のテーブルロックが解放されます。
  • (中略)
  • ROLLBACK は、テーブルロックを解放しません。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.5.1 テーブルロックとトランザクションの通信

後日談

この件などでストレスが重なったことで胃痙攣をやらして、今度は私がダウンしました2。人間の体も本番障害が起きるんですね…。

みなさんも、障害発生後の体調管理には十分ご注意ください!


  1. 今回の場合、私が LOCK TABLE するコードを書いたわけではなかったので、このシステムでこのような競合が起きることを知りませんでした。
  2. 布団の上で数時間のたうち回りました。

Object#clone() メソッドからスローされる CloneNotSupportedException はどのようにハンドリングするべきか

JavaObject#Clone() メソッドは throws CloneNotSupportedException が宣言されています。

protected native Object clone() throws CloneNotSupportedException;

しかし、クラスが Cloneable インタフェースを実装していれば CloneNotSupportedException はスローされません。 それにもかかわらず CloneNotSupportedException はキャッチ例外のため1、スローするかキャッチする必要があります。

                            /* 👇実装している! */
public class Example implements Cloneable {
    @Override
    public Object clone() {
        // エラー: 例外CloneNotSupportedExceptionは報告されません。
        // スローするには、捕捉または宣言する必要があります
        return super.clone();
    }
}

正しく実装していれば何もしなくてもいいハズなのですが…。 このようなとき、どのようにハンドリングすればいいのでしょうか。

そこで、Java API ではこのよう場合にどのように実装されているかを確認してみました。 Search · CloneNotSupportedException path:/src/java.base/share/classes/java/

InternalError でラップする

圧倒的に多いのが、CloneNotSupportedExceptionInternalError でラップするという実装です。 Cloneable インタフェースを実装しているのに CloneNotSupportedException がスローされたということは、VM 内で何か問題があったということでこのようにしているようです。

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

AssertionError でラップする

ArrayDeque, EnumMap, EnumSetCloneNotSupportedExceptionAssertionError でラップしていました。 プログラムがバグっているということを示すために、AssertionError を使用しているようです。直接、この例外を投げるのは珍しい気がします。2

public ArrayDeque<E> clone() {
    try {
        @SuppressWarnings("unchecked")
        ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
        result.elements = Arrays.copyOf(elements, elements.length);
        return result;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

RuntimeException でラップする

発生しないはずだから、とりあえず非キャッチ例外にしてしまえ!ということのようです。 ただ、RuntimeException だと型でエラーの概要が分からない、catch(Excetption e) で捕まってしまうという問題があります。

public Object clone() {
    try {
        return super.clone();
    } catch (CloneNotSupportedException e) {
        throw new RuntimeException(e.getMessage());
    }
}

握りつぶして、null を返す

java.util.Date では、キャッチして握りつぶしていました。 ただ、これは発生しないとはいえ null が返されてしまうルートができてしまうので、紛らわしいと思います。

public Object clone() {
    Date d = null;
    try {
        d = (Date)super.clone();
        if (cdate != null) {
            d.cdate = (BaseCalendar.Date) cdate.clone();
        }
    } catch (CloneNotSupportedException e) {} // Won't happen
    return d;
}

ちなみに

Cloneable インタフェースを実装しているクラスの clone メソッドで、throws CloneNotSupportedException を宣言しているのは見当たりませんでした。

まとめ

いかがでしたか。3
Cloneable インタフェースを実装していれば CloneNotSupportedException はスローされないので、どのようにハンドリングしても問題はないです。とはいえ、throws してしまうと呼び元に余計な手間をかけてしまいます。そのため、キャッチして何かしらのハンドリングをしておいた方がいいと思います。

そして、Java API の実装に合わせるのであれば InternalError にラップしてスローするのがいいようです。


  1. 設計ミスだと思います。

  2. 本来は、-enableassertions オプション付きで Java を実行しているときに、assert 文の条件が成立していないときにスローされるエラーです。

  3. これ書くとアフィリエイトブログっぽいですね。

Ansible で Mackerel を操作するプラグインを作りました!

Ansible で Mackerel の設定を取得したり変更したりできたらなーと思う時があります。 例えば…

  • アラートが鳴ってしまうのを防ぐために、Ansible 実行中はホストのステータスを standby にしたい
  • 特定のロールが設定されているホストの場合だけ、ファイルをコピーしたい

でも、Ansible の command モジュールで mackerel-agent を実行してこのような処理をさせるのは手間がかかります。 そこで、 Ansible の設計にのっとった形で簡単に Mackerel を操作できるように、プラグインを作ってみました! 1

https://github.com/YujiSoftware/ansible-mackerel-module

どういう風に使えるの?

例えば、Ansible 実行中はホストのステータスを standby にしたいとき。

- name: Set Mackerel Host Status
  mackerel_host:
    status: standby

# ~ホストの更新処理~

- name: Set Mackerel Host Status
  mackerel_host:
    status: working

ホストのロールを設定したいとき。

- name: Set Mackerel Host Role Fullnames
  mackerel_host:
    role_fullnames:
      - test_project:web

ロールによって動作を変えたいとき。

- name: Get Mackerel host configuration
  mackerel_host:
  register: mackerel

- name: Copy file if roles has "test_project"
  copy:
    src: ../files/example.conf
    dest: /etc/httpd/conf.d/
  when: mackerel['host']['roles']['test_project'] is defined

こんな感じに使えます! (ちなみに、API Key や Host ID はサーバ内にある mackerel-agent の設定ファイルをデフォルトで読み取るので、記述は不要です)

詳しい使い方は、GitHub の README をご覧ください!

まとめ

まだ ホストの情報取得/ステータス設定/ロール設定 しか対応していません。2 今後、JOJOに対応する操作を増やしていく予定です。

もし、こういうことができるようにしたい!という要望がありましたら、GitHub の Issues へ書き込んでいただければと思います。もしくは Up vote 👍 をお願いします。 https://github.com/YujiSoftware/ansible-mackerel-module/issues

ぜひご利用ください!


  1. すでに mackerelio/ansible-mackerel-agent というのがあるのですが、これは mackerel-agent をインストールするための Ansible テンプレートです。一方で、今回作ったプラグインは Mackerel API のラッパーです。

  2. Mackerel Advent Calendar が空いてる!チャンスだ!と思って急いで作りました。

JJUG CCC 2019 Fall ( #jjug_ccc ) - セッション資料の一覧

JJUG CCC 2019 Fall に行ってきました!


最近 Java のシステムをまたいじるようになって*1Java 欲が高まってきていたところにいろんなセッションを聞けてすごいテンション上がりました。
特に、「開け!ドメイン駆動設計の扉」がちょうどこれから一部のシステムをリプレースするにあたって導入したいけど「DDD、さっぱりわからん」と思っていたところだったのでとても助かりました。


また、マニアックな話も多数あってやっぱり JJUG CCC 最高!という気持ちです。
CLR の ValueType を起点に Project Valhala を覗いてみる」とか「オレ流OpenJDK「の」開発環境」とか、普段の JJUG では聞けない、でもとても知りたかった内容でした。こういうの、もっと聞きたい!


そして、懇親会で話しかけてくださった方、ありがとうございました!
すごく楽しかったです、そしてあっという間でした。もっとお話したいのでぜひ次の機会に!!


さて、最後にいつものを。

今回、残念ながら時間がかぶってしまって参加できなかったセッションがいっぱいあった*2ので、あとで読むために現時点で発表者の方が公開されている資料一覧をまとめしました。*3
(あとで JJUG CCC 2019 Fall のページにもリンクが載るかもしれませんが、とりあえず自分の方で調べました)

10:00-10:45

11:00-10:45

12:00-12:45 (ランチセッション)

13:30-14:15

14:30-15:15

15:45-16:30

*1:弊社、いろんな言語が混ざりすぎ!

*2:あと、今回はボランティアスタッフやっていたので、その間のセッションでも気になったのがありました。

*3:発表者のお名前は敬称略とさせていただきました。

Stack Overflow のタグから、どのフレームワークについてよく質問されているのかプログラミング言語ごとに分析してみた

Stack Overflow の質問には、その内容を示すタグが付与されています。

このタグ、よく見ると「言語」+ 「フレームワークやライブラリの名称」という構成が多いです。 つまり、これを分析すれば、言語ごとに質問の多い(つまり、よく使われている、人気がある)フレームワークやライブラリが何か分かるのではないでしょうか。

そこで今回は、kaggle で公開されていた Stack Overflow Data ^dataをもとに、質問に付与されたタグの出現回数を言語ごと×1年ごとに集計し、ランキングを算出しました。

より詳細なデータは こちら

解析結果

それぞれの言語ごとに、簡単な概要と解析結果(年度ごとのタグ出現回数ランキング)をまとめました。

Java

  • Spring Boot が登場1と同時に急上昇
  • ORM マッパは依然 Hibernate が上位
  • Swing の順位が低下すると入れ替えに、Java FX が上昇
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
android 1 1 1 1 1 1 1 1 1 20
spring 2 2 2 2 3 4 4 4 4 4
spring-boot 3 3 13 33 145 1567 10490 8932 6891 4822
hibernate 4 4 4 6 6 6 5 5 3 2
maven 5 5 7 7 7 12 15 22 87 498
arrays 6 6 5 5 5 5 11 12 24 26
json 7 8 8 8 12 11 18 28 59 174
javafx 8 13 14 19 29 72 160 595 422 295
eclipse 9 9 6 4 4 3 3 3 5 3
swing 10 7 3 3 2 2 2 2 2 1

Go

  • 基本的な言語仕様に関するタグが多い
  • Docker や MongoDB といったモダンな技術の組合わせが多い
  • ORM マッパーは GORM が人気
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
json 1 1 1 1 1 2 7 20 19 16
struct 2 2 3 3 3 9 4 45 9 92
http 3 3 2 4 4 3 7 2 44 92
goroutine 4 4 6 6 5 4 14 3 2 1
docker 5 6 16 33 48 173 567 283 130 92
mongodb 6 7 5 7 8 23 23 82 130 92
slice 7 5 7 5 19 7 18 10 19 92
concurrency 8 9 10 10 7 5 2 5 44 7
arrays 9 9 8 7 16 17 23 13 2 92
go-gorm 10 19 27 47 163 858 567 283 130 92

Python

  • 2016 年に Python3.x が Python-2.7 を上回った
    • Python 3.0 が2008年登場ということを考えるとかなり遅い
  • 機械学習や計算ライブラリの質問が多言語に比べ多い
  • Django (Webアプリケーションライブラリ) が一貫して上位にいる
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
python-3.x 1 1 3 4 5 4 6 7 18 21
pandas 2 2 2 3 4 6 20 1055 4807 3368
django 3 3 1 1 1 1 1 1 1 1
numpy 4 5 5 5 3 3 2 4 6 16
tensorflow 5 9 18 251 9023 5310 7081 6103 4807 3368
dataframe 6 6 9 23 55 93 251 2258 2827 3368
python-2.7 7 4 4 2 2 2 4 78 314 850
matplotlib 8 7 7 7 7 8 8 8 21 54
list 9 8 6 6 6 5 3 3 4 3
flask 10 14 11 10 11 13 12 74 259 3368

PHP

  • Laravel タグが急上昇
  • 全体的に Web 関連のタグがとても多い
  • Codeigniter が10年間安定した順位を保っている
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
laravel 1 2 4 6 9 19 195 3248 5129 3341
mysql 2 1 1 1 1 1 1 1 1 1
javascript 3 3 2 2 2 2 2 2 2 2
wordpress 4 6 6 5 7 8 9 11 10 19
html 5 4 3 3 3 3 4 4 3 3
jquery 6 5 5 4 4 4 3 3 4 6
ajax 7 8 8 8 8 6 6 6 8 8
arrays 8 7 7 7 6 5 5 5 5 7
codeigniter 9 9 9 9 10 9 7 7 9 16
sql 10 13 12 12 5 7 8 9 11 9

Ruby

タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
ruby-on-rails 1 1 1 1 1 1 1 1 1 1
ruby-on-rails-5 2 3 13 635 3910 3822 3612 3206 2469 1702
rubygems 3 6 5 15 16 11 8 3 2 3
ruby-on-rails-4 4 2 2 2 2 3 1218 1307 1284 908
activerecord 5 5 4 5 6 5 3 4 3 2
rspec 6 7 3 6 4 4 5 7 9 15
arrays 7 4 6 4 5 7 10 9 7 5
javascript 8 9 8 8 9 16 16 14 15 25
postgresql 9 11 12 13 15 24 34 63 88 132
ruby-on-rails-3 10 8 7 3 3 2 2 2 4 588

JavaScript

  • ライブラリは jQuery, React.js, Angurlar の順にランクイン
  • Node.js は毎年順位が上がってきている
  • Ajax が下がってきている(当たり前に使えるようになった?)
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
jquery 1 1 1 1 1 1 1 1 1 1
html 2 2 2 2 2 2 2 2 2 2
reactjs 3 6 9 15 111 942 7551 6176 4367 2899
node.js 4 4 5 6 7 9 9 15 48 491
css 5 3 4 4 3 3 4 4 5 5
angular 6 11 13 239 6645 8935 7551 6176 4367 2899
php 7 7 6 5 5 4 3 3 3 6
arrays 8 9 10 10 10 12 17 17 19 21
ajax 9 8 7 7 6 5 5 5 4 3
typescript 10 12 17 71 134 158 256 6176 4367 2899

SQL

  • SQL Server が予想外の1位
    • T-SQL 3も7位につけているところを見ると、意外と使われているっぽい
  • Oracle も安定して上位
  • 言語仕様に関するものでは Join がランクイン
タグ 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009
sql-server 1 1 1 1 2 2 2 2 1 1
mysql 2 2 2 2 1 1 1 1 2 2
oracle 3 3 3 3 4 4 5 7 6 5
postgresql 4 4 6 6 8 9 9 11 10 17
php 5 5 4 4 3 3 4 4 9 8
database 6 6 5 5 6 6 6 6 5 6
tsql 7 7 7 8 9 8 7 5 3 3
c# 8 8 8 7 5 7 8 8 7 7
java 9 11 10 10 10 10 11 13 17 20
join 10 10 11 11 11 11 10 12 12 13

おわりに

当然の結果にだったものもあれば意外な結果だったものも多数あるのではないでしょうか。 元データの方では、上位30位まで見れますので興味があれば合わせてご覧ください。 Tag trends by language for Stack Overflow | Kaggle


  1. 2014年4月1日にバージョン 1.0 が GA

  2. バージョンアップしないの…?

  3. SQL Server で使用する、SQLを手続型に拡張した言語