Screaming Loud

日々是精進

ドキュメントを書くときに気をつけていること(開発)

アキレス腱を切ってしまい、入院している@moc_yutoです。

今回はフォーマットがバラバラになりやすいドキュメントに関して書きます。

ドキュメントとは

ドキュメントは非同期で知識を共有するための大切なツールです。 引き継ぎ、新メンバーなど知識共有の効率をあげるための必需品です。

エンジニア界隈だけでなく企業で仕事をしている場合であれば、必ずと言っていいほどドキュメントは書くでしょう。

その際、両者にとって不幸にならないドキュメントとはどういうものでしょうか? 自分が書いている場合のドキュメントのフォーマットをまとめます。

自分が主にドキュメントに書く内容は以下3点です。

  • 背景
  • 目的
  • 詳細

内容について

背景

ドキュメントにおいて、一番重要だと思っています。 作業手順でない限り、基本的には必ず背景をいれています。

たとえば、画面の表示制御のためのフラグを単独で別テーブルで管理している場合があります。 コードやDBスキーマを読むだけでは、わざわざ別テーブルにしてる理由がわかりません。 しかしここで背景を書いておけば、そのフラグは一時的なもので一定期間でそのフラグを消すため、変更しやすいように別テーブルに置いている。などと分かります。

目的

これも背景に近いですが、ドキュメントを書いているのであれば、何かを説明しているはずです。

その説明するものは何のためにドキュメントに残しているのか、どうしてこれが必要なのか目的を書くべきです。

詳細

この部分はドキュメントを書くとなったら誰でも書くと思うので、特に留意する点はないかな。 長い文章にするのではなく、簡潔にするくらい。 ここに関しては、コードで補えることも多いので、ドキュメント自体のメンテ性を落とさないようにするために書きすぎないことでしょうか。

その他

章立てはなるべく細かくして、知りたい内容に関して見出しに行けば分かるようにします。

そして、読む人のこともしっかり考えるということも重要です。 読む人はこのドキュメントに何を期待しているんだろうと一瞬でも考えるとそのドキュメントの質はかなり上がるはずです。

まとめ

基本的にコードを読んで分かるというのは挙動だけであって、なんでそういう実装をしたのかはコードのコメントにちゃんと書いてないと分かりません。 ただコードを読むのにも、ある程度負担はかかるし、その概略を自然言語で読めるのであれば、そのほうがいいに越したことはありません。

ドキュメントは詳細に書きすぎなくてもいいが、要点はしっかり書いておくべきです。

参考URL

アジャイル開発「最低限のドキュメントで成功するための3つのポイント」 | tracpath:Works

2017年に読んだ本のまとめ

今年もやっていきます。

昨年のリンクはこちら→ yuutookun.hatenablog.com

今年の読んだ冊数は42冊。 去年と同じくらいですね。

年の後半になって面白いと思う本が減ってしまって本自体の読む量が減ってしまったというのが今年の印象です。

前半のほうは2016年に引き続き、組織をどうするかという話が多かった気がします。 その中でも印象的だったのは、 bookmeter.com

bookmeter.com

です。

働くモチベーションってどこから来るんだろう?みたいなことを考えさせられたものでした。 面白くて社内の勉強会で発表するほどでしたね。 以下そのときのスライドです。

How to Build a Team

あとこれは自分の中で久しぶりにヒットした本でした。 bookmeter.com これもかなり面白くて社内の勉強会で発表しました。

スライドはこちら エンジニアのためのマーケティング

このスライドはマーケティングのメディアに何故か紹介されてPVがすごいあがってましたw

あとこれから自分はどうしていくべきなのだろうか?みたいな大きい視野で考える材料になったのは、以下2冊ですね。

bookmeter.com

bookmeter.com

今年は色々モヤモヤ悩んでいたので、上記2冊はいい思考のたたき台になったと思っています。

さて、2018年はどんな本を読んでいこうかな?

読んだ本一覧はこちら mocyutoさんの読んだ本 - 読書メーター

AkkaStreamで簡単にCSVファイル変換を行うことはできるか?

Scala Advent Calendar 2017の24日目、クリスマス前夜です。
前日はiTakeshi@githubさんのScalaメタプログラミング今昔物語 - 本編でした。

私はドラクエ11をプレイしており、今クリア後の世界を楽しむというクリスマス感が全くない日を過ごしています。

さて今回は、簡単に大きなデータのCSVファイルを簡単に変換する処理を実装してみたいと思います。

実装としては、
「S3からファイルを受け取ってそのファイルを変換しつつローカルに保存する」
というのをakka streamで実装してみたいと思います。

Akka streamとはなんぞや?という方は以下を読んでみてください

Akka Stream についての基礎概念 - Qiita

やりたいこと

ローカルのファイルを変換して保存し直す

  • 入力:CSVファイル
  • 出力:名寄せなど色々変換した後のCSVファイル

動かすマシンは低めでメモリで動かすため、なるべくメモリに載せないでストリーム処理で変換したい。

実装

ローカルのファイルのInputStreamを入力に受け付けて、変換処理を行いローカルに保存するというメソッドです。

  • 入力:java.io.InputStream, 変換に必要な情報
  • 出力:実行可能なマテリアル

下記のようにそれぞれの処理が分けられるというのがAkkaStreamの強みです。

  • stream:入力
  • processCSV:変換処理
  • sink:保存処理

実際にコードを見てみましょう

import java.nio.file.{ Paths, StandardOpenOption }
import akka.actor.ActorSystem
import akka.stream.{ ActorMaterializer, IOResult }
import akka.stream.scaladsl.{ FileIO, Flow, Keep, RunnableGraph, Sink, Source }
import akka.util.ByteString

implicit val system: ActorSystem = ActorSystem()
implicit val ec: ExecutionContext = system.dispatcher
implicit val mat = AkkaUtils.materializer(system)

case class CsvTransformFormat(
  rawFilePath: String, // S3のファイルのパス
  tempEditedFileName: String, // 変換後のファイルパス
  tempDownloadPath: String, // 変換前のファイルパス
  transformLine: String => Future[String], // 変換するメソッド
  headerLine: Option[String] = None, // CSVのヘッダー
  charset: String = "UTF-8"
)

def stream2string(csvStream: InputStream, format: CsvTransformFormat): RunnableGraph[Future[IOResult]] = {
    val stream = scala.io.Source.fromInputStream(csvStream, "UTF-8")

    val processCsv = Source
        .fromIterator(() => stream.getLines()) // InputStreamの入力
        .mapAsync(threadNum)(errorLogHandler(format.transformLine)) // threadNum数の並行処理で変換処理を実行
        .filter(_.nonEmpty) // 空のlineは除外
        .map(s => ByteString(s + "\n", format.charset)) // streamを登録できるようにByteStringに変換

    import StandardOpenOption._
    val sink = FileIO.toPath(Paths.get(filename), options = Set(APPEND)) // ファイルに追記

    processCsv.toMat(sink)(Keep.right) 
}

def transformLine(line: String): Future[String] = {
    val seq = CsvUtils.parse(line)
    val resultSeq = seq.map(s => s"($s)")
    Future(CsvUtils.write(resultSeq))
}

val format = CsvTransformFormat("log", "/tmp/edited/hoge.csv", "/tmp/downloaded/hoge.csv", transformLine, Some(header), "SJIS")

stream2string(new FileInputStream(format.tempEditedFileName), format) match {
    case Right(r) =>  r.run()
}

mapAsync

processCSVのmapAsyncというのは、そのメソッドの第二引数部分の処理を第一引数のthread数で実行できます。 AkkaStreamではこのように複数スレッドで実行できるので、重い処理でも素早く実行できます。

またmapAsyncは並行に実施しますが、順序は保証しているので、CSVの変換処理などにはもってこいです。

このmapAsyncは何個でも並べられるので、変換処理をプラグインのような形で、簡単に追加出来ます。

この自由に追加できて、処理を簡単に並列化できるのがAkkaStreamの最大の強みと言っていいでしょう。

mapAsyncのパフォーマンス周りの説明としては、以下のブログがとても丁寧に解説していますので、ぜひ参照してみてください。

Maximizing Throughput for Akka Streams

sink

akkaStreamではファイルへの吐き出しはFileIOというメソッドが用意されています。 akkaStreamで流れてきたストリームをファイルへ吐き出すというのをsinkという変数で定義しています。

toMat

最後に toMat(sink)(Keep.right) というのは、to materializeの略で実際に実行するのではなく、コンテナにしてrun()を実行するためのパッケージとする処理です。

以下にMatの詳しい解説があります。

[Akka-Stream]Matって何? - Qiita

まとめ

今回は実際にAkkaStreamを使ってCSVの変換処理を作って見ました。 実際の実装を見て使いやすさが分かっていただけたでしょうか?

今回はCSVの変換でしたが、簡単にプラグインをつなぎ合わせることができるので、もっと複雑なことも簡単に作れると思います。

ぜひみなさんもAkkaStreamを使ってみてください。

実際のコードは気が向いたら年末にGitHubにあげようかなw

それでは!

AeroSpikeのベンチマークツールの導入

AeroSpikeには簡単な負荷試験ツールがあり、AeroSpike社自身で提供しています。

github.com

そもそもAeroSpikeって?はこちら

Aerospike基本のき

toolのダウンロード

今回はCentOSでのインストールを行います。 ほかのものであれば、mavenのインストールを適宜変えてください。

mavenのセットアップ

$ sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
$ sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
$ sudo yum install -y apache-maven
$ mvn --version

aero clientのセットアップ

$ git clone git@github.com:aerospike/aerospike-client-java.git
$ cd aerospike-client-java
$ ./build_all
$ cd benchmarks
$ mvn package 

benchmarkを打ち込む

insert only

./run_benchmarks -h 127.0.0.1 -p 3000  -n test -s testset -k 100000 -latency "7,1" -S 1  -o S:100  -w I   -z 8

上記のパラメータは

  • namespace: test
  • set: testset
  • keys: 100000 records
  • latency: latencyの表示のされ方 7ブロック, 21ms ずつ
  • Starting key: 1
  • Object: 100bytesのString
  • workload: Insert only
  • threads: 8

read, update

./run_benchmarks -h 127.0.0.1 -p 3000  -n test -s testset -k 100000 -latency "7,1" -S 1  -o S:100  -w RU,60   -z 8

上記のパラメータは

  • namespace: test
  • set: testset
  • keys: 100000 records
  • latency: latencyの表示のされ方 7ブロック, 21ms ずつ
  • Starting key: 1
  • Object: 100bytesのString
  • workload: Read 60%, Update 40%
  • threads: 8

実際に流してAMCで確認

以下は3台に対して、上記のread, updateの負荷をかけたときのAMCです。 f:id:yuutookun:20171124200911p:plain

まとめ

負荷試験が手軽にできるのはいいですね。