命短し学べよTechを

Tech系について学んだことやあれやそれを記録します。

「SOFT SKILLS ソフトウェア開発者の人生マニュアル」を読んだ

職場で「今後のキャリアとかどうしたらいいか考えてるんですよねー」という話をしたときにオススメしてもらって読んでみました。 これを読んで自分の道が明確に決まるものではないですが、考える方法や選択肢などについて知見が広がったため良かったです。

Amazon CAPTCHA

内容紹介の引用

ソフトウエア開発者専用に、「より良い人生」を送るためのノウハウ・スキルを網羅した、生き方バイブル本です。
プログラマーが良い人生を送るためには、技術習得法やキャリア構築法といったノウハウに加え、対人的な交渉・指導・意思疎通などをうまく行える能力や知恵、すなわちソフトスキルが不可欠です! 
本書では、キャリアの築き方、自分の売り込み方、技術習得法、生産性の高め方といった仕事で成功する方法だけでなく、財産の築き方、心身の鍛え方、恋愛で成功する方法など、「人生全般をより良く生きる方法」を具体的に説明します。

本書は技術の具体的な話は出てきません。 トレンドとしてどの技術を身につけるべきかということではなく、もっと汎用的な、キャリアを築くための心構え、ひいては幸せに生きるための心構えやテクニックを教えてくれます。 章立てがかなり細かく分かれていて(全7部71章)、必要ないところは読み飛ばしちゃってもOKです。(なにぶん取り扱っている内容が広いので)

僕が一番印象に残った箇所は プロとアマチュアの違い の部分です。 プロは 従うべき信念を持っている のに対して、アマチュア頼まれたことなら何でもする 。 プロは首尾一貫していて、安定している。自分の仕事に対して高品質を維持できるので、周りもそれを当てにできる。

今の職場は周りの人達が優秀な人達ばかりなのでつい言われたことをそのまま受け入れてしまいがちですが、誰かが深く事情を把握していない状態で発言したり、意見が衝突する間に立たされるような場面はよくあることだと思います。そういう場面で言われるがまましているとみんなが不幸になったりしますよね。(思い当たることが何個も...)

ちゃんと自分の基準を持って、自分の仕事の質を常に一定に保つ。基準に満たないことがあれば、誰が相手でも声を上げる。基準が間違っていれば直していけばいいので、自信を持って発言する。 これを気をつけようと思いました。

全71章のうち1つを取り上げましたが、他にも様々な領域で良い知見が得られる内容になっているので、オススメです。

Mysql, BigQuery, AthenaをCLI上で処理する

はじめに

社内のデータを見てトラブルシュートすることが多いのですが、コマンド1つでこれらのデータを元にほしい情報が得られる調査用scriptを作成し、自分含め他メンバーにも配布して利用してもらうということを行いました。 その際に得た知見や工夫した点をまとめます。

Mysql

gem mysql2 を利用しました。 MysqlSSH gateway経由で接続しますが、利用頻度が高く複数のscriptで利用されるため、ラッパークラスを作成しました。

class HogeMysqlClient

  # 各種設定は別途yamlで読み込む
  CONFIG_DATASOURCE = YAML::load(ERB.new(IO.read(File.expand_path('../../config/datasource.yml', __FILE__))).result)[ENV['STAGE']].freeze

  def initialize

    # CONFIG_DATASOURCEのvalidationなど記載
    ...

    @gateway = Net::SSH::Gateway.new(
      CONFIG_DATASOURCE['ssh']['host'],
      CONFIG_DATASOURCE['ssh']['name'],
      port: CONFIG_DATASOURCE['ssh']['port'],
      keys: CONFIG_DATASOURCE['ssh']['keys']
    )
    @local_port = @gateway.open(CONFIG_DATASOURCE['mysql']['host'], CONFIG_DATASOURCE['mysql']['port'])

    @client = Mysql2::Client.new(
      host: '127.0.0.1',
      username: CONFIG_DATASOURCE['mysql']['username'],
      password: CONFIG_DATASOURCE['mysql']['password'],
      database: CONFIG_DATASOURCE['mysql']['database'],
      encoding: 'utf8',
      port: @local_port,
    )
    # ファイナライザを登録することでscript終了時にgatewayをcloseする
    ObjectSpace.define_finalizer(self) { @gateway.close(@local_port) }
  end
end

queryメソッドを用意します。 symbolize_keys: true をつけることでハッシュのキーがsymbolになるためscriptで扱いやすくなります。

  def query(query)
    @client.query(query, symbolize_keys: true)
  end

今回はqueryをdisplayしつつ結果が欲しかったので以下のようなメソッドも用意しました。

  def run_query_and_display(query)
    puts query
    query(query)
  end

呼び出し側

@mysql_client = HogeMysqlClient.new

query = "SELECT ..."
records = @mysql_client.run_query_and_display(query)

CLI上で結果を表形式で出力するため、 gem terminal-table を使います。

rows = records.map { |record| record.values }
puts Terminal::Table.new title: "User", headings: records.fields, rows: rows

ちなみに特定のカラムに対しては以下のように値を取得できたりします。

user_ids = records.map { |record| record[:user_id] }

BigQuery

今回は gem open3 を利用して bq query の標準出力を受け取る形にしました。

※bq queryコマンドには[gcloudのsetupが必要です。] (http://cloud.google.com/sdk/docs/?hl=ja)

bq queryコマンドをそのままputsすれば表形式で結果が得られます。

query = "SELECT ..."
puts Open3.capture3(%{bq query --use_legacy_sql=false "#{query}"})

ただしこれだと以下の問題があります。

  • 特定のカラムを処理できない
    • 返り値が表形式の文字列のため
  • Titleがつけられない
    • Mysqlをterminal-tebleで表示する際にはTitleをつけられる

この場合は bq query にformatを指定することでjson形式で結果を受け取ることができます。

stdout, stderr, status = Open3.capture3(%{bq query --use_legacy_sql=false --format=prettyjson "#{query}"})
records = JSON.parse(stdout)

rows = records.map { |record| record.values }
puts Terminal::Table.new title: "User", headings: records.first.keys, rows: rows

こんな感じで表示しつつ、カラム毎に処理できるようになります。 が、これにもまだ欠点があります。 それはJSON.parseしたときにSELECTした順番が保証されないため、これを出力しようとするとカラムの順序がバラバラになってしまうことです。

これに対処する場合は gem google-cloud-bigquery を利用します。

bq_cli = Google::Cloud::Bigquery.new(
  project: "test-bq-xxxxx", #BigQueryのプロジェクトID
)

query = "SELECT ..."
records = bq_cli.query query

rows = records.map { |record| record.values }
puts Terminal::Table.new title: "User", headings: records.headers, rows: rows

スマートですね...! ちなみに今回なぜ gem google-cloud-bigquery を使っていないかというと、scriptの環境構築をdocker化することを見据えたときに、gemの認証周りが面倒になりそうだったからです。 おそらく認証用JSONファイルを手元に落として、Volumeする感じで行けるのかなという感じですがまだ試せていない。

Athena

gem aws-sdk-athena を利用しました。

ATHENA_CLIENT = Aws::Athena::Client.new
ATHENA_RETRY_INTERVAL = 1
ATHENA_TIMEOUT_SEC = 300

def run_athena_query(query)
  start_query_response = ATHENA_CLIENT.start_query_execution({
    query_string: query,
    query_execution_context: {
      database: CONFIG_DATASOURCE['athena']['database'],
    },
    result_configuration: {
      output_location: CONFIG_DATASOURCE['athena']['output_location']
    },
  })

  batch_started_time = Time.new
  execution_id = start_query_response.query_execution_id
  loop do
    sleep(ATHENA_RETRY_INTERVAL)
    resp = ATHENA_CLIENT.batch_get_query_execution({ query_execution_ids: [execution_id] })
    break unless resp.query_executions[0].status.state == "RUNNING"

    if (Time.now - batch_started_time).to_i >= ATHENA_TIMEOUT_SEC
      ATHENA_CLIENT.stop_query_execution({ query_execution_id: execution_id })
      raise RuntimeError, "queryの実行時間が長すぎるため処理を中断しました。"
    end
  end

  ATHENA_CLIENT.get_query_results({ query_execution_id: execution_id })
end

query = "SELECT ..."
query_response = run_athena_query(query)

急にコード量増えたぞオイという感じですが、ここではqueryを投げつつ以下の処理も実装しています。

  • 1秒毎にjobのstatusを確認して完了を待つ
  • 300秒経っても完了しない場合はtimeoutする

AWS SDKにはWaiterというclassがあり、これを継承しているClientは wait_untill という上記機能を持ったmethodが使えるのですが、現時点ではAthenaは未対応だったため自前で行っています。

また、 get_query_results の返り値は非常に扱いづらいです。 そのためMysqlなどと同じように扱えるようformatしました。

def parse_athena_query_response(query_response)
  rows = query_response.result_set.rows

  # headerのみの場合
  return [] if rows.size == 1

  header = rows[0].data.map { |row| row.var_char_value }
  rows[1..-1].map do |row|
    row.data.each_with_index.reduce({}) { |acc, (value, idx)| acc[header[idx].to_sym] = value.var_char_value; acc; }
  end
end

こうしちゃえば後は同じように扱えます。

records = parse_athena_query_response(query_response)
puts Terminal::Table.new title: "User", headings: records.first.keys, rows: records.map { |record| record.values }

自己紹介

はじめに

ブログ初投稿です。Shindoです。
ハンドルネームとしてpharuqとかcitymvとか使ったり使わなかったりします。ちなみにブログ名は小一時間考えてようやくつけました。そこ、ダサいとか言わない。
このブログでは普段学んだTech系のことやあれやそれについてアウトプットする場として使う予定です。

当記事では簡単に自己紹介したいと思います。よろしくお願いします。

自己紹介

経歴

三重県立いなべ総合学園高等学校 → 中京大学情報理工学部情報メディア学科 → 株式会社リンク・アップ → Repro株式会社(現在)

小学生の頃にネトゲにハマる(詳細は後述)。 また中学1年の終わり頃にギター(を教えてくれる人)に出会う。当時三重の片田舎ではバンドメンバーを集めることは到底できなかったため、ネットでバンドメンバーを募集して名古屋に通いバンドを始める。それなりに本気でやっていたが最終的に練習嫌いのため挫折して辞める。

大学ではメディアアートについて学ぶが、理系の割にガチガチな感じではなかったため大体遊んで過ごす。卒論は実写映画を作ってそれについて論文を書いた。

就活ではエンジニア志望で、「自分の裁量が大きいところがいい」と言ってろくに深く調べもせず大手ではなく中小企業を受ける。しかし入ってみるとそこは裁量が大きいベンチャー企業ではなく、大手ベンダーの下請けを細々とやっているSESだった。(当時の僕には見分けがつけられなかった)
そこでは自治体向けの財務会計システムの開発及び導入作業を担当した。開発言語はJava, Cobol, VBAなど。下請けの割には設計から開発まで一貫してプロジェクトに携わることはできたのでその点では良かったが、Cobolが現役な点、テストはエビデンスとしてキャプチャをエクセルに貼り付ける、ソースのバージョン管理はコメントアウトによって行うなど、後から思うととんでもなくレガシーな環境だった。
その頃趣味優先人間だった僕は特に疑問に持つことなく働いていたが、あるとき「もっと楽しく仕事がしたい」と思うようになったこと、またマネージャーの道を進み始めるところだったが物を作れる人間でありたかったため転職を考え、Ruby, Ruby on railsを独学で学んでWebサービスを公開した。(summoners-haunt.comというLeague of Legendsプレイヤー用SNS。登録人数10人ほどでサービス終了した)

その後偶然の人づてでReproと出会い、CRE teamとしてJoinして半年ほど経ったところ。 Reproでは主に顧客のテクニカルサポート、そしてそれを潤滑に行うためのツール作成や仕組み作りなどを行っている。

好きなこと

ゲーム

特に対戦系オンラインゲームが好きで、チームを組んで大会を目指すような遊びが好きです。 過去にやったゲームを羅列するとこんな感じ。

  • メイプルストーリー
  • RED STONE(小学生の頃なんとか金を貯めて月2500円の課金をする)
  • サドンアタック
  • ファンタジーアース ゼロ(Dカセ 5年くらいはやってた最高のゲームその1)
  • 天鳳(最高6段)
  • ストリートファイター4(格ゲー下手)
  • S4 League(最高のゲームその2 マイナーなゲームだったが日本代表として大会に挑む)
  • League of Legends(NAサーバー時代最高Dia2 大会目指すも自分が原因で負けが続き解雇される)
  • Hearth Stone(最高シーズン途中だけどLegend 10位くらい 今もスマホでちょこちょこやってる)
  • スマブラSP(最近ちょっとやってた。VIP部屋に行ったり出たり)

社会人になってからはやり込めないので疎遠気味です。
オフゲーは昔はやってました。好きなところだとFF、逆転裁判ロックマンエグゼなど。

E-Sports観戦

ゲームに付随してですが見るのも好き。 SF4の頃はよくそこらへん追ってました。 最近はLJLを観てます。

映画

ホラー以外はオールジャンル観ます。ただアクションは比率少ないかもでヒューマン系の心に残るのが好き。 今ぱっと思いつく好きな作品はこんな感じです。

久々にこんな文章書いたらめっちゃ時間かかった! 疲れたのでこれくらいにします、よろしくお願いします。