その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

golangのstruct custom tagをうまいことparseして値を引っこ抜きたいってとき

type Foo struct {
	Bar string `buz:"qux" iyan:"bakan"`
}

における `buz:"qux" iyan:"bakan"` を良い感じで buz => "qux", iyan => "bakan" のように取得したいというケースでは以下のようにするとよい:

tagKeyValue := reflect.StructTag(`buz:"qux" iyan:"bakan"`)
buz := tagKeyValue.Get("buz") // => "qux"
iyan := tagKeyValue.Get("iyan") // => "bakan"

という具合でreflect.StructTagを使うと良い感じでできます.よかったよかった.

……クソ,なんのためにこんなものを書いてしまったんだ!!!! 良いお年を!!!!

github.com

superuserに対する「ふつーのuser」にどういう名前を付けるべきか

TL;DR

  • unprivileged users
  • ordinary user
  • standard user

などが良いようだ.ordinaryとかstandardはuserに限らず他の種別でも使えそう.

会話の様子

shinpei0213 [14:11]
特権ユーザー root user 一般ユーザー xxxx user
xxxx なんて言えばいいの

moznion [14:12]
prisoner
単にuserだと問題が出るケースってことですよねえ

shinpei0213 [14:12]
そうそう
なるホディウスでした

moznion [14:13]
prisonerは完全に問題があるw

shinpei0213 [14:13]
だめかな
だめか

moznion [14:13]
「囚人」

shinpei0213 [14:13]
normal userだとへん?
でもadd user するときに prisoner とか渡さないっけ
わたさないな
なにかと勘違いしている

moznion [14:13]
jailingの文脈で思いついたw
normal,僕は変だと思わないっすけど,ネイチブの人とかはどう思うのかな
limitedにするとか?

shinpei0213 [14:14]
ah

moznion [14:14]
restrictedとか

shinpei0213 [14:14]
とりあえずnormalにする。azs
restricted いいね!!!!
それにします。天才かよ

moznion [14:15]

sunglasses

ichigotake [14:19]
Unprivileged users と表現しているところもあるようですね
>Unprivileged users can use the su and sudo programs for controlled privilege escalation.
https://wiki.archlinux.org/index.php/users_and_groups

moznion [14:19]
unprivillegedよさそう

ichigotake [14:19]
Unprivileged users でググってみると結構ヒットする

shinpei0213 [14:20]
第二の天才現る

moznion [14:20]
(タイプしにくいことを除くと
↑の僕のpostもタイポしてるしw
unprivilegedのほうが文脈としては正しそうな雰囲気ある

ichigotake [14:28]
英語版wikipedia を見るとこういう表現もあるようだ
standard user
ordinary user
https://en.wikipedia.org/wiki/Superuser

aws-lambda-perl5-layer 書いた

[2018-12-05 追記]
ビルド済みのLayerを公開しましたので,そちらを使うと便利です.
See also: http://moznion.hatenablog.jp/entry/2018/12/05/211523
[追記ここまで]

先日のre:InventでCustom AWS Lambda Runtimesが発表され*1,これはつまり任意の言語でAWS Lambdaを実行することを可能とする機能なんですが,ということはPerl5が動くということでして,すなわち動くと嬉しいはず,したがって動かすためのLayerを書いたという話です.

github.com

この場合のLayerというのはPerl5を動かすための基盤だと思ってください.基本的にはbootstrapが実際にぐるぐる回ってperlの関数を実行するという感じになっております.
どのようにして動いているかは公式のTutorialが詳しいので参考されたい: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html


さて実際やってみた感想としてはbootstrapを書くのは非常に簡単で,ざっくり挙動を説明するとOver HTTPでLambdaのイベントを取ってきてその内容に従って関数を実行して,結果をPOST backするという非常に素朴な感じです.どんな言語でも比較的容易に書けるでしょう.

問題は実行基盤を整備するところで,これは scripts/build.sh に様々集約されているわけですが,エッセンスとしては

  • デフォルトだとPATHは /usr/local/bin:/usr/bin/:/bin:/opt/bin 以下に通っているのでそこにランタイムを突っ込む必要がある
  • Layerのzip archiveのルートは /opt に アタッチされるっぽい
    • なので bin/ やら lib/ やらをzipのルートに入れておけば良い感じになる
    • そこにperlのランタイムを突っ込むという作戦
    • bootstrapが必要とするライブラリ群もあらかじめここに入れておく
  • Layer zipのルートに bootstrap を置いておけばそのファイルがbootstrapとして認識される (つまり実行される)
    • 実行可能なpermissionが当たってないと動かない
  • ランタイムやツールのインストール (つまりzipping) する際には実環境と揃える必要がある
    • 適切なDockerコンテナを使って処理する必要がある
    • lambciの提供しているものを使った

という感じです.結構難しくてハマってしまいました……なお,実際に利用可能な環境変数群については以下を参考にされたい: https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html


まだドキュメントが十分に揃っていなく,また謎挙動もいろいろあるのですが *2 非常に面白い機能だと思いました.夢が広がりますね!!!

設計Night2018で話してきました

connpass.com

スライドは以下です.

id:nkgt_chkonkさんに「パフォーマンス視点でのソフトウェアの設計というテーマでなんか話せない?」と持ちかけられ,当時色々考えていたこともあり「やってみるか!」という感じの経緯でできたのがこのトークです.


言いたかったことはスライド終盤に書いたように

  • 一発で良い設計にたどり着ければ最高だけどなかなかそうもいかんよね〜
  • 良い設計とは「変化に耐えうること」「変更にあたってコストが低いこと」ではないか
  • 設計には段階がある
  • 第一段階としての良い設計は「最適化の余地がある設計」ではないか

ということです.「早すぎる最適化」はもちろん良くないことですが,しかし「最適化の余地を残しておくことは重要」というような認識です.


スライド中に出てくる「美しいものは空気抵抗が少ない」というのは新幹線を開発した三木忠直さんという方の言葉で,つまり「美しいものは速い」という意味なんですが *1 ,かねがね「いい言葉だな〜」と思っていたので引用させてもらいました.困ったときはそっちの方に倒す,という感じで日々を暮らしています.


なお,この発表はズルをしていて,キュー(バッファ)に使うミドルウェアのコストに全く言及していませんでした.しかし前時代と比較してキューのようなミドルウェアはオンデマンドで利用しやすくなった,そして運用コストが下がって使いやすくなったというのはあると思います.Amazon KinesisやSQSやGoogle Cloud Pub/Subなどといったクラウドサービスがキュー(あるいはpub/sub)サービスを提供しているのが大きいと思います.以前は自分でマシンを確保して「ActiveMQを入れるぞ〜〜〜」みたいな感じだったわけですから……


ところで発表の冒頭でも言ったのですが,僕は割と野生の勘でソフトウェア設計をしがち (もちろん理論は基礎として持ちつつ) で,こういう感じで体系的に言語化するのが得意な方ではないのですが、一丁頑張ってやってみましたところ自分の中でも理解が整理されたり深まったりしたので良かったですねという感じです.


設計Night2018,いいイベントでした.ありがとうございました.

*1:必ずしも一般化できる話ではないことは承知していますが

stringsを使ってどのgoのバージョンでコンパイルしたバイナリかをサクッと取得する

How to find out which Go version built your binary | Dave Cheney

stringsを使わないちゃんとしたやり方はこちら.


雑に取るならstringsコマンドを使うとそれっぽいのが取れます.

$ strings main | grep go1[.]
stack=[cgocheckdebugcpugo1.11.1runnableruntime.scavengeunknown( (forced) -> node= blocked= defersc= in use)

なるほど,これはgo1.11.1でコンパイルされたバイナリ.非常にスッキリしています.

$ strings main | grep go1[.]
, not 390625<-chanArabicAugustBrahmiCarianChakmaCommonCopticFridayGOROOTGothicHangulHatranHebrewHyphenKaithiKhojkiLepchaLycianLydianMondayRejangSCHED SundaySyriacTai_LeTangutTeluguThaana[]bytechan<-efenceerrno go1.11objectpopcntselectstringstructsweep uint16uint32uint64 (scan  (scan) MB in  Value> dying= locks= m->g0= nmsys= s=nil
/tmp/test/bin/go1.11
...

これは1.11でコンパイルされている様子.1.11.1と比べてごちゃごちゃ度が増していて,かつパス情報が下にずらずらと列挙されていますね.

$ strings main | grep go1[.]
stack=[cgocheckgo1.10.4runnableruntime.scavengeunknown( (forced) -> node= blocked= defersc= in use)
/tmp/test/bin/go1.10.4
...
Go cmd/compile go1.10.4

1.10.4です.1.11と比較してスッキリとした風味.パス情報が列挙されており,かつその下にはGo cmd/compile go1.10.4という非常にわかりやすい文字列が入っています.親切!

$ strings main | grep go1[.]
value=cs     float32float64fs     gctracego1.9.7gs     invalidpanic: r10    r11    r12    r13    r14    r15    r8     r9     rax    rbp    rbx    rcx    rdi    rdx    rflags rip    rsi    rsp    runningsyscalluintptrunknownwaiting goal
/Users/moznion/tmp/test/bin/go1.9.7/src/fmt/scan.go
...
Go cmd/compile go1.9.7

1.9.7も1.10.4と似ていますね.Go cmd/compileが付いていて良い時代.

$ strings main | grep go1[.]
value=cs     float32float64fs     gctracego1.8.7gs     invalidpanic: r10    r11    r12    r13    r14    r15    r8     r9     rax    rbp    rbx    rcx    rdi    rdx    reflectrflags rip    rsi    rsp    runningruntimesyscalluintptrunknownwaiting goal
/Users/moznion/tmp/test/bin/go1.8.7/src/fmt/doc.go
...

1.8.7のようです.Go cmd/compileが無いですね.つまりこのマーカーは1.9から1.10.xまでの短い命だったのですね……


というわけでサクッと取れて便利でした.

Released lua-benchmarker

I've released lua-benchmarker that is a benchmarker for lua code. This benchmarker is for taking microbenchmark.

github.com

This library is available on luarocks. You can install this by luarocks install benchmarker.

luarocks.org

For example, write a benchmark code like following:

local benchmarker = require 'benchmarker'

benchmarker.new({
    ["insert append"] = function ()
        local arr = {}
        for i=1,1000 do
            table.insert(arr, i)
        end
    end,
    ["index append"] = function ()
        local arr = {}
        for i=1,1000 do
            arr[i] = i
        end
    end,
}):warmup(10):run_within_time(1):timethese():cmpthese()

then execute this code, the result is the following:

starting warming up: 10

Score:

index append:  1 wallclock secs ( 1.05 usr +  0.00 sys =  1.05 CPU) @ 51124.51/s (n=53695)
insert append:  1 wallclock secs ( 1.06 usr +  0.00 sys =  1.06 CPU) @ 9185.70/s (n=9760)

Comparison chart:

                    Rate  index append  insert append
   index append  51125/s            --           457%
  insert append   9186/s          -82%             --

Yes, this output format is taken from Benchmark.pm. This benchmarker is inspired by Benchmark.pm and tokuhirom/nanobench.

NOTE: This utility supports multi-platform (i.e. Windows is also supported!!).

Enjoy.

Elasticsearchに実際に書き込むテストなんかで「indexへの反映に時間がかかって困る〜〜」って時

Elasticsearchに実際に書き込む系のテストをするとき,例えば「fixtureを作ってindexしてクエリ打ってちゃんと結果が取れるかどうか」みたいなことをやると思うんですが,fixtureをindexする際にそのデータがindexに反映されるまでにrefresh_intervalぶんの時間がかかってつらい!!! というときにどうするかという話です.

Plan A. refresh_interval をめっちゃ短くする

要はrefresh_intervalがめっちゃ短ければ解決する(こともある)話なのでそれをやるという方法.上記のtweetで「1000ms」とか言ってるのはあくまでデフォルト値なので,それをカスタムしてやると良いという話です.

{
  "settings": {
    ...
    "refresh_interval": "1ms"
  }
}

などとしてやればindexへのdataの反映周期が1msecになるのでまあ充分でしょう,という割り切った感じです.テスト用途であればほぼ多くの場合はこれでも動作すると思います.コード自体に手を入れる必要がないのは良さそう.
が,間違えてもproduction環境に入れてはならない設定なのでindex templateを別個に管理する必要が出てくるでしょう.

Plan B. index refresh APIを叩く

www.elastic.co

fixtureを投入してから明示的にindex refresh APIを叩くという方法.叩くとデータが即座に近くindexに反映されるようになるので用途としてはマッチすると思います.これであればproductionのindex templateをそのまま流用できるので便利ですが,まいどまいどrefresh APIを叩くコードを挟まなければならないので,そこは何らかのtest utilityみたいなものでカバーするなどといった工夫が必要そうです.


Plan C. ?refreshをクエリに付ける

www.elastic.co

更新系のクエリを発行する際に?refreshというクエリパラメータをつけると,こちらも即座に近くindexにデータが反映されるようです.お手軽!




他にもあるかもですが,用途と場合に応じたものを使うと良いと思います.

テストが速くなってよかった!