ソモサン

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

発見があった取り組み: nand2tetris, データ指向アプリケーションデザイン + pingcap/talent-plan

ぼちぼちやってたやつです。

nand2tetris

公式サイト: Home | nand2tetris

CPU 設計から OS 開発までやろうぜ、って内容です。以前書いたやつ。 rohki.hatenablog.com

一回挫折して次に書くやつに浮気しながらなんとか仕上げました。長かった...
割と大学時代に履修したことが多く「今でもできるよな、俺...」て再確認の意味が強かったです。
なので一回、10 章 LL パーザをかくところで挫折したときはガチで凹みました。「できなくなってるのか俺」て。
とはいえ頭冷やしてベタに書き直せば 2 日でできて安心した次第。良かった、ほんとに。

データ指向アプリケーションデザイン + pingcap/talent-plan

talent-plan/README.md at master · pingcap/talent-plan · GitHub

こっちの方が学びは多かったです。歴史の話とか、関係性の話とか、ネットワーク遅延の難しさとか CAP 定理の話とか。
意図してやったわけではなかったんですが、pingcap/talent-plan の TP 201: Practical Networked Applications in Rust. 時間をおかず始めて知識を実体験できたのがさらに良かったです。
途中で Stateless が Stateful になり、そのあと Multi-thread になる際に実装で軽く「ここ RWLock じゃあなくて Mutex にしたら」て思考した瞬間にスループットと整合性の話を実感したりしました。
テストコードがあるのがありがたかったです。通らねーなーなんだろう、てゴールがあるのは大学時代の実習を思い出しました。

やるならネットワークかなぁ。TCP とか DNS とか。

オブジェクト指向入門した(つもり)

モチベーション

買った時は オブジェクト指向 #とは を源流を知りたくてかった、はずでした。
実際に届いたら厚さにビビったり、仕事でコード書く頻度が下がったりで長い間積読に。

最近本読めてねーなーから一念発起。最後の方は流し読みでもいいからまず読み切ろうって感じでした。

(咀嚼しきれてないけど) 感想ぽいもの

  • separate てなんか Future ぽい
    • 解決したい課題は同じで、非同期の箇所を型やなにがしらで明確化したいってところだろうか
  • 拡張型と参照型の区別をしない言語ってどうやって対応するんだろ
  • コンカレントエンジニアリングのクラスタって、境界付けられたコンテキストとかそういうのと似た印象
    • 故、めっちゃ難しいとおもってる
  • 継承なげーなーとおもうけど、長さに納得する内容。こんな自分の足を容易に撃てるもんつかってたのおれ?
    • 過去のやらかしが脳裏をよぎる
  • クラス不変表明勘違いしてた。んで、本に書かれてる定義にすげー納得できた
    • 業務知識をどこに持たせるといいのだろう、は今後の研鑽
  • 電子書籍版が欲しい。マジでほしい
    • 体力的にも精神的にもきつい
    • 検索機能とかこれなんだっけって目次ジャンプとか
  • 今まで学んだこととか実践したこととかを想起しながら読めたりした

おわりに

たぶん要所要所でよみなおすんだろうなー

rusoto が async/.await 対応したのをきっかけにさわってみた雑感

タイムラインに ↓ が流れてきたのでこれはやらねば async/.await 対応 してみての雑感です。

雑感

  • 感触はよさげ。.await?Result 型もむけたりする
  • .await 忘れが罠臭い。コンパイルできるし起動できるがスルーされる
  • Option::unwap_or_else とかがちょっとつらそう? 回避はできるし、まだわかりきってない可能性あり
  • Stream trait の実装はあきらめた。Pin とか Unpin とか Poll をわかってないと分かったのでいつか再チャレンジ

Stream を crate でどうにかしてもらった

Kinesis Stream って名前である以上どうしても Stream にしたかったので、調べたら async-stream を見つけられました。
実際には以下のよう使いました。

    pub fn stream(mut self) -> impl Stream<Item = Result<GetRecordsOutput, Error>> {
        try_stream! {
            loop {
                if let Some(current) = &self.token {
                    let r = self.get_records(current).await?;
                    self.token = r.next_shard_iterator.clone();
                    yield r;
                } else {
                    let next = self.get_iterator_token().await?;
                    self.token = Some(next);
                    continue;
                }
            }
        }
    }

k-iter/kinesis.rs at 94c79facb6e2459eedf3ce85a40dab034007f129 · ROki1988/k-iter · GitHub

私がつかめてない Streamasync/.await の関係性のところにするっと入ってくれました。
個人的には前よりもすっきりした印象を持ってます。

つづく

まだ 0.43.0-beta.1 なので注視しつつ、折を見て master に merge してきます。
ここ最近コード書いてなくてさび付いてるかなーとおもったんすが、まだ何とかなってました。
何とかなるぐらい Rust のコンパイルエラーとか fmt とか clippy が優秀ってのもある。ありがたし。

Rust.tokyo に参加した

rust.tokyo

遅ればせながら感想を。*1

まずは、スタッフや登壇者のみなさん、本当にありがとうございました。
セッションや Networking Time で非常によい刺激を受けられました。

セッション一覧で ServoTiKV by PingCAP が見られたので、努めてそちらを聞きに行きました。
英語で翻訳なしでしたがまぁそこは頑張ってみようと。
特に印象に残っている感想を以下で書いてきます。

Lifetimes: A Survival Guide

資料

そうそう! とうなづいてました。
データがどう入れ子になってていって、いつまで活きてて、あこのデータは一人歩き出来なきゃだから Clone しようとか。
Rust に惹かれた lifetime や Ownership の考えが視覚化されててよかったです。 まとめの画像、よいすよなー。

16d_summary

The Hitchhiker’s Guide to Servo Contributor

資料
登壇者の方の Blog

Servo をいじってみたことがあったのでこちらに。
こちらも W3C の標準化の話やブラウザベンダーとの関連、実際のコントリビュートのやり取りなど、幅広くも実際にやられたことが書かれてて、すげーなーと感じ入っておりました。

個人的な挑戦として初めて英語で質問しました。何とか伝わってよかった。
ちゃんと聞きたいことが先にあってでしたよ。「Servo のフルビルド、結構待ち時間いるけど、ローカルでの開発どうしてます?」ってやつです。 でこれでしたw

真面目な回答としては、インクリメンタルビルドもしっかりしてるし、今はそれかな、だったはず。

Contributing to Rust

いーろいろ感じるものがありました。時差の話とか、一日のコントリビュートにかける時間の話とか。
ちゃんと咀嚼してからなんか書きたいです。
聞けて良かった内容でした。

Networking Time

かつてないぐらいしゃべってた気がします。うるさくてすいません。
Networking Time の意義にそった行動はとれてたかなー。よかたよかた。

おわりに

最近コード書く機会を捻出できてきてるので、さらーにモチベーションが得られました。
ゆるく、やってきます。

*1:風邪ひいて頭痛で倒れてでした。やっと回復

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

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

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

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

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

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

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

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