App Engineのgo111用ディレクトリ構成
App Engine Standard EnvironmentでGo1.9を使う時は以下のように、リポジトリのルートをGOPATH
として設定し、appcfg.py
を使ってデプロイをしていました。
. ├── app.yaml └── src └── app ├── glide.lock ├── glide.yaml ├── app.go ├── handler ├── ... └── vendor
- app.go
package app import ( "net/http" "app/handler" ) func init() { http.Handle("/", handler.New()) }
これがgo111のランタイムになると、main
関数が必須になり、デプロイコマンドもgcloud app deploy
に変わります。*1
そのため、以下の条件を満たしつつ、どのようなディレクトリ構成にするのが良いのか考えてみました。
app.yaml
のパスは変更しないGOPATH
をリポジトリごとに変更しなくても良いようにするsrc/app
はpkg
に変更app.go
はpkg.go
に変更
- 依存関係の管理に
go.mod
を使う- リポジトリのルートに配置
1) ./main.go
を作成する
. ├── app.yaml ├── go.mod ├── go.sum ├── main.go └── pkg ├── handler ├── ... └── pkg.go
2) ./cmd/app/main.go
を作成し、app.yaml
のmainでパスを指定する
. ├── app.yaml ├── cmd │ └── app │ └── main.go ├── go.mod ├── go.sum └── pkg ├── handler ├── ... └── pkg.go
この時、プライベートなパッケージ*2を使っているとCloud Buildで依存関係を解決できません。
対策としては、代わりにvendor
を使う、もしくはgo.mod
のreplace
ディレクティブを使う、のどちらかです。
しかし、vendor
を使う場合にはgo.mod
を.gcloudignore
に追加し、GO111MODULE
をoff
にしないといけません。
また、go.mod
を使うにはGO111MODULE
をon
にしないといけません。
GO111MODULE
はリポジトリをGOPATH
の配下に置くか、外に置かでauto
の時の値が変わるため*3、組み合わせが非常に複雑です。
まとめると次のようになります。
依存管理 | リポジトリの場所 | GO111MODULE | デプロイ可否 |
---|---|---|---|
go.mod | GOPATH配下 | auto(=off) | offだと使用不可 |
go.mod | GOPATH配下 | on | OK |
go.mod | GOPATH外 | auto(=on) | OK |
go.mod | GOPATH外 | off | offにできない |
vendor | GOPATH配下 | auto(=off) | 1)はOK、2)はNG |
vendor | GOPATH配下 | on | NG |
vendor | GOPATH外 | auto(=on) | NG |
vendor | GOPATH外 | off | offにできない |
※確認する際にCloud Buildで使われたイメージはgcr.io/gae-runtimes/go111_app_builder:go111_20190503_1_11_9_RC00
です
正しくgo.mod
を使えば問題なくデプロイできるため、replace
対象のパッケージが管理できるのであればこちらを使うのが良さそうです。
ただ、replace
対象のパッケージはgitのsubmodules
やsubtree
で管理することになり、それが煩雑になってしまう可能性があります。
そういった場合、現段階だと1)をGOPATH
配下に置くのが無難です。
app.yaml
でmain
を指定することができませんが、今のプロジェクトでは必要なかったので以下のディレクトリ構成にすることにしました。
. ├── app.yaml ├── go.mod // デプロイしない ├── go.sum // デプロイしない ├── main.go ├── pkg │ ├── handler │ ├── ... │ └── pkg.go └── vendor // `GO111MODULE=on go mod vendor` で作成
*1:https://cloud.google.com/appengine/docs/standard/go111/go-differences 参照
*3:GOPATH配下: off、GOPATH外: on
MojaveでGDB 8.3を使う
1. インストール
brew install gdb --HEAD
2019/04/13時点でインストールされるバージョンは8.3.50。
2019/05/14以降は--HEAD
不要。
echo "set startup-with-shell off" >> ~/.gdbinit
を実行するようにと表示されるが、ファイルの場所は$HOME/.config/gdb/init
でも良い。
2. 証明書の作成
キーチェーンアクセスを起動し、メニューから
キーチェーンアクセス > 証明書アシスタント > 証明書を作成...
を選択。
以下を入力したら他はデフォルトのまま(期限は伸ばしておいても良い)ひたすら「続ける」を押していく。
名前: gdb-cert 証明書のタイプ: コード署名 ✅: デフォルトを無効化
「ログイン」に作成されたgdb-cert
を「システム」にドラッグ&ドロップし、
証明書の情報を開いて「信頼」の「コード署名」を「常に信頼」に変更。
3. コード署名
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.cs.debugger</key> <true/> </dict> </plist>
署名する。
codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb)
4. 設定を反映させる
sudo pkill taskgated
goonのクエリをチューニングした時のメモ
App Engine(Go 1.9)でDatastoreからデータを取得するのにgoonのGoon.Runを使っていたが、プロパティを追加する改修*1をした後、
latencyが遅くなり、DatastoreでDeadline exceeded
が頻繁に発生するようになってしまった。
とりあえず該当するリクエストをStackdriver Traceで見てみるとdatastore_v3.Nextが7回ほど呼ばれていることがわかった。
まずはこの呼び出し回数を減らせば速くなるだろうと思い、Query.BatchSizeを設定してみたが、呼び出し回数に変化は無く、全く効果がなかった。
それも変だなと思い、試しにデバッグログを仕込んで1回の呼び出しでどれくらいのデータを取得できているのか見てみたところ、なんと90件くらいしか取得できていなかった。
ここまで改修前の状態を確認していなかったので念のため調べてみると、datastore_v3.Next
の呼び出し回数は5回、1回あたりのデータ取得件数は約120件だった。
このことから、原因はプロパティを追加したことでデータのサイズが増え、一度に取得できる件数が減ったことによるDatastoreへのアクセス増ということがわかった。
ということで次はMemcacheを使ってDatastoreへのアクセスを減らすようにしてみた。
方法としては今までのクエリにQuery.KeysOnlyを設定してキーだけを取得するようにし、本体はGoon.GetMultiで取ってくるというシンプルなもの。
しかし、これだと1000件のデータを取得しようとするとGoon.GetMulti
がMemcacheにデータを格納する際に大量のメモリを消費してしまうため、エラーになったり、インスタンスが落ちるようになってしまった。
そこでGoon.GetMulti
の呼び出しが100件ずつになるようにしてみたところ、エラーは解消されたように見えたが、どうやらMemcacheにデータが乗っておらず、常にDatastoreにアクセスしてしまっていた。
仕方なく50件まで減らしたら安定してキャッシュが効くようになり、ようやく速度が改善されるようになった。
そもそもデータ構造がイマイチっぽいので本来はそこから手を入れるのが良さそうだが、今からそれをやるのもなかなかに厳しい状況なので当分はこれでしのぐことになりそう。
*1:後で確認したらデータサイズが80%ほど増加していた...
第1回 ゴリラ.vimが開催されました
2/18(月)にゴリラ.vim #1が開催され、スタッフ兼発表者として参加してきました。
スタッフになったのはvim-jpのslackでゴリラさんこと@gorilla0513さんがVimの勉強会をやろうとしているのを見かけ、これは前々から思っていた渋谷でVim勉強会をするチャンスなのでは!?と会場提供を申し出たのがきっかけです。
そして当初は発表するネタが思い浮かばなかったのでスタッフに専念するつもりでいたのですが、少し経っても発表者枠が空いており、かつメンバーがVimConf 2018のスピーカーばかりという状態だったため、これは自分も何か話した方が良いんじゃないかと考え、ここ最近使ったgotypeとgo-treeを基にVimと外部コマンドという発表をしてきました。
ちなみに発表・デモは@thincaさんのshowtimeとquickrunを使いました。
全体の発表内容としては、短い時間ながらも初心者向けから上級者向けまであり、とても充実したものとなっていました。
特に前半の発表は自分にもそういう時期があったんだよなぁ、と非常に懐かしい気持ちになりました。
その後、一通り発表が終わった後は懇親会となり、各々がVimトークでとても盛り上がっていました。
渋谷ヒカリエ 弊社セミナールームにて ゴリラ.Vimが開催されました。懇親会では神々によるvimrcの設定サポートなど、とても和気あいあいとした勉強会でした。開催に協力することができて大変光栄です :)#gorillavim #denatechstudio pic.twitter.com/8u6mFUjACM
— DeNA Tech (@DeNAxTech) 2019年2月18日
開催前からこのイベントは盛り上がる...という予感はありましたが、予想以上に盛り上がり、スタッフとしても発表者としても参加者としても、嬉しい限りです。
トレンド入り!!!!!
— ゴリラ (@gorilla0513) February 18, 2019
#gorillavim pic.twitter.com/UTUbpFpGXv
ということで、今回ご参加いただいた皆様、ありがとうございました。
次回も是非よろしくお願いします。
(#2は少し枠が増えています!)
Vimの折畳に対応したtreeコマンド
golang.tokyo #21のDevQuizはGoでtreeコマンドを作成するというものでした。
treeコマンドといえば、以前Vimでファイル一覧をツリー表示するためのtree.vimというプラグインを作る時に使いました。
このプラグインはディレクトリにマーカーをつけることでサブディレクトリを階層ごとに折り畳んで表示します。
当時、マーカーを(Go製の)自作コマンドでつけることも検討しましたが、実装が非常に面倒くさそうだったのでVim scriptでtreeコマンドの結果をパースすることにしました。
そんな事情があったため、せっかくなのでDevQuizをやりつつ、追加機能としてVimの折畳に対応した出力形式も実装してしまおう!と思って作ったのがgo-treeです。*1
こちらはコマンドラインフラグに-V
をつけると以下のような出力になります。
$ go-tree -V github.com/daisuzu/tree.vim github.com/daisuzu/tree.vim/../ github.com/daisuzu/tree.vim/./ github.com/daisuzu/tree.vim/README.md github.com/daisuzu/tree.vim/autoload/{{{ github.com/daisuzu/tree.vim/autoload/tree.vim}}} github.com/daisuzu/tree.vim/ftplugin/{{{ github.com/daisuzu/tree.vim/ftplugin/tree.vim}}} github.com/daisuzu/tree.vim/plugin/{{{ github.com/daisuzu/tree.vim/plugin/tree.vim}}} github.com/daisuzu/tree.vim/syntax/{{{ github.com/daisuzu/tree.vim/syntax/tree.vim}}}
コマンド自体はVimから実行しても良いし、
:r! go-tree -V [DIR]
シェルで実行した結果をVimに流し込んでもOKです。
# -cでfoldmethod(fdm)をmarkerにする go-tree -V [DIR] | vim - -R -c 'setl fdm=marker' # モードラインでfoldmethod(fdm)をmarkerにする echo -e "vim: fdm=marker\n$(go-tree -V [DIR])" | vim - -R
そうするとgithub.com/daisuzu/tree.vim
はVimで以下のように表示されます。
github.com/daisuzu/tree.vim/../ github.com/daisuzu/tree.vim/./ github.com/daisuzu/tree.vim/README.md +-- 2 lines: github.com/daisuzu/tree.vim/autoload/----------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/ftplugin/----------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/plugin/------------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/syntax/------------------------------- ~ ~ ~ ~
さらに、コマンド単体で動作するのでjob機能を使った非同期実行も簡単です。
:vnew | setl fdm=marker | call job_start('go-tree -V', {'out_io': 'buffer', 'out_buf': bufnr('%')})
このように、github.com
配下に大量のファイルがあるような場合でもVimをブロックすることなく操作を継続することができます。
現在はvimrcにAtree
コマンドを定義してgo-treeを常用しています。
filetypeをtree.vim
のものにすることで、そのまま表示するよりも少し見栄えが良くなっています。
command! -nargs=? -complete=dir -count -bang -bar ATree \ call s:async_tree(<q-args>, <count>, <bang>0, <q-mods>) function! s:async_tree(dir, depth, bang, mods) abort let cmd = 'go-tree -V ' . g:tree_options if a:bang " コマンドに!をつけたら隠しファイルも表示する let cmd .= ' -a' endif if a:depth > 0 let cmd .= ' -L ' . a:depth endif " cd後もgfできるようにディレクトリ名を絶対パスにする let cmd .= ' ' . fnamemodify(a:dir != '' ? a:dir : '.', ':p:h') execute a:mods . ' new' setfiletype tree let bufnr = bufnr('%') call job_start(cmd, { \ 'out_io': 'buffer', \ 'out_buf': bufnr, \ 'exit_cb': {channel, msg -> s:goto_first(bufnr)}, \ }) endfunction " オリジナル版はカーソル位置が先頭行になるので同じ挙動にする function! s:goto_first(bufnr) if bufnr('%') != a:bufnr " バッファを移動していたら何もしない return endif if line('.') != line('$') " 行を移動していたら何もしない return endif normal gg endfunction
予想通り実装は面倒でしたが、Vimをより快適に使えるようになりました!
*1:人が見ることはあまり考えてませんでした...
Vimのexecute()と組み合わせて便利なコマンド
この記事はVim Advent Calendar 2018の13日目の記事です。
先月に開催されたVimConf 2018で、Migrating plugins to standard featuresというタイトルで発表してコマンドの結果をバッファに表示する方法を紹介しました。
その中で解説しきれなかったテクニックとして、以下のようなコマンドを定義しておくとL <コマンド>
*1のようにして簡単に任意のコマンドの結果をバッファに表示することが出来るようになります。
command! -bar ToScratch \ setlocal buftype=nofile bufhidden=hide noswapfile command! -nargs=1 -complete=command L \ <mods> new | ToScratch | \ call setline(1, split(execute(<q-args>), '\n'))
当日はプラグイン一覧やMRUを表示する用途として、
:scriptnames
v:oldfiles
(:oldfiles
でも良い)
を紹介しましたが、簡単に使えるなら他にも便利なコマンドがあるんじゃないかと思って調べてみました。
なお、対象は:exusage
から
- 引数不要
- ファイルタイプやカーソル位置、バッファの状態などに依存しない
- 結果が複数行になる
コマンドのみとしています。(収拾ががつかなくなりそうなので...)
定義系
各種定義を一覧表示するのは何かと便利そうです。
年末にvimrcを掃除する際などにも大活躍するんじゃないでしょうか?
:augroup
... 自動コマンドのグループ:autocmd
... 自動コマンド:abbreviate
... 短縮入力:noreabbrev
や、先頭にc
,i
を付けても良い
:command
... コマンド:function
... 関数:highlight
... ハイライト:runtime syntax/hitest.vim
だと実際にハイライトされる
:let
... 変数:map
... マップ:noremap
や、先頭にc
,i
,l
,n
,o
,s
,t
,v
,x
を付けても良い
:menu
... メニュー:noremenu
や、先頭にa
,c
,i
,n
,o
,s
,tl
,v
,x
を付けても良い
開いているもの
gf
で開く用途には向きませんが、どちらも大量に開いている時はバッファに表示されると便利そうです。
:buffers
... バッファ- または
:files
,:ls
- または
:tabs
... タブ
各種操作結果
こちらも大量に表示されるような状況では便利そうです。
:registers
... レジスタ- または
:display
- または
:marks
... マーク:messages
... メッセージ:history
... コマンド履歴
Vimの情報
Vim関連の様々な情報はバッファに表示して見たいというケースはあまりないかもしれません。
個人的には代替の手段の方が使い勝手が良いと思っています。
:compiler
... コンパイラ用設定:digraphs
... ダイグラフ:h digraph-table
の方が見やすいかもしれない
:set
... 既定値と異なるオプション:setglobal
や:setlocal
もある:options
でも良いかもしれない
:version
... バージョン番号やその他の情報
デバッグ向け
Vim script開発者向け。
:breaklist
... ブレークポイント
QuickFix
切り替える際に表示するだけならわざわざバッファに表示する必要はないかもしれません。
:chistory
... grepやmakeなど、quickfixリストの履歴- locationリストの場合は
:lhistory
- locationリストの場合は
:clist
... quickfixリストの内容- locationリストの場合は
:llist
- locationリストの場合は
移動系
こちらも一時的に表示するだけならバッファに表示する必要はなさそうです。
※実際に移動できるマッピングなどがあれば...*2
:jumps
... ジャンプリスト:tags
... タグスタック:tselect
... タグ
その他
ということで最後はこちら。
:smile
... 「ついに見つけたんだね、セイバー!」
oooo$$$$$$$$$$$$oooo oo$$$$$$$$$$$$$$$$$$$$$$$$o oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o o$ $$ o$ o $ oo o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o $$ $$ $$o$ oo $ $ "$ o$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$o $$$o$$o$ "$$$$$$o$ o$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$o $$$$$$$$ $$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$$$$$$ """$$$ "$$$""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$ $$$ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$o o$$" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$o $$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o o$$$oooo$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o$$$$$$$$$$$$$$$$$ $$$$$$$$"$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$"""""""" """" $$$$ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o$$$ "$$$o """$$$$$$$$$$$$$$$$$$"$$" $$$ $$$o "$$""$$$$$$"""" o$$$ $$$$o o$$$" "$$$$o o$$$$$$o"$$$$o o$$$$ "$$$$$oo ""$$$$o$$$$$o o$$$$"" ""$$$$$oooo "$$$o$$$$$$$$$""" ""$$$$$$$oo $$$$$$$$$$ """"$$$$$$$$$$$ $$$$$$$$$$$$ $$$$$$$$$$" "$$$""""
golang.tokyo 静的解析Dayでgolang.org/x/tools/go/analysisを使ってみた
golang.org/x/tools/go/analysis 面白そうだなー。 けど触る時間がなかなか取れないなー。 と思っていたらちょうどタイムリーなツイートがあり、
12/8とかにGoで静的解析を一緒にモクモクやるかいをどこかでやりたいけど、会場がないので、一緒にモクモクしてくれる人とどこか会場を探してます。
— tenntennʕ ◔ϖ◔ʔ ==Go@Goが生きてる (@tenntenn) November 27, 2018
数時間後にこちらの開催が決まりました。
ということでモクモクとgscでhonnef.co/go/tools/lintからgolang.org/x/tools/go/analysis
への置き換えをやってきました。
ちなみにgsc
はgolang.tokyo #14のLT*1で紹介した自作のlinterです。
変更点をざっくりまとめると、
honnef.co/go/tools/lint
ではCheckerというinterfaceに実装をするが、golang.org/x/tools/go/analysis
ではAnalyzerというstructになるhonnef.co/go/tools/lint
ではJobを受け取ってast.Inspectで処理をしていくが、golang.org/x/tools/go/analysis
ではPassを受け取ってInspector.Preorderで処理をしていくAnalyzer
のRequires
フィールドに必要な前処理を指定する- 次の処理に結果を渡すような作りにすることも可能
- コマンド本体はlintutil. ProcessFlagSetからmultichecker.Mainになる
- ただしmainでflagを扱えなくなってしまう!
- ツール全体でExitCodeを0にするといった制御が不可に...
- なので
Pass
のReport
フィールド(関数)を書き換えることで 無理矢理 実現した
- ただしmainでflagを扱えなくなってしまう!
- テストはhonnef.co/go/tools/lint/testutilがgolang.org/x/tools/go/analysis/analysistestになる
- 使い方はだいたい同じ
といったところです。
統一的なインターフェースになって再利用しやすくなるのもメリットですが、処理速度も向上していて、 手元で3秒くらいかかっていたのが一瞬で終わるようになりました。
今日はまだgolang.org/x/tools/go/analysis
を使いこなすところまではいけませんでしたが、とても有意義な時間を過ごせました。
便利なAnalyzer
が増えるとみんな幸せになれるのでもっともっと使う人が増えていくと良いですね!
awesome analyzerを作るという気持ちを示した #golangtokyo
— tenntennʕ ◔ϖ◔ʔ ==Go@Goが生きてる (@tenntenn) December 8, 2018