ソモサン

私rohkiによる活動や読書の記録をつらつらと書くページです

思いつきメモ: ふりかえりにつかうチームレーダーチャートを最大値/最小値で作ると面白そう

アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き

アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き

えー、直観で書いてます。やってないし根拠もないです。
連休中、アジャイルレトロスペクティブを読んだときに、ふっとわいてきた思いつきです。

僕個人として、運用でメトリクスを見ていると、統計方法として用意されている平均の使用頻度が低いです。
パーセンタイルとか最大最小の方が特徴が表れて分析に使いやすい感触です。
その結果、平均の使用頻度がますます下がっていく感じです。

で、チームレーダーもそうなのかなーとなんとなく思いました。
書籍には平均するって書いてあって、どなんだろっておもったんです。
チームメンバーがつけた点数をぐちゃっと混ぜた、最小値だけのチャートと最大値のチャートを一緒にプロットしたとして。
その差分 = チーム内の認識の差分の大きなところなわけで、それがすごい開いてたら見えてるものの差が表れてるかもで、きっと面白い。
一方、平均するとチーム内の認識の差分が見えなくなるので、もったいないなーって思ったんです。
んで差がなかったら、チームとしてチャートの分析と改善をやってきましょう、でよさげ。

どうだろうなー、いつかやるかな?

AWS Kinesis ひたすら読みにいく k-iter の複数 shard 対応(と、Rust の Future/Stream/Tokio 話)

github.com

AWS Kinesis Stream Subscriber

USAGE:
    k-iter [FLAGS] [OPTIONS] --region <NAME> --stream-name <NAME>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information
        --verbose    Enable verbose mode.

OPTIONS:
        --data-format <TYPE>       Set data output-format. [default: UTF8_STRING]  [possible values: RAW_BYTES,
                                   RAW_STRING, UTF8_STRING]
    -t, --iterator-type <TYPE>     Sets iterator type. [default: LATEST]  [possible values: LATEST, AT_SEQUENCE_NUMBER,
                                   AFTER_SEQUENCE_NUMBER, AT_TIMESTAMP, TRIM_HORIZON]
    -r, --region <NAME>            Sets a region name. [possible values: ap-northeast-1, ap-northeast-2, ap-south-1, ap-
                                   southeast-1, ap-southeast-2, ca-central-1, eu-central-1, eu-west-1, eu-west-2, eu-
                                   west-3, sa-east-1, us-east-1, us-east-2, us-west-1, us-west-2, us-gov-west-1, cn-
                                   north-1, cn-northwest-1]
        --sequence-number <NUM>    Set Sequence number when Iterator Type is AT_SEQUENCE_NUMBER or
                                   AFTER_SEQUENCE_NUMBER.
    -s, --shard-id <ID1 ID2>...    Set shard ids. If you don't set, iterate all shards
    -n, --stream-name <NAME>       Sets a stream name.
        --timestamp <TIMESTAMP>    Set timestamp(UNIX Epoch milliseconds) when Iterator Type is AT_TIMESTAMP.

-s, --shard-id ... Set shard ids. If you don't set, iterate all shards

でけたー。できましたよ。細々とやっとりました。
何も指定しなければ全 shard をにらみにいきますし、当然指定もできます。
複数の shard id も指定できます。あ、sequence-number も複数の方がいいかな? 確認大変そう。また今度にしよう。

つまったところ

futures::stream::Stream の実装

impl Stream for KinesisShardIterator {
    type Item = GetRecordsOutput;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(current) = &self.token {
            let r = GetRecordsInput {
                shard_iterator: current.clone(),
                ..Default::default()
            };

            self.client
                .get_records(r)
                .map(|r| {
                    self.token = r.next_shard_iterator.clone();
                    Async::Ready(Some(r))
                })
                .map_err(Into::into)
                .wait()
        } else {
            self.get_iterator_token()
                .map(|next| {
                    self.token = Some(next);
                    Async::NotReady
                })
                .map_err(Into::into)
        }
    }
}

このあたり ですな。
wait じゃあなくて poll つかって動かなかったり、map_or_else つかって型が合わなかったり、Iterator だけでいいんじゃあ…とあきらめかけたり、いろいろやってました。
というか、wait まわりはまだちゃんとわかってないです。動いたーで喜んで今書いてます。

Stream でかつ、Async::Ready(None) を返さなければひたすら動き続ける(という理解な)ので、呼び出し先で こう 書けます。

            let it = match iter_type {
                IteratorType::LATEST | IteratorType::TRIM_HORIZON => {
                    KinesisShardIterator::new(na, ia, ta, ra)
                }
                IteratorType::AT_SEQUENCE_NUMBER | IteratorType::AFTER_SEQUENCE_NUMBER => {
                    let seq = value_t_or_exit!(matches.value_of("sequence-number"), String);
                    KinesisShardIterator::new_with_sequence_number(na, ia, ta, seq.as_str(), ra)
                }
                IteratorType::AT_TIMESTAMP => {
                    let timestamp = value_t_or_exit!(matches.value_of("timestamp"), f64);
                    KinesisShardIterator::new_with_timestamp(na, ia, ta, timestamp, ra)
                }
            };

            tokio::spawn({
                Interval::new_interval(Duration::from_millis(1000))
                    .map_err(|e| eprintln!("timer failed; err={:?}", e))
                    .zip(it.map_err(|e| eprintln!("subscribe error = err{:?}", e)))
                    .map(|(_, r)| r)
                    .forward(
                        tx.clone()
                            .sink_map_err(|e| eprintln!("send error = err{:?}", e)),
                    )
                    .and_then(|_| Ok(()))
            });
        }

Interval も Stream を実装しています。
zipKinesisShardIterator と並べることで、1 秒ずつ Kinesis の record を確認しに行くようになりました。
0.5 秒とかにすると Rate Limit Exceed とかって出ました。知ってる。

tokio::sync::mpsc の取り扱い

これは動くようになったところを 1 段階目とすると、今は 3 段階目です。 最初は 1 record 毎に Sender を複製して send を呼んでいました。だせーと思いながら動かすために目をつむってかいたのが、1 段階目。
2 段階目は try_send&mut self なのに気づいて、毎度複製するよかましかーと書き換えたやつ。
3 段階目が、send_all とか forward をみて、mut なしでいけるのではと気付いて書き換えた今のやつです。

課題

Verbose mode での shard-id 表示

ほしくない?

ローカルでのテスト

いい加減 localstack つかったテストかけるようにしよう。これもこれで大変です。

しめ

適用できたぜい。なんちゃってでも Future と Stream、Tokio が扱えたのはでかいです。
もうちょっと待てば async/await を含んだ release がでます。けどいけそうっておもって書かずにはってかんじでした。 出たら書き直します。

Rust での Iterator 返し + from_fn + Self

自分が作りたいもののためにメモ。きっと忘れて見返す。

こんな書き方できるんですねー。 ちょっと前までコンパイルエラーで落ちてた記憶がうっすらとあります。

#[derive(Debug)]
struct SpanIdentity {
    trace_id: String,
    span_id: String,
}

#[derive(Debug)]
struct SpanContext<'a> {
    id: SpanIdentity,
    parent_span: Option<&'a Self>,
}

impl<'a> SpanContext<'a> {
    fn context_iter(&'a self) -> impl Iterator<Item = &'a SpanContext<'a>> {
        let mut acc = self;
        std::iter::from_fn(move ||{
            let r = acc.parent_span;
            acc = r.unwrap_or(acc);
            r
        })
    }
}


fn main() {
    let a_a = SpanContext{id: SpanIdentity {trace_id: "a".to_owned(), span_id:"a".to_owned(),}, parent_span: None};
    let a_b = SpanContext{id: SpanIdentity {trace_id: "a".to_owned(), span_id:"b".to_owned(),}, parent_span: Some(&a_a)};
    let a_c = SpanContext{id: SpanIdentity {trace_id: "a".to_owned(), span_id:"c".to_owned(),}, parent_span: Some(&a_b)};
    
    for x in a_c.context_iter() {
        println!("{:?}", x);
    }
}

Rust Playground

OpenCensus meetup vol.1 に参加してきました!! #opencensusjp

参加してきました

https://opencensus.connpass.com/event/123885/

いやー面白かった。
自分が気になって見積もりに時間かけてるところがやっぱりネックになっていて、みんな気にしてるという同じ認識を持てたのもよかったです。
京都からきてよかった。
資料リンクだけ並べてるので、感想は後ほど。 -> 書いた!

なぜに京都から???

rohki.hatenablog.com 前に調べたり、Scala 関西で @grimrose さんの話きいたりしてました。僕にとって今一番面白いところだからです。
面白さの背景を書くと、Managed Service を使っていてメトリクスやログがとりやすくても追跡はひたすらしんどいってのを何度か味わってるからです。
ロードバランサーのエラーコードの発生個所や遅延の発生、HTTP のアクセスログ、アプリケーションログ、サーバーのリソース消化具合、DB のクエリの遅延やトランザクションの待ち、キャッシュのヒット率、Disk IO、Disk Queue、などなどなど。
AWS の Managed を使ってるとこの辺りは取れます。取れますが、これらを組み合わせてサービスを構築しているのは自分たちで、各メトリクス・ログの因果関係を追っていく必要があります。
僕は CloudWatch/Elaticsearch を使って追ってます。追っていますが、ひっっっっっっじょうに疲れるんです。
インフラ構成を脳裏に置きながら、みんなで仮説を立てて、関連するメトリクスを洗い出し、同じグラフに描画して、まず相関があるかを確認する。
これ繰り返すだけでお腹が一杯になってしまって、原因らしきものを特定したときにはすでにグロッキーなのです。
この特定までの負荷を安くして、一次対応とか根本対応どうするにもっと時間をかけたい、ってのがきっかけです。

本編

イベント主旨説明 / Opening / @ymotongpoo

資料 URL: https://bit.ly/20190403-oc-intro-update

アンケート見ながら、やっぱり StackDriver つよいなーという。場の問題? それはあり得るw
書きながら改めてみると Zipkin や Jaeger が少ないのも面白いです。やはりこの辺りを自前でやるのはつらいというこうとでしょうか。
運用の運用で奔走させられそうな雰囲気を感じます。

Session 1: OpenCensus Intro and Status Update / @ymotongpoo

資料 URL: https://bit.ly/20190403-oc-intro-update

会場の雰囲気としては、Service への期待が大きかった気がしてます。というか僕が期待してます。
Reverse Proxy や Service Mesh に tracing 機能もこちらによるか、ないしは連携できると色々はかどるのかなぁと思ったりしてます。
OpenTracing とのマージの話は後にも出てきますが、うん。ちょっとごまかそう。うん。

Session 2: OpenCensusを実際に使ってみて、便利なところと、困ってるところ / @sinmetal

資料 URL: https://docs.google.com/presentation/d/1J0fh9_C-Juhy1N3EB1G9PqR8NRWgwJjb0VCy7VfRbXI/
趣味のゲーム https://github.com/metal-tile

まさしく聞きたかったお話でした。Sampilng rate と追跡性のお話。
低すぎると取りこぼしがでて、高いと負荷とお値段があれで、じゃあ一時的に全取得ってやってみたら Buffer full で捨てられて…
特に最後かなぁ。全取得が難しいケースもあると分かったのはとても大きいです。この辺りは実装と設定によりそう。

Session 3: Distributed Tracing with OpenCensus at Wantedly / @munisystem

資料 URL: https://speakerdeck.com/munisystem/distributed-tracing-with-opencensus-at-wantedly-inc

Wantedly さんで投入されたお話。
これも聞きたかった話ですねー、Sampling rate とか性能問題とかAWS X-ray と比べてのところとか。
X-ray の導入で悩ましいのが、AWS 色が強すぎるところなんですよね。
OpenCensus の Partners & Contributors でも GoogleMicrosoft は入っていて AWS は入ってないというところがあります。
Library の話は強い!!という感想。場合によってはそういうこともありますよね。うーむ。

あと、バックエンドへの最適化が話に上がっていて、抽象度あげたいから OpenCensus を入れたけど痒いところに手が届かないという、バランスの難しさもわかりました。
ここでも OpenCensus Service の話題。期待たかいっすよね。

Session 4: OpenCensus Javaで始めるOpenCensus / @grimrose

資料 URL: https://docs.google.com/presentation/d/1i3LYjz26lz8WWuIqcwBpc8d0I-8_kTu7hnDWvdnOZEE/edit#slide=id.p

Java 実装 Android で動くんかい!! 元クライアントエンジニアとして確かに知りたいときはある。あるものの、どうやって送るんだそれ???
通信負荷考えると Sampling rate もっと考えなきゃ云々。
閑話休題
Java は Exporter のサポートが厚めですよね。対応状況も Beta ですし。 Spring Cloud は知らなかった…調べてみよう。
ちょいちょい Scala 実装の話があって個人的に非常にありがたいw try with resource の話がちょっと盛り上がっていたかも? golang の defer の話が上がっていたからかもです。

LT1: JavaのCustom Exporterを書いてみた / Writing custom exporter for Java / @r_rudi

資料 URL: https://docs.google.com/presentation/d/e/2PACX-1vRMNdvFdqwHlyfgj8fAQA-sU391UooWegR2s87rpUD9RzOsPzxnycoH3LkZDt12v48kxmphq68qa-Y_/pub?start=false&loop=false&delayms=3000&slide=id.p
https://github.com/shirou/opencensus-exporter-trace-xray

Session 4 と関連が強い、ということで順番をいれかえて LT トップバッターへ。
そうそうこれこれ。X-ray との話。というか強い。
スライド 11 にある対応表とかめっちゃありがたいです。なるほどなぁ。

LT2: TracingとLoggingの連携 / Tracing and logging correlation / @ladicle

資料 URL: https://speakerdeck.com/ladicle/integration-with-tracing-and-logging
https://github.com/Ladicle/opencensus-and-jaeger

gopher くんの好みの話でざわっとなって途中から気になるところが変わってしまいましたw そうか、ニンジンがすきで魚が嫌いなのか… デモもそれにそってやられてて思わず笑ってしまいました それはさておき。
Tracing と Metrics と Logging の関係図よいすよね。この図を念頭に置いて設計してきたい
OpenTracing との話は、うん。

LT3: OpenCensus Stats で PaaS のメトリクスを補完する / Interpolate PaaS metrics with OpenCensus Stats / @apstdnb

資料 URL: https://docs.google.com/presentation/d/17E5TqNuq46_8B62beMOg_1bHY_jDVBTdmzSJYrqKLgs/edit

自己紹介が強すぎるw
当然ながら GCP は本家本元に近いので連携が強いですよね。資料も豊富だ。
悩ましい…

LT4 : Ruby実装についてひと言 / Ruby implementation / @kawasy

資料 URL: https://speakerdeck.com/kawasy/current-status-of-opencensus-ruby-number-opencensusjp

良かった。なぜかいまいちな Ruby 対応に関してひとことでした。
というより Erlang/Elixir の強さが何でそんなに強いのという。Beta にはいってますやん。
やっていく宣言はかっこいい!!

LT5 : ウェブフロントエンドからサーバーまでの一気通貫のトレーシングに挑戦してみる / Try consistent tracing from web frontend to backend servers / @shibukawa

資料 URL: https://docs.google.com/presentation/d/145vr-ouZFxlLub5dGyl3TkJmxqWqPMvmuZObUMgRzEg/edit
https://github.com/shibukawa/opencensus-sample

これもこれで面白いですやつ。僕個人の話でいうと、フロントエンド周りの知識がよわいんですよね。
確かにここきっかけでリクエストとか描画のタイミングが追えるようになるかも? 性能問題が出ないように気を付けながらですが。

Observablility は港区用語。港区用語とは…(関西在住 エンジニアの感想)
Real っては確かによい表現。その時の Real が知りたいのです。

おわりに

雰囲気よかったです。
なんとなくみんなの興味の根っこが同じでありつつ、いろんなアプローチがされていました。
vol. 2 も参加できるかな?

nand2tetris に入門中

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

01 の nand gate のところをやってます。 ベン図とかド・モルガンの法則とかの懐かしワードをどうにか思い出して解いてます。
なんか基礎をのばしてーなーと思って会社の制度を利用して今手元にあります。ありがたい。

www.nand2tetris.org

書籍の公式サイトがあって、↑にシミュレータのプログラムもあります。
テストスクリプトもついてるので、サクサクと試行錯誤できてよい感じ。こーいうのやってると大学時代思い出しますねー

(追記あり) AWS Lambda が正式に Rust 対応したので KinesisFirehose にくっつけて性能計測した #rustlang #rust_jp

これは Rustその2 Advent Calendar 2018 の 2 日目の記事ということにしました。

結果(2018/12/3 更新)

f:id:rohki:20181204083205p:plain

github.com

ありがたい PR のおかげで更新できました! tatsuya6502 さんありがとうございます!
246k record を 57.6 秒で処理したので、4,270 record/sec でなんと 4,000 の大台突破です。
修正内容は僕がサボったところを指摘いただいた感じでした。むしろすみません。

Runtime record/s
Python 142.8
Rust(on Python 2.7) 2348.6
Golang 3910.0
Rust(on Custom Runtime) v1 3967.2
Rust(on Custom Runtime) v2 4270.0

ということで、4,000 を越えました!すごい!!

成果物

github.com

これまでのあらすじ

rohki.hatenablog.com rohki.hatenablog.com

初めて Kinesis Firehose で計測してから 1 年弱、 Golang で計測してから 10 ヶ月弱…

aws.amazon.com

ついに正式対応ですよ! なんかもっとすごかったけど!! ということで、ここまできたら計測せねばということでやりました。

実装

去年に作ったやつを適当にちょちょっと書き換えたら動きました。時間は 2 時間ぐらい?
この辺は数をこなしたおかげかと思っとります。

せっかく linux 環境作ったし、そのまま build したやつ動くかなーとやってみた結果、llibc のバージョンで怒られました…。

静的リンクすればいいんんだろう、ということで musl 版を以下を使って作りました。 rohki.hatenablog.com

コマンドは以下です。

cross build --target x86_64-unknown-linux-musl --release

計測環境

  • Runtime: Custom Runtime
  • メモリ: 128 MB

いつも通りの下限性能です。
これまでと同じどおり、Kinesis Data Generator で Apache Log っぽいものを投入して、時間あたりでどれだけ処理できるかをみます。

結果(再掲)(2018/12/3 更新)

f:id:rohki:20181204083205p:plain

246k record を 57.6 秒で処理しました。

Runtime record/s
Python 142.8
Rust(on Python 2.7) 2348.6
Golang 3910.0
Rust(on Custom Runtime) v1 3967.2
Rust(on Custom Runtime) v2 4270.0

と、いっても最大風速の性能がならんだ、というところです。 感覚的には 3800 から 3700 が平均性能といったところでしょうか。それでも十分速いです。

今回は速度も安定してました。毎回 4,200 record/s を安定して出していたので、予測しやすくなってます。

んで、実戦投入できる?

熱意と覚悟、Lambda のコスト削減に並々ならぬ事情があれば。
Lambda の仕様を考慮した build まわりのややこしさがどうしたって足を引っ張ります。
OpenSSL の反省を踏まえて今回の GLIBC はさくっと対応できましたが、Lambda の仕様と対応策の検討が追いつかないとひたすらハマる落とし穴でした。
動いてますがなんか見落としがありそう…やるのであれば場所を選んで少しずつって所かと思います。

さいご

ついに正式対応。感慨深い…
書いてさらしたら PR もらえた。ありがたい…

PC 新調した

f:id:rohki:20181201174248j:plain
A485 Thinkpad(OS: Linux Mint)

ひさしぶりの Linux 環境である。
Wi-Fi につまったり、日本語入力の設定につまったりしたけども、まぁ動きました。この記事も新しい端末から書いてます。

やったこと

  • Wi-Fi ドライバが認識されなかったので、ドライバを追加してビルドした
    • Secure Boot が邪魔していたので無効化した。
  • 日本語入力を設定した。iBus とか懐かしい
  • mac から zshrc を持ってきた。パスを修正すればいけた。
  • Password Manager にログインした。Dashlane 使ってるけど、Add-in だけでどうにか使える

ちょっと感動したこと

f:id:rohki:20181201175051p:plain

DuckDuckGoWi-Fi について調べたら、右側に手順が出ましたよ!
内容はフォーラムから引っ張ってきている模様。

ちょっとこまってること

TouchPad が大きいせいで、カーソルがとんじゃいます。変なところに文字入力してて辛い。
キーボードは固めかなー。そのうちなれるでしょう。

感想

パスワードマネージャと zshrc があればセットアップはわりと楽なもんですね。
次は開発環境構築。