Java読書会「現場で役立つシステム設計の原則」を読む会 第2回に参加
遅れて午後から参加しました。今回は、書かれていることは確かに理想だけど...という切り口の議論が多かったように思います
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法
- 作者: 増田亨
- 出版社/メーカー: 技術評論社
- 発売日: 2017/07/05
- メディア: Kindle版
- この商品を含むブログ (4件) を見る
かんしんごと vs かんしんじ?
(業務の) 関心事 ← 「かんしんじ」vs.「かんしんごと」で意見が割れている。自分は「かんしんじ」派かなぁ #javareading
— YutakaKato.jar (@kagaorange) September 29, 2018
#javareading
— yuYabu@転職中 (@yuyabu2) 2018年9月29日
関心事=かんしんごと?かんしんじ?どっちの読みが正しい?という話になった。昔みた増田さんが話しているビデオだと「ごと」で読んでたような?
増田さんの発表を聞いた人のブログにも「関心ごと」と書かれているので多分「ごと」が適切だと思うのですがhttps://t.co/kn8w21bhpj
YutakaKatoさんと漢字の読みで張り合っています
私は著者が講演で「かんしんごと」と発音しているビデオ(1:28:2~)に影響を受け、これにつられて「かんしんごと」で通しています
なぜ「関心事」だけ「じ」と読むのでしょうか?
「心配事」はいわば和語扱い、「関心事」は漢語扱いだからです。
日本語の読み方的には「かんしんじ」が正しいようです😇。DDD界隈読みということもありえるのでしょうか。
まぁ漢字の読み方とかにこだわっているわけではありませんが、この本やたらと「関心事」という単語が出てくるので、どうしても気になってしまうのです。
なんと252回も!出てきますw
とりあえずDRYみたいなのは有害
- 共通しているからという理由でまとめてはいけない。ドメインの観点から検討して、本当に共通か? を意識して共通化する必要がある。
- 共通化というのは結果にすぎない。
- とにかくDRY原則!というのが有害
- 日時系のAPIは日付概念はものすごく抽象化されているから便利に使える
- とりあえずなんとなく共通処理をまとめました、だと誰も使わない
https://qiita.com/tag1216/items/91a471b33f383981bfaa
- ドメインを考えるのはすごく大変。重複クラスをまとめるだけなら脳みそを使わない。
Accountパターンの出どころは?
関心事のパターン | 業務ロジックの内容 |
---|---|
口座(Account)パターン | 現在の値(現在高)を表現し、妥当性を管理する |
期日(DueDate)パターン | 約束の期日と判断を表現する |
方針(Policy)パターン | 様々なルールが複合する、複雑な業務ロジックを表現する |
状態(State)パターン | 状態と、状態遷移のできる/できないを表現する |
業務ルールを記述するドメインオブジェクトの基本パターンより抜粋
ちょっと議論の内容は失念したが、こういうプログラミングのプラクティスは
エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)
- 作者: マーチン・ファウラー,長瀬嘉秀,株式会社テクノロジックアート
- 出版社/メーカー: 翔泳社
- 発売日: 2005/04/21
- メディア: 大型本
- 購入: 10人 クリック: 635回
- この商品を含むブログ (143件) を見る
アナリシスパターン―再利用可能なオブジェクトモデル (Object Technology Series)
- 作者: マーチンファウラー,Martin Fowler,堀内一,友野晶夫,児玉公信,大脇文雄
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2002/04
- メディア: 単行本
- 購入: 7人 クリック: 89回
- この商品を含むブログ (70件) を見る
エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)
- 作者: エリック・エヴァンス,今関剛,和智右桂,牧野祐子
- 出版社/メーカー: 翔泳社
- 発売日: 2011/04/09
- メディア: 大型本
- 購入: 19人 クリック: 1,360回
- この商品を含むブログ (131件) を見る
などが元になっていたりするらしい。 読書家な人が集まっているので「このパターンは一般的なやつ、このパターンはXXに乗ってるやつだね」という流れになったが、Accountパターンは何が元になっているか?という結論が出なかった。 エリックエバンスの本は手元にあったので一応中を調べてみたが、それらしいものはなかった
- PoEAAの訳はひどい
- とても読めたものではないので捨てた
粒度の小さいクラスを大量に作ることの是非
本書では単位や属性のクラスをかなり細かく作成していくが、クラス数が増えるほど依存関係が増えたり複雑になるので、とりあえずクラスを小さくすれば良いというものではない。
個人的に大事なことは責務が混ざらない程度に小さくすることだと思う
他技術的なトピックなど
適当に議論中に出たトピックをまとめます
for文でコレクションを確認しているコードはStream APIのAnyMatchなどを使うべき
JavaのStringクラスの方がクラス設計としては良いが、結果的にRubyが文字列をただのバイト列として扱っているやり方のほうが結果的に成功している
- JavaがStringクラスに採用している設計パターンが結果的に良くなかったという趣旨の話があった
JSR-305
@NotNull
などのアノテーションの標準化
Kotlin最高
- Springの開発スピードが早い(Kotlinの開発がついていけてないらしい)
次回は「小さく分けたサービスを組み立てる」から
CC本読み会第12回
Concurrency Control and Recovery in Database Systemsを読む勉強会です。
内容としてはロックのパフォーマンスに関してです。今回のポイントは競合の種類(Resource/Data)と競合を回避する手段(abort/block)がメインだと思います よくわからない箇所があったので自宅で復習しました。
今回はp87から
- 3.12 LOCKING PERFORMANCE
- Resource and Data Contention
- Thrashing
- Blocking and Restarts
- Predeclaration
- A Bound on Workload
- MPL, Transaction Length, and Granularity
- Read Locks and Nonuniform Access
を読みました。次回は「3.13 TREE LOCKING」からになります。
Concurrency Control and Recovery in Database Systems
- 作者: Philip A. Bernstein,Vassos Hadzilacos,Nathan Goodman
- 出版社/メーカー: Addison-Wesley
- 発売日: 1987/02/01
- メディア: ハードカバー
- この商品を含むブログを見る
目次です
競合(Contention)に関して
- 競合には2種類ある。リソース競合とデータ競合。
- リソース競合(Resouce Contation):メモリやIO待ちなどのシステム的な部分
- データ競合(Data Contation):ロックに関連する競合
- 多かれ少なかれ多くのシステムは(物理上のスペックの上限があるので)リソース競合が発生しうる。
- リソース競合以外の競合としてデータ競合が定義できることは発展的な議論をするために便利な概念となる。
- データ競合はリソース競合を緩和する可能性がある
Resource contention also hurts a pure restart policy more than a blocking policy. With the latter, some transactions are blocked in lock queues, so fewer transactions compete for resources. (Thus, data contention alleviates resource contention for a blocking policy.)
- データ競合が起きてできることが少なくなると、リソースの利用率が下がる。
競合に伴うパフォーマンス低下(Thrashing)について
- リソース競合,データ競合それぞれに応じてThrashingが発生する恐れがある
- DC Thrashing:ロックの取り合いによって生じるパフォーマンス劣化のこと(デッドロック含む)
- RC Thrashing:スタベーション,またはlive lockのこと
- DCスラッシングに関してはDBMSのリソースが無限にあっても発生する恐れがある
- DC Thrashing pointを超えてからトランザクションを増やすと、blockされていないトランザクションの数が減少する
競合の解決方法
競合(Data Contation)の解決方法は2種類ある
- blocking戦略:ロックが解放されるまで待たせるロックキューを作る(waitさせる)
- restart戦略:専有しようとしたリソースがすでにロックされていた場合、トランザクションをabortして最初からやり直す
一般的なRDBMSの実装状況
- blocking/restartは設定で選べないのか?
- 中身の処理内容が全く違うので設定で切り替えるような問題ではない
blockingとrestartsのパフォーマンスの比較
- restartする方が一見コストが高いように見える
- 再起動のコストを払う必要になるため
- 中断した箇所までのトランザクション処理の成果は捨てられるため
- が実際にはblocking戦略の方が早く性能劣化(Thrashing)を引き起こす
- blockingがrestartsより良いという理屈は次の状況が両方発生する場合に
- restartに時間がかかりすぎる → 実装によって回避可能
- リソース競合が頻繁に怒る → CPU数を増やすことによって回避可能
restartの方がblockingより応答時間は増える
- restert時にロック解放する必要があるため*1
ロックの取り合いをさせるくらいならアボートさせる方が良いというのが最近のDB界隈のトレンド
トランザクションは本来APエンジニアに見せるべきではない
- 本来ならAPエンジニアにはcheckpointを設定させるだけにするべき
ブロックは利己的
リスタートは自己犠牲的
abortは処理が重そうだけどlockの方が軽いのではというのが一般的だが、実態はabortさせてredoする方がパフォーマンスが良い。
2PLの検討(Basic 2PL,Conservative 2PL)
Basic 2PLとConservative 2PLについて地球で一番わかりやすい解説はこちら
※以後Basic 2PLをB2PL,Conservative 2PLをC2PLと略します
- 理屈上はB2PLの方がC2PLよりロックを保持している時間が短い
- 一見B2PLの方がパフォーマンス上メリットがありそうである
- ただしこれはデータ競合の発生が少ない場合のみ。
- データ競合の発生が激しくなるにつれC2PLの方がロックを保持している時間が短くなる
この節p91の「Predeclaration」ですがなぜデータ競合が多いとC2PLが勝るのか、 根拠がよくわかりませんでした。
結論としてはC2PLを導入することによって - デッドロック回避 - DCスラッシングの阻止(軽減) のメリットある。(後者の方が期待度が高い)
性能限界 ~DC Thrashing ギリギリまでパフォーマンスをあげる~
リソースが無限と考えても、トランザクションを増やしたり、並列化するとスループットは上がるが、どこかで下がるポイント(DC Thrashing)がある。
DC-thrashing thus defines an operating region the combination of parameters that affect the performance.
実はこれは計算で算出可能
- N:MPL(multiprogramming level)多分トランザクション数
- k:一つのtransactionが要求する平均lock数
- D:データ資源
の時DC Waolkoad
だから性能限界は
となる。が、これは
- DBアクセスが一様的である前提
- ピークとかない。増減しない平均的な接続が常に発生するイメージ
- リソース競合を考慮していない
なのでかなり楽観的な数値である。 あくまで1.5というのはこの規模の数字である、という参考を示しているにすぎない。
MPLについて
- MPL(並列化するプロセス数)は1.5D/k2以上あげるべきではない。
- N(MPL)をあげるとデッドロックが発生する率も上がる
- restartのコストが高い場合はさらにNを下げる必要がある
Long Transactionについて
以下の事象はスループットを下げる要因となる
そもそもトランザクションよりもロックが増えることによる影響が大きいので トランザクションを分割することでworkloadをあげることができる
例えば
前述の式
に当てはめると
となり、これは分解前のより倍よくなっている。
Granularity(粒度)について
Granularityの定義はchapter1のp2に書いてある
The size of the data contained in a data item is called the granularity of the data item.
- 小さいD(荒い granularity)は大きな範囲にロックがかかる
- 大きいD(細かい granularity)は小さな範囲にロックがかかる
Granularityが性能に及ぼす要因は3つある
- 1.ロックオーバーヘッド
- granularityが細かいほどロックが必要=オーバーヘッドが増える
- 2.データ競合
- granularityが荒いほどデータ競合が起こる可能性がある
- ただし、granularityが細かければ良いという訳ではない
- 一般的にgranularityが細いほどロックが増える傾向にある
- ロックが増えるともちろんロックの衝突(Data contation)も起こりやすくなる
- 3.リソース競合
- 前述したが、データ競合はリソース競合を改善する可能性がある
- ranularityが荒いとリソース競合が起きにくい。
- ranularityが細かいとリソース競合が起きやすい
- 前述したが、データ競合はリソース競合を改善する可能性がある
上記の3要因を踏まえるとGranularityによるスループットの変化は以下のグラフのようになる
ただしこのグラフのようなスループット増減を必ずとるわけではない
Read Lock
- 今まではWrite Lockを前提に考えてきたが、lockの何割かがread lockと考えるとパフォーマンスは工場する
- アクセスの偏り
- アクセスの偏りが発生しているとworkload(負荷)増加の恐れがある
他雑談
- MVCCは理解に3年かかる。説明できるようになるのに8年かかる
- 神林さんのdb tech showcase2018の発表ですが、資料公開/ブログで再整理される予定があるそうです!
- ANSIのSQL標準の資料は、新しいものは有料だが、ベータ版は公開されていて読めるらしい
- プログラミング言語のコンペでSQLを使って2位になったチームがあったらしい?
- ISUCONではない。
- COBOLで作られたWEBサーバーがあるらしい?(プロジェクト)
次回は「3.13 TREE LOCKING」から!
*1:ロック解放は時間がかかるような処理なのか?
dbtech showcase Tokyo2018に参加
dbtech showcase Tokyo2018に参加
このカンファレンスは「プログラマのためのSQL」の参加者から聞いて知りました。 大企業/メガベンチャーのDBの運用ノウハウだったりデータベースベンダーの方から最新の製品テクノロジなどが楽しく聞けた3日間でした。
ストレージ
- A27:DBエンジニアのためにSSD Q&A集
SSDのセッション。内容は以下の4つ
#dbts2018
— yuYabu@転職中 (@yuyabu2) September 20, 2018
FW進化の歴史
0.何もしない(古いデータの読み取りがどんどん遅い)
1.読み出しごとにリフレッシュ(初回リードが毎回遅い)
2.定期的に強制リフレッシュ(余計な書き込みが発生する)
3.Patrol Read:アドホックにリフレッシュ(電圧が下がっている場所を発見する)
SSDはHDDより爆速で早い、程度の認識しかなく、そこまで興味はなかったのですが、twitterのタイムラインが盛り上がっていたので急遽参加しました。
SSDの仕組みから始まり、寿命やコントローラーの説明、NVMeの展望などが語られました。
SSDはファームウェアは順調に進化しているが、それを利用するOSやFSはまだまだ技術に追いついていない(特にNVMe)という印象を持ちました。
企業システム向けのSSDはまだまだ進化の余地があり、今後ますます注目が集まってくる分野になると思います。
MySQL 8.0
ドキュメントベースのNoSQLデータベースとして利用しつつ、jsonの任意のデータを選択した仮想列を作成してそちらをSQLから参照するというテクニックが披露されました(x dev API)。MySQL8.0では他にもWindow関数が入ったりして大きく標準SQLに近づいたというか、自分の中でMySQL株が上がりまくりです。
トランザクション理論
- A13・14:今後のDBのトランザクション処理のあり方について徹底討議する ~"InvisibleWriteRule: トランザクションの書込み最適化" を中心に
- C33:MVCCにおけるw-w/w-r/r-wのあり方とcommit orderのあり方の再検討~Sundial: Harmonizing Concurrency Control and Caching in a Distributed OLTP Database Management Systemを題材に
1日めのA13/14は忘れ物で*1遅れて参加したため、後半部分しか聞けませんでした。最近気になって購読し始めた「ぱと隊長日誌*2」の筆者の方や神林さん、kumagiさん(参加者)などなど、トランザクションに関する情報発信をされてる方々が勢揃いされていたので、面白いディスカッションだったと思う。
C33は事前に前倒しで開始する...との連絡が非公式にありましたが、5分前に到着したところすでに始まっており、前倒ししたのに10分程度延長されていた
DBTS2018 C33「MVCCにおけるw-w/w-r/r-wのあり方とcommit orderのあり方の再検討」にご興味のある皆様へ、神林さんから伺ったことを共有します。開演時刻を少し前倒しするかも(注:昨年も同様で、かつ終演時刻overの熱さ)。スライド公開予定:Noとなっているけど、公開するかも。(続く)#dbts2018
— ぱと (@pato_taityo) September 20, 2018
今日の神林さんの講演は予想通り前倒し開演、終演時刻ぶっちぎり(でも終わらない)だったな。でも、あの熱の入った講演スタイルは大好きだったりする。#dbts2018
— ぱと (@pato_taityo) September 21, 2018
発表内容としてはこちらの論文を読みながらMVCCやOCCを分散システムに適応するとどうなるか、という発表内容だった。
http://www.vldb.org/pvldb/vol11/p1289-yu.pdf
個人的に印象に残ったのが、
- Sundialのアルゴリズムではロックに頼らず、時系列順(Timestamp Ordering)に頼ってデータの整合性を担保する
- サイト間で物理時間は同期しない。*3同期するのは順序のみで、順序同期なら技術的に可能らしい
- 実行順序を工夫することで、発生順ではありえない操作が実現できる
r1(x2)w2(x2)c2c1→w2(x2)r1(x2)c2c1
あとちなみに別セッションですが...
Amazonのマルチマスタなデータベースに関して分散システムの勉強会で話題に登りましたが...
#dbts2018
— yuYabu@転職中 (@yuyabu2) September 21, 2018
amazonのマルチマスターのコンフリクト解決はcommit先勝ち。
競合する後続のトランザクションはどんなに長くてもロールバック(例外もある)
書き込み競合は先勝ちとなっているようです。ではデッドロック検知は...気になりますねぇ(やっぱりタイムアウトなんでしょうか?)
大企業のDBA
他にもLINEさんのセッションやリクルートさんのセッションが面白かったです。 普段は聞けない大企業のDB運用事例について聞くことができました。
#dbts2018
— yuYabu@転職中 (@yuyabu2) 2018年9月20日
LINEが使ってるデータベース関連プロダクト
・REBMS
MySQL
Oracle
SQLServer
CUBRID(韓国Narva製)
・NOSQL
redis
mongoDB
HBase
elastic
※redisとMySQLがメイン
#dbts2018
— yuYabu@転職中 (@yuyabu2) September 21, 2018
NoSQLはキーバリュー,ドキュメント型
とかで分類するのは古い
NoSQLの新しい分類方法
- レスポンスタイム追求型
- 開発容易性追求型
リクルートさんの方は社内事例の紹介という訳ではなく、昨今話題の様々なデータベース関連製品の紹介&整理でした。分量が多すぎて理解しきれていませんが、格DBやその使い分けに関してわかりやすく紹介されていました。
#dbts2018
— yuYabu@転職中 (@yuyabu2) September 20, 2018
LINEではMySQL Query Replayerを開発中!
ネットワークパケットからクエリを取得してリプレイするツール。
来年のdbtech show caseで発表予定か!?
LINEさんは自社でMySQL関連のツールを色々作られているそうです(面白そう) 1日目のYahooさんの発表も聞いておけばよかったと後悔。
勉強時間の大半をRDBMSに費やしていましたが、今までDBAの方のお話を聞く機会が今までなかったので刺激的でした。
*1:メガネ...これがないと発表会では何も見れない
プログラマのためのSQL 読書会(27)に参加
余談ですが参加者にdbtech show case tokyo 2018の参加者も多かったので、そちらの話題で雑談などが盛り上がりました。*1
「29.7ビット単位の集約関数」から読みました。
- 作者: ジョー・セルコ,Joe Celko,ミック
- 出版社/メーカー: 翔泳社
- 発売日: 2013/05/24
- メディア: 大型本
- この商品を含むブログ (16件) を見る
- 「30.高度な集約、ウィンドウ関数、OLAP」にあるスノーフレークとは
適切に設計されたOLTPデータベースは正規化されている。通常、DWHはスタースキーマスキーマまたはスノーフレークスキーマを採用しており、これはかなり非正規化されている。
- スノーフレークはITでは多義語なので注意すること
- Amazon RedShift
- https://kyrt.in/2014/06/08/snowflake_c.html
- twitter社が作成した分散環境でユニークなIDを振る仕組みと、DWHでのテーブル設計パターンの2つともがスノーフレークを表しているような印象を受けました。
ペタバイト以上のデータを持つ企業
がペタバイト以上のデータを持つ企業だと書かれているが、 このほんの情報は古く、2014年時点では売上10億ドル以上の 企業の56パーセントが 1PB以上のデータを管理している*2
MySQL 8を試したくbrew upgradeしたところ、前バージョンで正常終了してないため起動できない的なエラーで怒られてるなう。
— yuYabu@転職活動中 (@yuyabu2) September 19, 2018
MySQL Sandboxで独立した環境を構築できる
- dbdeployerも検討すべき。
MySQLはなぜ5の次が8なのか
11月にpostgreSQLのカンファレンスおすすめ
P.562のクエリがおかしい
▼P.562 SELECT B.region_nbr, S.city_id, SUM(S.sales_amt) AS total_sales FROM SalesFacts AS S, MarketLookup AS M WHERE EXTRACT (YEAR FROM trans_date) = 2011 AND S.city_id = B.city_id AND B.region_nbr = 6 GROUP BY ROLLUP(B.region_nbr, S.city_id);
- Bという別名はどこから出て来たのか。(Mではないのか?誤植?)
このSQLを実行できるTableが書かれていないのでそちらも欲しい
- 最新のMySQLにwindow関数に対応した
- 最新のSQLiteはWindow関数に対応した
- PostgreSQLの挙動に準拠している
CUME_DIST()
関数- 初めて見たので検証予定
Window Frame
- 理解しきれてないので検証予定
前職ではウインドウ関数を多少していたのですが*3、WindowFrameを弄ったりは普段やりません。*4 ので、この辺は技術検証の必要があるかなと思います。
次は「30.6ベンダー拡張」から!
分散処理本第38回に参加
Distributed Computing: Principles, Algorithms, and Systemsを読む勉強会です。やはり難しいので少し予習していきました。アルゴリズムの分類までは一応軽く読んで概要をつかんでから臨みました
- 分散システムに置けるデッドロックの検出アルゴリズムの分類
- path-pushing
- edge-chasing
- diffusion computation
- global state detection.
- 10.6 Mitchell and Merritt’s algorithm for the single-resource model
- 余談:ACM Degital Libaryは高い
予習をしてブログの下書きを作っておくと、感想のポストがサクッとかけて良いですね。
Distributed Computing: Principles, Algorithms, and Systems
- 作者: Ajay D. Kshemkalyani,Mukesh Singhal
- 出版社/メーカー: Cambridge University Press
- 発売日: 2011/03/03
- メディア: ペーパーバック
- この商品を含むブログを見る
分散システムに置けるデッドロックの検出アルゴリズムの分類
- path-pushing
- edge-chasing
- diffusion computation
- global state detection.
出典:E. Knapp, Deadlock detection in distributed databases, ACM Computing Surveys, 19(4), 1987, 303–328.
※詳しい方が概要を知っているがその分類名を知らない、ということが発生していたので、一般的な分類ではないのかもしれない。
path-pushing
各サイトがWFGを送り合いグローバルなWFGを構築する。 送信しながらどんどんWFGを更新してデッドロックの発生、検知を明らかにして行く
edge-chasing
グラフのエッジに剃ってprobesと呼ばれる特殊なメッセージを送信して行く。 以前送信したprobeと同じものを送信した場合、それでcycleを検出できる
diffusion computation
deadlockが発生していれば帰ってくるようなエコーメッセージを送る。
よく解らん?という話になった
参考にしてある論文は以下のもの
Herman and K. M. Chandy, A Distributed Procedure to Detect AND/OR Deadlock, Technical Report TR LCS-8301, Department of Computer Sciences, University of Texas, Austin, TX, 1983.
本書記載のアルゴリズム例は2つあり、
- Chandy–Misra–Haas algorithm for one OR model
- Chandy–Herman algorithm
前者はWikipediaの記事があった
https://en.wikipedia.org/wiki/Chandy-Misra-Haas_algorithm_resource_model
CMHアルゴリズムとしても有名らしい
global state detection.
分散システムの各スナップショットからデッドロック有無を確認する。しかし以下の条件が必要である
(i) a consistent snapshot of a distributed system can be obtained without freezing the underlying computation.
(ii) a consistent snapshot may not represent the system state at any moment in time, but if a stable property holds in the system before the snapshot collection is initiated, this property will still hold in the snapshot.
10.6 Mitchell and Merritt’s algorithm for the single-resource model
- WFGの逆向きにprobを飛ばす
- edge-chasingアルゴリズムのうちの一つ
アルゴリズムとしてはpublic/privateなlabelを使う 文章化するのがちょっと難しいが一言で表すなら
- 待ち状態に対して2種類(private/public)のラベル作成し値zを与える
- 下の例はu->v間の待ちを表しているこのzはu+vより大きな値かつユニークでなければならない
- この操作を待ちの連鎖が終わるところまで続けて行く
2週することで図のDetect
の状態が発生が把握できる(1週しただけだとDeadlockかどうかわからない)
p360の図は誤植
- Figure 10.2 The four possible state transitions [22]
という図があるが元ネタが「E. Knapp, Deadlock detection in distributed databases, ACM Computing Surveys, 19(4), 1987, 303–328.」という論文の図であり、引用するときに一部記号が間違っている。
また10.6
は本書中の記載のみだと理解が辛く、引用元の論文をよく読み込まないと何を行っているかわからない。
余談:ACM Degital Libaryは高い
500ドルくらいするらしい。。。
次回は「10.7 Chandy–Misra–Haas algorithm for the AND model」から!
a tour of Goをやった
Goならわかるシステムプログラミングで推奨していたのでとりあえず一周して見た*1
https://go-tour-jp.appspot.com/list
Go言語の文法については、全くのプログラミング初心者でなければ、tour of Goという公式のチュートリアルを一通りこなすだけで、本書の範囲なら十分理解できるでしょう
Goならわかるシステムプログラミングp4より抜粋
と書かれていたのでとりあえずやっておこう、というつもりで進めたのだが。。。 だらだらやったのもあってか1.5日ほどかかった。
- フィボナッチ
- ワードカウント?的なやつ。一応テストは全てパスした
- なんか画像を作るやつ
- ASCII文字 'A' の無限ストリームを出力する Reader
- io.Readerをラップした暗号復号処理(rot13)
- conncurenncyを使った二分木探索
- mutexを使ったクローラー
- 他:違和感のある翻訳
- 感想
とりあえず自分は日頃競プロ的な頭を使うプログラミングをやらないので、 プログラミング能力が劇的に落ちていることを実感した。
時間をかけて競プロを強めようとは思わないが、アルゴリズムとかのエンジニアとしての基礎的な部分を固める必要があると再度認識した。
コードはあまり人に見せられるような綺麗なものではないけど、時間がかかった課題のものを中心に公開しておきます。
フィボナッチ
https://go-tour-jp.appspot.com/moretypes/26
DRYにものすごく反しているような???
package main import "fmt" // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { count := 0 v1 := 0 v2 := 0 value := 0 return func() int { defer func() { count++ }() if count == 0 { return 0 } else if count == 1 { v1 = 0 v2 = 1 return 1 } else { value = v1 + v2 v1 = v2 v2 = value return value } } } func main() { f := fibonacci() for i := 0; i < 15; i++ { fmt.Println(f()) } }
ワードカウント?的なやつ。一応テストは全てパスした
https://go-tour-jp.appspot.com/moretypes/23
package main import ( "golang.org/x/tour/wc" "strings" ) func WordCount(s string) map[string]int { wordlists := strings.Fields(s) countMap := make(map[string]int) for _, v := range wordlists { countMap[v]++ } //return map[string]int{"x": 1} return countMap } func main() { wc.Test(WordCount) }
なんか画像を作るやつ
https://go-tour-jp.appspot.com/moretypes/18
package main import "golang.org/x/tour/pic" func Pic(dx, dy int) [][]uint8 { value := make([][]uint8, dy) for i := 0; i < dy; i++ { value[i] = make([]uint8, dx) for j:=0;j<dx;j++{ value[i][j] = uint8(i^j) } } return value } func main() { pic.Show(Pic) }
生成する画像は、好きに選んでください。例えば、面白い関数に、 (x+y)/2 、 x*y 、 xy などがあります。
ASCII文字 'A' の無限ストリームを出力する Reader
package main import "golang.org/x/tour/reader" type MyReader struct{} // TODO: Add a Read([]byte) (int, error) method to MyReader. func(r MyReader) Read(b []byte) (int ,error){ b[0] = 'A' return 1,nil } func main() { reader.Validate(MyReader{}) }
io.Readerをラップした暗号復号処理(rot13)
rot13Reader(io.Readerをラップして換字式暗号を標準入出力に変換してだす)
readerが最後にerrorとしてio.EOFを返すのが解らずに、1時間ほど試行錯誤した。
package main import ( "fmt" "io" "os" "strings" ) type rot13Reader struct { r io.Reader } func (r2 rot13Reader) Read(p []byte) (n int, e error) { i := 0 b := make([]byte, 1) for { _, err := r2.r.Read(b) //fmt.Printf("n = %v err = %v b = %v\n", n, err, b) //fmt.Printf("b[:n] = %q(%v)\n", b[0], b[0]) if err == io.EOF { break } value := b[0] //文字の場合のみ暗号置換を実施する if 64 < b[0] && b[0] < 91 { value = value + 13 if value > 90 { value = value - 26 } } if 96 < b[0] && b[0] < 123 { value = value + 13 if value > 122 { value = value - 26 } } //fmt.Printf("lot13 = %q(%v)\n", value, value) p[i] = value i++ } return i, io.EOF } func main() { s := strings.NewReader("Lbh penpxrq gur pbqr!") r := rot13Reader{s} //b := make([]byte, 100);r.Read(b) io.Copy(os.Stdout, &r) }
解読後は普通の文章になっているので多分あっていると思うが、if文の境界値条件はきちんと検査していないのでバッグっているかもしれない。
conncurenncyを使った二分木探索
https://go-tour-jp.appspot.com/concurrency/8
package main import ( "fmt" "golang.org/x/tour/tree" ) // Walk walks the tree t sending all values // from the tree to the channel ch. func Walk(t *tree.Tree, ch chan int) { //fmt.Println(t) //左があるときはどんどん左に行っちゃう if t.Left != nil { Walk(t.Left, ch) ch <- t.Value if t.Right != nil { Walk(t.Right, ch) } //左がなくなったけど右がある場合はそっちに行く } else if t.Right != nil { ch <- t.Value Walk(t.Right, ch) } else { //葉 ch <- t.Value } } // Same determines whether the trees // t1 and t2 contain the same values. func Same(t1, t2 *tree.Tree) bool { ch1:=make(chan int,10) ch2:=make(chan int,10) Walk(t1,ch1) Walk(t2,ch2) for i:=0;i<10;i++{ v1:= <-ch1 v2:= <-ch2 if(v1!=v2){ return false } } return true } func main() { fmt.Println("test") /* ch := make(chan int) go Walk(tree.New(2), ch) count := 0 for i := range ch { fmt.Println(i*10) count++ if(count == 10){ //closeするタイミングをどこに入れるか解らなかったので //呼び出し側にいれた。こういうプログラミングは良くないと思う close(ch) } } */ b1:= Same(tree.New(1), tree.New(2)) fmt.Println(b1) }
mutexを使ったクローラー
https://go-tour-jp.appspot.com/concurrency/10
Crawlメソッドの呼び出しを並列に(goroutine)にしなければ動く。 並列にするとなぜか正しく動かない。これは自分がmutexとか並列プログラミングにまだまだ無知だからだと思う。 とりあえず私が並列プログラミングをやるとdeadlockだらけになることがわかった。
package main import ( "fmt" "sync" ) type Fetcher interface { // Fetch returns the body of URL and // a slice of URLs found on that page. Fetch(url string) (body string, urls []string, err error) } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { // TODO: Fetch URLs in parallel. // TODO: Don't fetch the same URL twice. // This implementation doesn't do either: if depth <= 0 { return } mux.Lock() if check[url] == 1 { mux.Unlock() return } check[url] = 1 body, urls, err := fetcher.Fetch(url) mux.Unlock() if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { //go Crawl(u, depth-1, fetcher) //並列だと正しく動かない Crawl(u, depth-1, fetcher) } return } func main() { check = make(map[string]int) Crawl("https://golang.org/", 4, fetcher) } // fakeFetcher is Fetcher that returns canned results. type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } var mux sync.Mutex var check map[string]int func (f fakeFetcher) Fetch(url string) (string, []string, error) { if res, ok := f[url]; ok { return res.body, res.urls, nil } return "", nil, fmt.Errorf("not found: %s", url) } // fetcher is a populated fakeFetcher. var fetcher = fakeFetcher{ "https://golang.org/": &fakeResult{ "The Go Programming Language", []string{ "https://golang.org/pkg/", "https://golang.org/cmd/", }, }, "https://golang.org/pkg/": &fakeResult{ "Packages", []string{ "https://golang.org/", "https://golang.org/cmd/", "https://golang.org/pkg/fmt/", "https://golang.org/pkg/os/", }, }, "https://golang.org/pkg/fmt/": &fakeResult{ "Package fmt", []string{ "https://golang.org/", "https://golang.org/pkg/", }, }, "https://golang.org/pkg/os/": &fakeResult{ "Package os", []string{ "https://golang.org/", "https://golang.org/pkg/", }, }, }
他:違和感のある翻訳
呼び出す 具体的な メソッドを示す型がインターフェースのタプル内に存在しないため、 nil インターフェースのメソッドを呼び出すと、ランタイムエラーになります。
https://go-tour-jp.appspot.com/methods/13
編集履歴を辿って原文を確認すると以下のようであった
Calling a method on a nil interface is a run-time error because there is no type inside the interface tuple to indicate which concrete method to call.
https://github.com/atotto/go-tour-jp/commit/123f593e8270035b7f59a8d4eaa0e61bbee98239
具象メソッドを含むinterfaceタプルが存在しないのでnilインターフェースの呼び出しはエラーになります。くらいが妥当な気がする?
というかinterfaceタプル、ってなに?っていう感じでgoをそこまでわかっていないので具体的に今どういう役が適切かは提案できない。
https://qiita.com/umisama/items/e215d49138e949d7f805
だたgoのnilはJavaのnullとも違うし、nil自体がnil型のような特性を持っているような振る舞いがあるようなので、 この辺りは今後注意して学習して行きたい。
感想
今までは写経中心だったり、ロジックというよりもAPIのドキュメントを調べて書かれている通りに実装することが中心だったが、Goできちんとプログラミングを書くのは初めてだったのでいい経験になったと思う。
何よりも、tour of Goをやらねばいけない、という憑き物のようなものが落とせてよかった。
*1:と行っても周回するつもりはあいrません
GolangでUDPサーバー&クライアントを作成してWiresharkで検証する
Goならわかるシステムプログラミング第7章 UDPソケットを使ったマルチキャスト通信に書かれている内容をWiresharkを使ったり、デバッグでライブラリ(netパッケージ中心)のソースを読んだりして検証してみました
サーバー側
- net.ListenPacket()の戻り値のnet.PacketConnインターフェースを使う
package main import ( "fmt" "net" ) func main() { fmt.Println("Server is runnign at localhost:8888") conn, err := net.ListenPacket("udp", "localhost:8888") if err != nil { panic(err) } defer conn.Close() buffer := make([]byte, 1500) for { length, remoteAddress, err := conn.ReadFrom(buffer) if err != nil { panic(err) } fmt.Printf("Received from %v: %v\n",remoteAddress, string(buffer[:length])) _, err = conn.WriteTo([]byte("Hello from Server"), remoteAddress) if err != nil { panic(err) } } }
ReadFrom関数の実装が気になったので確認してみた
//udpsock.go func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } n, addr, err := c.readFrom(b) if err != nil { err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} } if addr == nil { return n, nil, err } return n, addr, err }
//udpsock_posix.go func (c *UDPConn) readFrom(b []byte) (int, *UDPAddr, error) { var addr *UDPAddr n, sa, err := c.fd.readFrom(b) switch sa := sa.(type) { case *syscall.SockaddrInet4: addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneCache.name(int(sa.ZoneId))} } return n, addr, err }
//fd_unix.go func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { n, sa, err = fd.pfd.ReadFrom(p) runtime.KeepAlive(fd) return n, sa, wrapSyscallError("recvfrom", err) }
//syscall_unix.go func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil { return } if rsa.Addr.Family != AF_UNSPEC { from, err = anyToSockaddr(&rsa) } return }
//zsyscall_darwin_amd64.go // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) } else { _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) if e1 != 0 { err = errnoErr(e1) } return }
このSyscall6
の中身は見れなかった。名前的にもこれはネットワーク系のシステムコールなのでしょうか。
という訳で、コールスタックは私の場合は
syscall.recvfrom (/usr/local/go/src/syscall/zsyscall_darwin_amd64.go:146) syscall.Recvfrom (/usr/local/go/src/syscall/syscall_unix.go:262) internal/poll.(*FD).ReadFrom (/usr/local/go/src/internal/poll/fd_unix.go:215) net.(*netFD).readFrom (/usr/local/go/src/net/fd_unix.go:208) net.(*UDPConn).readFrom (/usr/local/go/src/net/udpsock_posix.go:47) net.(*UDPConn).ReadFrom (/usr/local/go/src/net/udpsock.go:121) main.main (/User/Study/go-sys/chap7/udp_server/udpServer.go:18)
となった。
クライアント側
package main import ( "fmt" "net" ) func main() { conn, err := net.Dial("udp4", "localhost:8888") if err != nil { panic(err) } defer conn.Close() fmt.Println("Sending to server") _, err = conn.Write([]byte("Hello from client")) if err != nil { panic(err) } fmt.Println("Receiving from server") buffer := make([]byte, 1500) length, err := conn.Read(buffer) if err != nil { panic(err) } fmt.Printf("Received: %s\n", string(buffer[:length])) }
net.Dial
でnet.Connインターフェース(実装はnet.UDPConn)を取得してそちらにWrite,Readで送受信ができる。
とてもシンプルですね。
キャプチャ結果
この2つのソースで送受信それぞれ1つづつしかパケットが飛んでいない。
UDPはとてもシンプルですが、TCPが信頼性を担保するためにいかに頑張ってくれているか、というのが実感できるのではないでしょうか。
- 作者: 渋川よしき
- 出版社/メーカー: Lambda Note
- 発売日: 2017/10/19
- メディア: テキスト
- この商品を含むブログを見る