Yabu.log

ITなどの雑記

新卒入社したSESの会社を退職しました

新卒で入社して3年半ほど務めました。9月26日が最終出社日でした。 前職でどのようなことを経験してきたか書いてみようと思います。

前職について

一応正社員契約でしたが、SESと呼ばれるものでお客様先に派遣されて勤務します。いわゆる客先常駐というやつです。メリットは短期間で複数の現場に派遣されるので、転職することなく複数の業界/現場で働くことができることだと思います。

前職で担当した仕事&仕事に関連して学んだこと

Salesforce

salesforceは簡単に言ってしまえばインフラとかミドルウェアの構築が一切不要なPaaSのことです。

簡単な顧客管理機能等がバインドされています。画面をクリックするだけで簡単なアプリケーションを作成することができます。

また

  • apexというjavaに似た言語
  • visualforceというhtmlに似たマークアップ言語*1
  • SOQLというSQLに似たクエリ言語

を組み合わせることで柔軟なアプリケーションの構築が可能です。割と少ない工数で形になるものが出来上がるので、前職のような小さい会社と相性がいいのかなという印象です。

Salesforce社は自社でprogateのようなSalesforceを学べる教育サイトTraiheadを持っており、これの出来が非常に良いです。

COBOL,Javaマイグレーション

COBOLで書かれたシステムをJavaで書き直すという経験が出来ました。業務分析などはせずに基本的にCOBOLのソースを一行単位で一致するJavaに置き換えます。一応証拠としてJavaのソースに対応するCOBOLコメントアウトして添えていました。こんな感じです。

//PRODUCT-NO     PIC 9(8). 👈移行後のJavaのコードに対応するCOBOLのコードをコメントアウトして残しておく
Integer productNo;        //👈 移行後のJavaのコード

デバッグスキル

諸事情により納期前のどうしてもバグが取れない5万行程度の機能のバグ取りの担当になりました。 ほぼ新人の自分が完成できるか不安しかありませんでした。

とにかく自分が知っている道具は全て駆使しました。Eclipseの条件付きブレークポイントとか、Watch式とか。 結局、EclEmmaというプラグインを導入してメソッドごとにカバレッジを取りながら、 微妙テストデータを変えながら調査を行ったのが一番効きました。正しい値を出す/出さない時でカバレッジが変わっているメソッドに徹底的にブレイクポイントとWatch式を貼ってデバッグしました。

バグは結局全て見つかりました。

  • ryokin12[1][2]こういう複雑な変数名や[]内のindex
  • if分の大小条件

が微妙に違ったりしているものでした。 見つかった時は創意工夫して結果が出たことが非常に嬉しかったし、納期にも間に合うことができました。でも汎用機からオープン系へのマイグレーションは社内にノウハウがあるような巨大なSIerでないとしんどいと思います。

テスト

テストをすることが多かったため 在籍中にテストに関する本を5冊読みました

知識ゼロから学ぶソフトウェアテスト 【改訂版】

知識ゼロから学ぶソフトウェアテスト 【改訂版】

テスト駆動開発

テスト駆動開発

ソフトウェアテスト293の鉄則

ソフトウェアテスト293の鉄則

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)

はじめて学ぶソフトウェアのテスト技法

はじめて学ぶソフトウェアのテスト技法

※5冊目は今読んでいます

テストは製品の品質を担保する重要なプロセスだと思っています。 そこが軽視されていたり、いい加減だったりするのよくありません。 今ではテストは知的興奮が大いにある物作りのエキサイティングな分野の一つだと思っています。

テストや品質保証を専門にキャリアを築いている人も社外で何度かあいました。 そういう人の生き方は尊敬しているし、決してテスト=雑用では無いと思っています

偉そうなことを行っているが以下は自分もよくわかっていません

  • 複雑な平行/並列処理が絡むテスト
  • 大規模なシステムの負荷テスト
  • テストの自動化のためのテクニック(特にフロントエンド周り)

フロントエンド周りの知識

フロントエンド周りの技術は進歩というか変化が特に激しく、 サーバーサイド側の人間が片手間で追従できるものでは無いと思っています。 だからフロントエンド周りのスキルは時間をかけて基礎からじっくり勉強するつもりはありませんでしたが、 どうしても作っているものに納得が行かなかったため何冊か本を読んで勉強しました。

作りながら学ぶ HTML/CSSデザインの教科書

作りながら学ぶ HTML/CSSデザインの教科書

JavaScriptパターン ―優れたアプリケーションのための作法

JavaScriptパターン ―優れたアプリケーションのための作法

※3冊目は見事に途中で挫折していますね

yuyubu.hatenablog.com

フロントエンドエンジニアとして世間一般で通用する自信はありませんが、 フロントエンド技術を利用しているチームの成果物の改善にはかなり貢献できたと思います。

フロントエンド技術(UI/UX)の本質的な難しさは,非技術者のもっとも関心が高くなる部分という点だと思います。 現場によってはカジュアルな仕様変更などが多数発生します。

ここをしっかり戦い抜いていくには技術以外にも人間工学とか、心理学とかもしっかり抑えて 「なぜそのデザインがBestなのか」を説得できる能力が必要だと思いました。

Javaとかオブジェクト指向とか

大学時代はほとんどJavaを触っていませんでした。趣味でAndroidアプリの入門書を一通りやったり、大学で1コマそういう授業があっただけでした。

Javaは動かし始めるまでに覚えないといけない概念が多いわりに、それが駆使できるのはもっと経験が必要なので、覚えた概念の恩恵みたいなものはイマイチわからなかった。 結局最初はC言語によくわからないキーワードが大量に引っ付いたプログラムみたいな印象を受けました。

で就職してからはこれを使わなくてはならないと思って、Javaを本格的に学んだ。

なぜ、あなたはJavaでオブジェクト指向開発ができないのか―Javaの壁を克服する実践トレーニング

なぜ、あなたはJavaでオブジェクト指向開発ができないのか―Javaの壁を克服する実践トレーニング

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

Effective Java 第3版

Effective Java 第3版

Javaは動かし始めるまでに覚えないといけない概念が多いわりに、それが駆使できるのはもっと経験が必要なので、覚えた概念の恩恵みたいなものはイマイチわからなかった。

大学時代分からなかったこの箇所がわかるような気がしてきた。 だから必死で勉強しました。特に後者の2冊はとても勉強になった。 Javaのプロジェクトには数週間しか関われていないけど*2Java及びその関連技術(JVM言語等)を自分の強みにしていきたいという思いがあります。

正規表現

業務で正規表現エンジンを作ったという話ではありません。*3

事務作業が8割くらいの時期があり、雑務にひたすら正規表現を使って進めることで退屈をしのいでいました。

きちんと本で勉強して覚えたわけではないけど、1ヶ月程度毎日ググりながら正規表現を書いていたので、機械的に行えば良いだけのテキスト変換、置換などは、加工対象の文字を大体見ただけでどういう正規表現を書けば良いか思いつくし、ほとんど空で書けるようになりました。

正規表現完全に理解した!と思って調子に乗ってネットにいい加減なこと書きまくって、ある日ruby正規表現エンジン(Onigmo)を作ってる人から訂正コメントが飛んできて、自分は何も理解できてない、と自信を失ったりしたこともありました。

qiita.com

位置指定子っていう概念が微妙に理解できていないこと以外はだいたい実践的な部分では困らなくなった。あと計算量とかパフォーマンスの理解が不十分。

大学のころ、正規言語とかオートマトンの授業を取っていたので、この辺はきちんと勉強したいですね。

SQL&RDBMS

開発がやりたかったけど、結局規模の大きい保守案件に配属になった。データはたくさんあったので*4SQLRDBを勉強すると自分にも会社にも+になるだろうなと思い、勉強し始めた。

ある日バグの影響範囲を調査するため、再帰クエリが適応できそうな調査案件があった。 他のメンバーはお手上げだったが、急いで技術調査をして調査のクエリを作った。その時復習した内容は以下の記事にまとめてあります

qiita.com

こういう風にやればできますよと提案して、結局自分が担当になってそのデータを調査、修正するクエリを書いた。 あの時は純粋に学んできたことが活きた数少ない瞬間だった。自分にも会社にも+になったと思う。

Excel VBAや自作スクリプトによる調査の効率化

大規模なシステムだったので、目grepが厳しいのでExcel VBAJavaScriptを使って効率的に調査をするツールを作成しました

VB.NETのクラスを調査するファイルを作っていて、

https://teratail.com/questions/118863

vbのこんな仕様に悩んだりしていました。

プログラムの解析を正規表現でゴリゴリ書いていましたが限界を感じました

http://leoclock.blogspot.com/2009/01/blog-post_27.html

仕事関係ないけどこの経験から少し言語処理系とかに興味を持つようになりました

セキュリティ

詳しくは書きませんが、システムのセキュリティ強化のため個人的に勉強していました。

「プロになるためのWeb技術入門」 ――なぜ、あなたはWebシステムを開発できないのか

「プロになるためのWeb技術入門」 ――なぜ、あなたはWebシステムを開発できないのか

色々指摘することができましたが、進捗にはマイナスでしかなかったので、迷惑だったかなと思います。

会社へのSlack導入

脱メールでのコミュニケーションを目標に、上司への提案でSlackをチームで採用していただきました。チーム、部という順番で広がっていったのでおそらく、退職後しばらく経てば全社的に使っていると思います。

これから中小企業で導入して見たいけど、使い方が良く分からない。。。という人は最近は技術コミュニティでもSlackを積極的に使っているので、 そちらに参加して慣れながら活用方法を学んでいけば良いと思います。

私ができなかったこと、足りなかったこと

自社/協力会社の有志で勉強会

どうしても業務で経験できることには限界があるので、エンジニアには業務以外での技術研鑽が必要だと思います。 SESというのは客先の稼働時間が大事なので基本的に就業時間内に勉強はできませんが、 もっと仕事で使っている技術やフレームワークについて、特に新人を含めた勉強会などができたらよかったと今になって思います。

ちなみに仲良くなったパートナー会社の社員さんには技術書を貸したり、JavaScriptの基本を教えたりしていた。

コミュニケーション

私はお酒が飲めないこともあって、非常に人付き合いが悪かった。別に飲み会を欠席したことで非難されるようなことは1度くらいしかなかったけど、 そういう所で信頼関係が結べていなかった所でスムーズに物事が運ばなかった面はあったと思います。

またあまり失礼とか気にせず思ったことを口にするタイプだったので、色々勉強して見ました。

yuyubu.hatenablog.com

まだまだ未熟者ですが日々修行中です

今後

働く前は開発こそ至高みたいな考え方があったけど、自宅学習でそれなりにスキルを高めて、問題解決ができれば保守/調査/テストも面白いと思いました。 でも開発をあまりやらせてもらえなかったので開発をしたい思いが強いです

今後は以下の業務に関わってみたいです

  • Java関連技術を使ったWEBサービスの構築、運用、保守
  • DBA
  • データベースに関連する開発

最後になりましたが、前職では社長、前社長、前前社長をはじめ、様々な方々のお世話になりました。 本当にありがとうございました。

*1:今はLightning Componentという技術が使われているそうですが、過渡期に抜けたので最近の事情は知りません

*2:COBOLマイグレーション案件を除く

*3:正規表現エンジンは作ってみたい。

*4:記憶が曖昧だが1億レコードのテーブルもあった

Java読書会「現場で役立つシステム設計の原則」を読む会 第2回に参加

遅れて午後から参加しました。今回は、書かれていることは確かに理想だけど...という切り口の議論が多かったように思います

かんしんごと vs かんしんじ?

YutakaKatoさんと漢字の読みで張り合っています

www.youtube.com

私は著者が講演で「かんしんごと」と発音しているビデオ(1:28:2~)に影響を受け、これにつられて「かんしんごと」で通しています

なぜ「関心事」だけ「じ」と読むのでしょうか?

「心配事」はいわば和語扱い、「関心事」は漢語扱いだからです。

oshiete.goo.ne.jp

日本語の読み方的には「かんしんじ」が正しいようです😇。DDD界隈読みということもありえるのでしょうか。

まぁ漢字の読み方とかにこだわっているわけではありませんが、この本やたらと「関心事」という単語が出てくるので、どうしても気になってしまうのです。

なんと252回も!出てきますw

とりあえずDRYみたいなのは有害

  • 共通しているからという理由でまとめてはいけない。ドメインの観点から検討して、本当に共通か? を意識して共通化する必要がある。
  • 通化というのは結果にすぎない。
  • とにかくDRY原則!というのが有害
  • 日時系のAPIは日付概念はものすごく抽象化されているから便利に使える
    • とりあえずなんとなく共通処理をまとめました、だと誰も使わない

https://qiita.com/tag1216/items/91a471b33f383981bfaa

  • ドメインを考えるのはすごく大変。重複クラスをまとめるだけなら脳みそを使わない。

Accountパターンの出どころは?

関心事のパターン 業務ロジックの内容
口座(Account)パターン 現在の値(現在高)を表現し、妥当性を管理する
期日(DueDate)パターン 約束の期日と判断を表現する
方針(Policy)パターン 様々なルールが複合する、複雑な業務ロジックを表現する
状態(State)パターン 状態と、状態遷移のできる/できないを表現する

業務ルールを記述するドメインオブジェクトの基本パターンより抜粋 

ちょっと議論の内容は失念したが、こういうプログラミングのプラクティスは

エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)

エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)

アナリシスパターン―再利用可能なオブジェクトモデル (Object Technology Series)

アナリシスパターン―再利用可能なオブジェクトモデル (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,堀内一,友野晶夫,児玉公信,大脇文雄
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2002/04
  • メディア: 単行本
  • 購入: 7人 クリック: 89回
  • この商品を含むブログ (70件) を見る

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

などが元になっていたりするらしい。 読書家な人が集まっているので「このパターンは一般的なやつ、このパターンはXXに乗ってるやつだね」という流れになったが、Accountパターンは何が元になっているか?という結論が出なかった。 エリックエバンスの本は手元にあったので一応中を調べてみたが、それらしいものはなかった

  • PoEAAの訳はひどい
    • とても読めたものではないので捨てた

粒度の小さいクラスを大量に作ることの是非

本書では単位や属性のクラスをかなり細かく作成していくが、クラス数が増えるほど依存関係が増えたり複雑になるので、とりあえずクラスを小さくすれば良いというものではない。

個人的に大事なことは責務が混ざらない程度に小さくすることだと思う

他技術的なトピックなど

適当に議論中に出たトピックをまとめます

  • for文でコレクションを確認しているコードはStream APIのAnyMatchなどを使うべき

  • JavaのStringクラスの方がクラス設計としては良いが、結果的にRubyが文字列をただのバイト列として扱っているやり方のほうが結果的に成功している

    • JavaがStringクラスに採用している設計パターンが結果的に良くなかったという趣旨の話があった
  • JSR-305

  • 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

Concurrency Control and Recovery in Database Systems

目次です

競合(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されていないトランザクションの数が減少する
    • ブロック(ロック解放待ち)とデッドロックがDC Thrashingを引き起こす。特に後者は致命的。
    • DC Thrashing point(Thrashingが起こり始める臨界点みたいなイメージ)
      • DC Thrashing point以降に1つのトランザクションの追加で複数(2以上)のトランザクションが平均的にブロックされてしまう
      • DC Thrashing pointを超えてロックキューが増加するとシステムの使用率がかなり低下する
    • DC Thrashing point以降にシステムに負荷をかけるとパフォーマンスが大きく下がる

競合の解決方法

  • 競合(Data Contation)の解決方法は2種類ある

    • blocking戦略:ロックが解放されるまで待たせるロックキューを作る(waitさせる)
    • restart戦略:専有しようとしたリソースがすでにロックされていた場合、トランザクションをabortして最初からやり直す
  • 一般的なRDBMSの実装状況

    • blocking戦略をとっているもの
      • MySQLはデフォルトは50秒待ち
      • MySQL Clusterは1.2秒
      • DB2は2分待ち
      • Oracleは永久待ち?
    • restart戦略をとっているもの
      • SQLServerはabortしてredo(デフォルトは無限回試行する)
  • blocking/restartは設定で選べないのか?
    • 中身の処理内容が全く違うので設定で切り替えるような問題ではない

blockingとrestartsのパフォーマンスの比較

  • restartする方が一見コストが高いように見える
    • 再起動のコストを払う必要になるため
    • 中断した箇所までのトランザクション処理の成果は捨てられるため
  • が実際にはblocking戦略の方が早く性能劣化(Thrashing)を引き起こす
  • blockingがrestartsより良いという理屈は次の状況が両方発生する場合に
    • restartに時間がかかりすぎる → 実装によって回避可能
    • リソース競合が頻繁に怒る → CPU数を増やすことによって回避可能
  • restartの方がblockingより応答時間は増える

    • restert時にロック解放する必要があるため*1
  • ロックの取り合いをさせるくらいならアボートさせる方が良いというのが最近のDB界隈のトレンド

  • トランザクションは本来APエンジニアに見せるべきではない

    • 本来ならAPエンジニアにはcheckpointを設定させるだけにするべき
  • ブロックは利己的

  • リスタートは自己犠牲的

  • abortは処理が重そうだけどlockの方が軽いのではというのが一般的だが、実態はabortさせてredoする方がパフォーマンスが良い。

    • この本はrestart派
    • 神林さんはblocking派
      • ただし単純なlockではなく後ろにずらす
        • 先日のsundialの論文の発表でも似たような事を言われていたような?
    • 最近はトランザクションの量が多いので、キャッシュの汚染率が高い
      • 定説としてこの本に書かれている内容から時代が変わっている
        • ただこの本が古いという訳ではなく、lockとrestartのコストはアーキテクチャに依存するので随時計測する必要がある。

2PLの検討(Basic 2PL,Conservative 2PL)

Basic 2PLとConservative 2PLについて地球で一番わかりやすい解説はこちら

qiita.com

※以後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 W = k^{2}N/D \lt 1.5

だから性能限界は

 k^{2}N/D \lt 1.5

となる。が、これは

  • DBアクセスが一様的である前提
    • ピークとかない。増減しない平均的な接続が常に発生するイメージ
  • リソース競合を考慮していない

なのでかなり楽観的な数値である。 あくまで1.5というのはこの規模の数字である、という参考を示しているにすぎない。

MPLについて

  • MPL(並列化するプロセス数)は1.5D/k2以上あげるべきではない。
    • N(MPL)をあげるとデッドロックが発生する率も上がる
    • restartのコストが高い場合はさらにNを下げる必要がある

Long Transactionについて

以下の事象はスループットを下げる要因となる

そもそもトランザクションよりもロックが増えることによる影響が大きいので トランザクションを分割することでworkloadをあげることができる

例えば

前述の式

 W = k^{2}N/D

に当てはめると

(k/n)^{2}(2N)/D = k^{2}N/2Dとなり、これは分解前のW=k^{2}N/Dより倍よくなっている。

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によるスループットの変化は以下のグラフのようになる

f:id:yuyubu:20180928005540p:plain
Granularityとスループットの関連。p95から引用したものに補足説明したもの

  • ただしこのグラフのようなスループット増減を必ずとるわけではない

    • 例1:データベースの大部分にアクセスする長いトランザクションの場合
      • 粒度を細かくしても結局ロックが増えるだけなのでDの増加に対してkが伸び悩まないため①の性能劣化より後のスループット変化が起こらない
    • 例2:短いトランザクションに関してはDの増加に伴うkの伸び悩みが早く起こるので①の性能劣化が現れない
      • いきなり右肩上がりのグラフになる
  • Read Lock

    • 今まではWrite Lockを前提に考えてきたが、lockの何割かがread lockと考えるとパフォーマンスは工場する
  • アクセスの偏り
    • アクセスの偏りが発生しているとworkload(負荷)増加の恐れがある

他雑談

  • MVCCは理解に3年かかる。説明できるようになるのに8年かかる
  • 神林さんのdb tech showcase2018の発表ですが、資料公開/ブログで再整理される予定があるそうです!
  • ANSISQL標準の資料は、新しいものは有料だが、ベータ版は公開されていて読めるらしい
  • プログラミング言語のコンペでSQLを使って2位になったチームがあったらしい?
    • ISUCONではない。
  • COBOLで作られたWEBサーバーがあるらしい?(プロジェクト)
    • COBOLerにもなんでもCOBOLで実現する変態的な人がいるらしい

次回は「3.13 TREE LOCKING」から!

*1:ロック解放は時間がかかるような処理なのか?

dbtech showcase Tokyo2018に参加

dbtech showcase Tokyo2018に参加

f:id:yuyubu:20180925232952j:plain

このカンファレンスは「プログラマのためのSQL」の参加者から聞いて知りました。 大企業/メガベンチャーのDBの運用ノウハウだったりデータベースベンダーの方から最新の製品テクノロジなどが楽しく聞けた3日間でした。

ストレージ

  • A27:DBエンジニアのためにSSD Q&A集

SSDのセッション。内容は以下の4つ

  • 1.製品寿命
  • 2.TLC
  • 3.enterprise向けssd
  • 4.NVMe仕様SSD

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分程度延長されていた

発表内容としてはこちらの論文を読みながら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
  • 分散システムと普通のRDBMSではcommitの意味が違う
    • 普通のRDBMS:データを保存する(ACID)
    • 分散システム:データを確定させる(他のサイトから見えるようにする)

あとちなみに別セッションですが...

Amazonのマルチマスタなデータベースに関して分散システムの勉強会で話題に登りましたが...

マルチマスターでのデッドロックの検知はそんなに優しくない AWSはマルチマスターをやっているらしい(無謀?超絶技術?)

分散処理本第37回に参加

書き込み競合は先勝ちとなっているようです。ではデッドロック検知は...気になりますねぇ(やっぱりタイムアウトなんでしょうか?)

大企業のDBA

他にもLINEさんのセッションやリクルートさんのセッションが面白かったです。 普段は聞けない大企業のDB運用事例について聞くことができました。

リクルートさんの方は社内事例の紹介という訳ではなく、昨今話題の様々なデータベース関連製品の紹介&整理でした。分量が多すぎて理解しきれていませんが、格DBやその使い分けに関してわかりやすく紹介されていました。

LINEさんは自社でMySQL関連のツールを色々作られているそうです(面白そう) 1日目のYahooさんの発表も聞いておけばよかったと後悔。

勉強時間の大半をRDBMSに費やしていましたが、今までDBAの方のお話を聞く機会が今までなかったので刺激的でした。

*1:メガネ...これがないと発表会では何も見れない

*2:https://taityo-diary.hatenablog.jp

*3:やるなら原子時計とか使ってやる。今の技術でも難しいと聞いたことがある

プログラマのためのSQL 読書会(27)に参加

余談ですが参加者にdbtech show case tokyo 2018の参加者も多かったので、そちらの話題で雑談などが盛り上がりました。*1

「29.7ビット単位の集約関数」から読みました。

プログラマのためのSQL 第4版

プログラマのためのSQL 第4版

適切に設計されたOLTPデータベースは正規化されている。通常、DWHはスタースキーマスキーマまたはスノーフレークスキーマを採用しており、これはかなり非正規化されている。

ペタバイト以上のデータを持つ企業

ペタバイト以上のデータを持つ企業だと書かれているが、 このほんの情報は古く、2014年時点では売上10億ドル以上の 企業の56パーセントが 1PB以上のデータを管理している*2

  • 巨大なデータを扱うDB

  • dbtech show caseで話題のMySQL8.0の環境構築がうまくいかなかった

  • 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が書かれていないのでそちらも欲しい

  • 99年にANSI SQLRoll up,Cube関数が入った

  • 最新のMySQLにwindow関数に対応した
  • 最新のSQLiteはWindow関数に対応した
  • CUME_DIST()関数
    • 初めて見たので検証予定
  • Window Frame
    • 理解しきれてないので検証予定

前職ではウインドウ関数を多少していたのですが*3、WindowFrameを弄ったりは普段やりません。*4 ので、この辺は技術検証の必要があるかなと思います。

次は「30.6ベンダー拡張」から!

*1:参加されていない方申し訳ない

*2:http://www.jdsf.gr.jp/backup/stm/img/201507/01.pdf

*3:ウインドウ関数簡単なものなら空でかけます

*4:普通はウインドウ関数もあまり利用しないと思いますが、プロダクションのSQLの品質が疑わしい場合かなり使えます

分散処理本第38回に参加

Distributed Computing: Principles, Algorithms, and Systemsを読む勉強会です。やはり難しいので少し予習していきました。アルゴリズムの分類までは一応軽く読んで概要をつかんでから臨みました

予習をしてブログの下書きを作っておくと、感想のポストがサクッとかけて良いですね。

Distributed Computing: Principles, Algorithms, and Systems

Distributed Computing: Principles, Algorithms, and Systems

分散システムに置けるデッドロックの検出アルゴリズムの分類

  • 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が発生していれば帰ってくるようなエコーメッセージを送る。

よく解らん?という話になった

参考にしてある論文は以下のもの

    1. Chandy, J. Misra, and L. M. Haas, Distributed deadlock detection, ACM Transactions on Computer Systems, 1(2), 1983, 144–156.
  1. 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

アルゴリズムとしてはpublic/privateなlabelを使う 文章化するのがちょっと難しいが一言で表すなら

  • 待ち状態に対して2種類(private/public)のラベル作成し値zを与える
    • 下の例はu->v間の待ちを表しているこのzはu+vより大きな値かつユニークでなければならない
    • この操作を待ちの連鎖が終わるところまで続けて行く

f:id:yuyubu:20180918234652j:plain

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日ほどかかった。

とりあえず自分は日頃競プロ的な頭を使うプログラミングをやらないので、 プログラミング能力が劇的に落ちていることを実感した。

時間をかけて競プロを強めようとは思わないが、アルゴリズムとかのエンジニアとしての基礎的な部分を固める必要があると再度認識した。

コードはあまり人に見せられるような綺麗なものではないけど、時間がかかった課題のものを中心に公開しておきます。

フィボナッチ

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 などがあります。

f:id:yuyubu:20180918023900p:plain
(x+y)/2
f:id:yuyubu:20180918023856p:plain
x * y
f:id:yuyubu:20180918023902p:plain
x ^ y

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のnilJavaのnullとも違うし、nil自体がnil型のような特性を持っているような振る舞いがあるようなので、 この辺りは今後注意して学習して行きたい。

感想

今までは写経中心だったり、ロジックというよりもAPIのドキュメントを調べて書かれている通りに実装することが中心だったが、Goできちんとプログラミングを書くのは初めてだったのでいい経験になったと思う。

何よりも、tour of Goをやらねばいけない、という憑き物のようなものが落とせてよかった。

*1:と行っても周回するつもりはあいrません