退職メモ

9/20に退職した(正確には最終出社で今は有給消化中)のでちょっと記録も兼ねてメモしておく。

blog書くのももうこういう機会しかないなぁ...

 

入社したのが2018年05月なので1年4ヶ月程在職した事になる。

原因は色々あるが上の方と考え方が根本的に違う事に気づいたから。またこちら側にも問題があっただろうが向こうがあまりにも不誠実な対応だったため。残る選択肢もあったが、年齢的に残留しても後々辛いだけなのも分かっているし早めに決定したほうが無駄な時間を過ごさなくても良いだろうという判断でした。

 

仕事としては解析基盤を0から作ったり、通常のweb開発したり。今までの経験から当たり前にするべき事をしていなかった部分に色々意見だして意識を変えたりとか。

入社した直後だとドキュメントなんか必要無いという意見が当たり前だったり、週の半分は無駄なMTGで時間使って開発遅かったり、カウボーイ開発だったり... 入社した時点で起業して10年ほどの会社だったがあまりにも酷すぎた(まあ今でも色々問題はあるけど...)。それらに意見して意識を変えてという所から変えていった。

まあそれでも根本は変わらないようで結局退職という道を選択しました。

 

次はまだ決まってませんが、今個人で開発しているプロジェクトがあります。当面はラーメン代を稼ぐのを目標にしているけど、その先もぼんやりとイメージはあるのでちょっと頑張ってみようかと思っています。また平行して転職活動も考えているのでどこかいい所があれば有り難いなぁ。

MySQL、PostgreSQLのdump csv/tsvを読み書き出来るgemを作った

久々の投稿ですね。

もう気力もだいぶ落ちてきたので投稿も無いかと思ったらネタができたので。

 

仕事で何をしているのか

まずは仕事内容から。gemも仕事で必要性があって書いたコードなのでここの説明は必要でしょう。

 

さて、ここ何年も複数の仕事で不定期で解析基盤に関わってきました。

現在の仕事でも0から基盤作りを担当しています(ちょとお休み中だけど)。

環境はWeb側がMySQL、DWH側はRedshiftという構成。

基盤の開発は当然ローカルで行いますが、互換のDBとしてPostgreSQLになります。RedshiftはPostgreSQL互換を謳っているので妥当な選択でしょう。

まあ今時ならよくありそうな構成でしょうか。

 

問題

ここで問題になるのがデータの移行。MySQLからPostgreSQLへのデータ移行。

 

まず、標準のcsv/tsvと異なるのでRubyの標準添付のCSVライブラリがそもそも使えません。例外が発生して処理が一切できません。

もう少し言えばRFCCSVのフォーマットが一応定義されていますが、Wikipediaにあるようにそもそも自然発生的に現れた物なので本来は標準の仕様というのが決めづらいフォーマットでもあります。RubyCSVライブラリで処理できないcsv/tsvは多数あるでしょう。さらに文字コードから改行コード等考えるべき事は多数あります。

RFC 4180 - Common Format and MIME Type for Comma-Separated Values (CSV) Files

Comma-Separated Values - Wikipedia

 

次の問題はMySQLPostgreSQLのdumpしたcsv,tsvのフォーマットが違うこと。

MySQL

MySQLはきちんとした仕様を探せませんでしたが、このURLにある程度まとまっています

MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.6 LOAD DATA INFILE 構文

また、実験をしていて気づいたのですがdump方法によってもフォーマットが異なるようです。

例えば `mysql -e'select * ...'` とした場合、

  • `"` でカラムを囲まない。 逆に `"` が含まれていてもエスケープはされない
  • `\n` 等の特殊文字は `\\n` とエスケープされる

`LOAD DATA...` でdumpする場合は

  • `"` はエスケープを行うが、 `\"` とエスケープされる

と言った具合です。

基本的には`LOAD DATA...` でdump/loadを行うのが仕様的に安全な様に見えます。

また確認はしていないですがバージョンや特殊な型(例えばgeometry等の型では確認をしていません)によっても多少の変化がある可能性はあります。

PostgreSQL

PostgreSQLの場合はこのURLが良い資料でしょう。

https://www.postgresql.jp/document/9.4/html/sql-copy.html

 

これを読むとMySQLとは異なり基本的にはRFCに従うのが原則のようです。

ただし、RFCではカバーしきれない `NULL` 等の特殊な場合の対応として独自対応が入るようです。

また、MySQLとは異なり `\0` はおそらく扱えない様です。

PostgreSQLでは `COPY` でdump/loadをするのが安全なようです。

その他問題

MySQLPostgreSQLのテーブル名に使える文字列の最大長が異なる(他のDBの種類でも違います)、仕様上の問題からあるDBにはinsertできるデータが他のDBにはinsertがそもそもできない、等の複雑な問題があるようです。

つまりは完璧にデータ移行自体が不可能な場合が仕様上ありえます。

 

上記問題をはじめは知らず、sed等で頑張って変換を行おうと頑張りましたが失敗をし、色々なテストや調査から真面目に変換が必要と判断して、仕方なく変換ライブラリを作成するに至りました。

ちなみに既存のツールを探すのも当然行いましたが、これもどうもこれと言ったツールがないようでした

このページが良くまとまっていますが、これといったツールがなかったり、調査だけでも非常に多くのコストが必要と判断し諦めました。

Converting from other Databases to PostgreSQL - PostgreSQL wiki

変換方法

基本的にはRubyで1文字づつ読み込みstackにためて判定を行うparserの作成を行いました。

とは言え仕様を完全に把握もできず仕事の範囲内で問題が出ないレベルにとどめています。

またPureRubyなコードの為どうしても動作は重めです。

レポジトリ

レポジトリはこちら。下手な英語ですがサンプルコードがあるのでまあなんとなく分かるかと思います。

仕事のコードからはクラス名等いくつか細かい変更を行っていますが動作は同じです。興味があれば使ってもらえればと。

https://github.com/longicorn/rdb_csv

転職done

本当に久々の更新。
精神的にも疲れていたし更新する気力も起きなかったが、心の余裕も出来たし記録もしておきたいので更新。
なんかblogどうやって書けば良いかすら忘れている...


5年ちょとほどいた会社を退職した。
その前の職から少し書いておく。

前々職

この前はソシャゲでいわゆるポチポチゲー時代。
Web開発といえばそうだが、通常のWeb開発とは異なり大分特殊な開発だった。
給与は400万届かないレベル。残業もひどく毎日終電という状況。いくら頑張っても現場にリターンは無く、他の諸々で退職。
まあそれでも元組み込み屋からしたら色々面白く学びも多かった。ソウルジェムは濁りまくったが。

スキル的にはCからPython(2系)とか、使ったことがないDatabaseとか、組み込み屋の感覚からしたらアリえない仕様書なしのカウボーイ開発とか、
変化が激しく新鮮でもあった。
まあそれでもシステムプログラミングとかの知識が役に立たないかと言えば意外と役に立つのはちょとびっくりな記憶。
Pythonでもメモリを意識しないと駄目だったり、裏で動くデーモンのコードを読まざるを得なかったり。

また、新しいパラダイムを学ぶ必要にも迫られた。カウボーイ開発だからこそ如何に楽をしながら後々まで悪影響を出さないか、開発スピードを早めるかが課題となる。
ここを解決しないと徹夜しても間に合わない。テストコードを取り入れたり、アジャイル等を学んだり、組み込み時代にはあまり使わない確率、統計あたりを利用して楽をして問題を解決したり。組み込み時代だと線形代数とかそっち系が多かった記憶。

これらの考え方は次の職場でも生きることとなる。

前職

あまりにもアレだったので、次の職の条件は、多少なりとも給与UP、普通のWeb開発、あと個人的に好きなRubyでの開発、ビジネスとしてはSESとかでは無く自前サービス、このあたりを前提に探して引っかかったところに入社(行きあたりばったりともいう...)。


ここでは色々学ばせてもらった。良い所も悪いところも。

会社とは一切関係ないが、まず良くなったと感じたのは開発環境周り。Ruby,RailsというWebだと珍しくもない普通な環境。ただそれだけだが前職というかPython(2系)の環境と比べたら非常にやりやすかったのを覚えている。マルチバイト周りとか、DBとかのバックエンド系との連携のしやすさとか、テストコード周辺の環境が整っていて入りやすかったり、etc...

そして、転職の目標の一つである"普通のWeb開発"。これは達成。Railsでビジネス的にもある意味普通。
これは良かった。いくら大量のデータを扱うDatabaseと大量のアクセスを扱っても、最先端なクラウドサービスを使っても得られない普通の知識と経験はやっぱり大事。
スマホアプリ開発も経験したし、とあるqueryのパーサ的なのを作ったり、位置情報系や自然言語処理にも片足を突っ込んだり色々やらせてもらった。なかなか良い経験だったように思う。

ただ、普通なWebサービスな会社ではあるが技術寄りの会社じゃあないのが最終的にはネックになった。
営業が幅を利かせているのであまりにも目先の売上しか見ない体質とエンジニアリングの軽視、現場の人へのリターンのなさ、etc...。営利企業としては正しいかもしれないがエンジニアとしては非常に辛い日々。そしてついに心が折れて転職となりました。

次は業界自体は似たような会社だが、より技術よりの会社。さてどうなるか不明だが頑張りたい。次こそは...
そして、それとは別でプライベートで売上を意識したサービスも作りたいなぁ。

[Ruby] sendとmethod_missingと分かりづらい仕様

つい最近、腰痛で一時動けなかくつらい思いを久々にしました。
昔痛めて生涯の友となった腰痛ですが、数年に一回来るので辛いです。


さて、最近はRubyでとあるライブラリを作成しているのですが、黒魔術を使わざるを得ない状況はどうしても出てくるものです。
そこで不可解なエラーに遭遇しました。

コードを単純化するとこうなります。

class Test
  def method_missing(method, *args)
    Test1.new.send(method, *args)
  end
end

class Test1
  def method_missing(method, *args)
  end
end

Test.new.select(1)


実行するとなぜかエラー。

$ ruby hoge.rb 
hoge.rb:3:in `select': wrong argument type Integer (expected Array) (TypeError)
        from hoge.rb:3:in `method_missing'
        from hoge.rb:12:in `<main>'

試しに他の存在するメソッド(例えば__id__とか)を呼ぶと問題は無い。
Test、Test1クラスはObjectクラスを継承しただけなので、ほぼ同じ。
https://docs.ruby-lang.org/ja/latest/class/Object.html を見るとselectは存在しないはず...
仕方なく同僚に相談して、二人で悩んで解決しました

ちなみにTest1を使用しなくてもこれで反応します。

class Test
  def method_missing(method, *args)
    send(method, *args)
  end
end

Test.new.select(1)


まず、見るべきはsendの仕様。
https://docs.ruby-lang.org/ja/latest/class/Object.html#I___SEND__

send, __send__ は、メソッドの呼び出し制限 にかかわらず任意のメソッドを呼び出せます。


メソッドの呼び出し制限 にかかわらず??


そして、private methodを見るとselectが存在する!

> Object.new.private_methods.sort
=> [:Array, :Complex, :DelegateClass, :Float, :Hash, :Integer, :Rational, :String, :__callee__, :__dir__, :__method__, :`, :abort, :at_exit, :autoload, :autoload?, :binding, :block_given?, :caller, :caller_locations, :catch, :default_src_encoding, :eval, :exec, :exit, :exit!, :fail, :fork, :format, :gem, :gem_original_require, :gets, :global_variables, :initialize, :initialize_clone, :initialize_copy, :initialize_dup, :irb_binding, :iterator?, :lambda, :load, :local_variables, :loop, :method_missing, :open, :p, :print, :printf, :proc, :putc, :puts, :raise, :rand, :readline, :readlines, :require, :require_relative, :respond_to_missing?, :select, :set_trace_func, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :sleep, :spawn, :sprintf, :srand, :syscall, :system, :test, :throw, :trace_var, :trap, :untrace_var, :warn]

つまり、この流れでエラーとなっているようです

  • sendはprotect、privateであっても呼び出しが可能
  • Object#selectがprivateメソッドとして用意されている
  • よって、Test1#method_missingを呼ぶ前にselectメソッドを見つけてエラーとなる

さて、問題はどうするか?ということですが、メソッドのリストをよく見るとpublic_sendというのがあるようです
sendをpublic_sendにすると問題なく呼ばれたようで解決しました。

class Test
  def method_missing(method, *args)
    Test1.new.public_send(method, *args)
  end
end

初めはRubyのバグを踏んだ?と思いましたが、よくよく調べると仕様通りの動作ですがなかなかややこしい問題でした。
黒魔術はほどほどに...

Ubuntu 16.04でNVIDIAのドライバを入れるのが簡単に!

数年ぶりに腰痛で2,3日動けなく、結構辛い日々を送っています。
高校生の時からの持病で仕方ないのですが、やっぱり辛い。
攻殻機動隊的な世界はまだでしょうか?


さて、Ubuntu 16.04になってもnouveauは負荷をかけるとすぐ死ぬ問題が相変わらず続いています。
流石に嫌になってきたので重い腰を上げて調べてみると、aptに普通にnvidiaのドライバがあるようです。

$ apt-cache search nvidia|sort
〜省略〜
clibcuda1-361 - NVIDIA CUDA runtime library
nvidia-304 - NVIDIA legacy binary driver - version 304.131
nvidia-304-dev - NVIDIA binary Xorg driver development files
nvidia-304-updates - NVIDIA legacy binary driver - version 304.131
nvidia-304-updates-dev - NVIDIA binary Xorg driver development files
nvidia-331 - Transitional package for nvidia-331
nvidia-331-dev - Transitional package for nvidia-340-dev
nvidia-331-updates - Transitional package for nvidia-340
nvidia-331-updates-dev - Transitional package for nvidia-340-dev
nvidia-331-updates-uvm - Transitional package for nvidia-340
nvidia-331-uvm - Transitional package for nvidia-340
nvidia-340 - NVIDIA binary driver - version 340.96
nvidia-340-dev - NVIDIA binary Xorg driver development files
nvidia-340-updates - Transitional package for nvidia-340
nvidia-340-updates-dev - Transitional package for nvidia-340-dev
nvidia-340-updates-uvm - Transitional package for nvidia-340-updates
nvidia-340-uvm - Transitional package for nvidia-340
nvidia-346 - Transitional package for nvidia-346
nvidia-346-dev - Transitional package for nvidia-352-dev
nvidia-346-updates - Transitional package for nvidia-346-updates
nvidia-346-updates-dev - Transitional package for nvidia-352-updates-dev
nvidia-352 - Transitional package for nvidia-361
nvidia-352-dev - Transitional package for nvidia-361-dev
nvidia-352-updates - Transitional package for nvidia-361
nvidia-352-updates-dev - Transitional package for nvidia-361-dev
nvidia-361 - NVIDIA binary driver - version 361.42
nvidia-361-dev - NVIDIA binary Xorg driver development files
nvidia-361-updates - Transitional package for nvidia-361
nvidia-361-updates-dev - Transitional package for nvidia-361-dev
nvidia-current - Transitional package for nvidia-current
nvidia-current-dev - Transitional package for nvidia-current-dev
〜省略〜

nvidia-**の数値はバージョンのようですので、とりあえず最新版を入れます。
あとは再起動なりすればドライバが自動で適応されます。
昔のめんどくさい作業はなんだったんだろう...
起動してからlsmodすればドライバが適応されているのが分かるはずです
OSのインストール時に自動で入れてくれたら嬉しいのですがなにかあるのでしょうね

OOPが何かとかで盛り上がってる

このあたりで盛り上がっていますね。
http://blog.goo.ne.jp/viscuit/e/8ea0ce6d1d3aab0f95d92fd42c473558
http://qiita.com/shibukawa/items/2698b980933367ad93b4
https://twitter.com/yukihiro_matz/status/730032023163691009


個人的には、OOPってメモリとその操作を一体化する、というだけだと思う。
こういうのは何故生まれたか? Cのような手続き型から考えるのが手っ取り早い。
(適当のコードを書いているでのバグっていても許して貰えれば嬉しい)

OOPが無い時代の代表的な言語はCで、こんな操作になるだろう。

int a;

a = 3;

twice(a); //3 * 2 = 6
minus(a, 1); // 3 - 1 = 2

別に不思議な操作ではないんだが、twiceやminusの引数の型はintになるはずだ。
これをlongでもやりたいとなると、関数の型を変えるしかない。
でも、intでもlongでも動かしたいとなると、twice_int、twice_longが必要となるだろう(longの方でintの計算もできるという話は分かっているし、置いておく。そこが目的では無いので)。

これが2,3回程度なら良いが汎用的なことを考えると大変だ。
ましてや、関数の種類が増えるとさらに厳しいし、間違えた関数を呼ぶこともあるだろう。


さて、Cだと構造体で自前の型を作って開発するのが当たり前だ。
慣れてくると、この関数と型が本質的に別なので、めんどくさくなり構造体に関数も定義するようになるのは珍しくない。
こんな具合だ。

struct Hoge {
  int x;
  int  (*twice)(int);
};

struce Hoge hoge;

hoge.twice(2); //めんどくさいので、twiceは別に設定済みだとしておく


これだと、構造体と関数が一体になっている。
呼び出しも一体化できる。C++に近いし、OOPといって良いと思う。


OOPの良さはここにある、と個人的には思っている。
継承や、カプセル化がどうのや、メッセージだはどうでも良い。
上記のCでのOOPも含めて、そこは実装の話だ。
そうじゃあなくて、上記のようにデータと関数、つまりメモリと操作が一体になっている部分だ。

データ部分は別にプロセスでも良い。
OSはプロセスという実行部分とメモリをセットにした単位だ。
かなり強引だが大きな目で見るとこれもOOだし、Shellのパイプはプロセスからプロセスへとデータを渡しているので、これもOOPだろう(通常はそうは言わないが)。

ようは、それだけでOOPは非常に単純なんだといつも自分は思っている。
極論すぎるのかなぁ。

NeoBundleからdein.vimにしてみた

しかし、大分久しぶりの投稿。
NeoBundleがオワコンで、dein.vimに移行してね、ということらしいので対応してみた。

インストール

https://github.com/Shougo/dein.vim にある通りにインストールする。

$ curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > installer.sh
$ sh ./installer.sh ~/.vim/dein

.vimrcの設定もreadmeを見つつ設定すると問題なく動作した。
ちなみに、pulginをインストールするには、こうする。

:call dein#install()

.vimrcにこれを書く方法もあるっぽいが、毎回インストール/アップデートするのも重そうなのでやめておいた。

.vimrc分割

.vimrcが長年使い続けた結果そこそこ長くなって自分でも辛い状態になっていた。
どうも調べると.vimrcは別ファイルを読み込む機能があるようなので、ついでに整理もしてみた。

~/.vim/hoge.vimというファイルを作り、これを読み込ませるにはruntime!とする。

runtime! hoge.vim

あとは機能毎に分割するのだが、置き場所に悩んだのでとりあえず~/.vim/vimrcs/以下に放り込むようにして、.vimrcではruntimeだけを行う方式にしてみた。
これは短くなって綺麗なんだけど一度にぱっと把握できないのはちょっと不便。


ということで個人のvim設定レポジトリを更新した。
https://github.com/longicorn/.vim
あと、nvimも気になるのでそのうち試してみるのも手かな?