direnvを使ってgithubのアカウントを複数設定する
はじめに
仕事のGithubアカウントと個人のGithubアカウントが別のため、direnvを使用してディレクトリごとに別のアカウントを使用できるようにしました。
direnvとは
direnvとはディレクトリごとに.envrc
を設定することによりディレクトリごとに環境を変更することができます。Githubのsshだけでなく色々な用途に使えます。
direnvのインストール
ドキュメントに従って以下を実行します。
$ git clone https://github.com/direnv/direnv
$ cd direnv
$ make install
私の環境では最新版のv2.14.0
がうまく動かなかったため、v2.7.0
をインストールしました。
$ git clone https://github.com/direnv/direnv
$ cd direnv
$ git checkout v2.7.0
$ make install
.envrc
の設定
まずそれぞれのアカウント用に別のssh keyを用意します、ここでは仮に~/.ssh/id_rsa_github1
, ~/.ssh/id_rsa_github2
とします。
direnvではディレクトリ配下全ての設定を書き換えることができるので、作業ディレクトリをそれぞれ別に~/work
, ~/personal
とします。
次にそれぞれのディレクトリにssh keyを設定します。
GIT_SSH_COMMANDの環境を設定することで、gitコマンドを入力した際に自動的にこのコマンドが呼ばれるため.envrc
に以下のコードを追加します。
# ~/work/.envrc export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_github1"
# ~/personal/.envrc export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_github2"
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
のエラーが出た際には
$ direnv allow
を実行すると有効にすることができます。
まとめ
複数のGithubアカウントを扱うことができるようになりましたが、芝が分散してしまうのでとても悲しいです。個人のアカウントが使える時代が来ることを祈っております。
リンク
Async HooksとAsync Resourcesの導入
概要
この記事はNode.js Advent Calendar 2017 15日目の記事です。
カナダから非同期で失礼します。
Async hooksとはNode.jsの非同期イベントをトレースすることができるネイティブライブラリです。まだ試験段階なのでAPIが変わる可能性がありますが、Async Hooksに入門したので、Async HooksとAsync Resouresの使い方、実際の使用例の考察をまとめました。
Node.jsのバージョンはv9.3.0を使用しています。
Async Hooksの基本的な使い方
まずは下記のコードを見てみます。このコードはAsync Resourcesが1秒ごとに生成され、破棄される様子を見ることができます。
const fs = require('fs'); const util = require('util'); const asyncHooks = require('async_hooks'); const hooks = { // Resourceが生成されるときに呼ばれる init(asyncId, type, triggerAsyncId, resource) { const obj = { asyncId, type, triggerAsyncId, resource }; fs.writeSync(1, `init\n${util.format(obj)}\n`); }, // Resourceのcallbackが呼ばれる直前に呼ばれる before(asyncId) { fs.writeSync(1, `before\t${util.format({ asyncId })}\n`); }, // Resourceのcallbackが終了したときに呼ばれる after(asyncId) { fs.writeSync(1, `after\t${util.format({ asyncId })}\n`); }, // Resourceが破棄されるときに呼ばれる destroy(asyncId) { fs.writeSync(1, `destroy\t${util.format({ asyncId })}\n`); }, // Promiseのresolveが呼ばれるタイミングで呼ばれる promiseResolve(asyncId) { fs.writeSync(1, `promiseResolve\t${util.format({ asyncId })}\n`); } }; // Hookの生成 const asyncHook = asyncHooks.createHook(hooks); // Hookを有効にする asyncHook.enable(); // 1秒おきにResourceの生成 (function start() { // 実行中のasyncId const eid = asyncHooks.executionAsyncId(); // 呼び出し元のasyncId const tid = asyncHooks.triggerAsyncId(); console.log(`start\teid: ${eid} tid: ${tid}`); setTimeout(start, 1000); })(); // 10秒後にhookを無効にする setTimeout(() => asyncHook.disable(), 10000);
createHook
に任意の関数を渡すことで、非同期イベントの検知が簡単にできるようになります。特別難しい機能は無いので、それぞれの用語については下記の表にまとめました。
async_hooks
用語 | 型 | 説明 |
---|---|---|
createHook | Function | Hookの作成 |
executionAsyncId | Function | 実行中のasyncIdの取得 |
triggerAsyncId | Function | 呼び出し元のasyncIdの取得 |
createHook
用語 | 型 | 説明 |
---|---|---|
init | Function | Resoureが生成される時に呼ばれる |
before | Function | Resourceがcallbackを呼ぶ直前で呼ばれる |
after | Function | Resoureがcallbackを呼び終えた直後に呼ばれる |
destroy | Function | Resoureが破棄される時に呼ばれる |
promiseResolve | Function | PromiseのResourceでresolve が呼ばれる時に呼ばれる |
asyncId | number | Async Resourceに割り当てられたid |
triggetAsyncId | number | 呼び出し元のasyncId |
enable | Function | Hookの有効化 |
disable | Function | Hookの無効化 |
⚠使用上の注意⚠
createHook
の関数内でエラーが発生した際はuncaughtException
でキャッチすることはできず、スタックトレースを残してプロセスが終了されます。
またconsole.log
, process.stdout.write
は非同期オペレーションなためAsync Resourceが生成されます。Hook内の各関数ではfs.writeSync
を使用してください。
スタックトレースを遡る
何か面白いことできないかなぁと探っていたところ、興味深いコードを見つけたので、これをベースにAsync Resouresの誕生から終焉までの軌跡を表示するようにしてみました。 実際のログはこちらです。コードは以下のとおりです。
const delay = util.promisify(setTimeout); const DELAY = 1000; const map = new Map(); function init(asyncId, type, triggerAsyncId, resource) { const obj = { asyncId, type, triggerAsyncId, resource }; Error.captureStackTrace(obj, init); map.set(asyncId, [obj.stack, triggerAsyncId]); fs.writeSync(1, `init\n${util.format(obj)}\n`); } function destroy(asyncId) { fs.writeSync(1, `destroy\tasyncId: ${asyncId}\n`); showStackTrace(asyncId); map.delete(asyncId); } function showStackTrace(asyncId) { const array = map.get(asyncId); if (!array) { return; } const [stack, tid] = array; fs.writeSync(1, `stack: \tasyncId: ${asyncId} triggetAsyncId: ${tid}\n${stack.replace(/(.*)\n/, '')}\n`); showStackTrace(tid); } asyncHooks.createHook({ init, destroy }).enable(); let promise = delay(DELAY); for (let i = 0; i < 10; i++) { promise = promise.then(() => delay(DELAY)); }
スタックトレースを遡ることができるので、デバッグに使えるかもしれません。
メモリリークの検知
以下のコードはAsync Resousesが生成されたものの、リソースが破棄されない例です。map
のサイズが次第に大きくなっていくことが一目でわかるので、メモリリークの検知の早期発見に使えるかもしれません。
const map = new Map(); function init(asyncId) { map.set(asyncId, 1); fs.writeSync(1, `init\tasyncId: ${asyncId} mapSize: ${map.size}\n`); } function destroy(asyncId) { map.delete(asyncId); fs.writeSync(1, `destroy\tasyncId: ${asyncId} mapSize: ${map.size}\n`); } asyncHooks.createHook({ init, destroy }).enable(); const queue = []; (function start() { new Promise(resolve => queue.push(resolve)); setTimeout(start, 100); })();
Async Resourcesの基本的な使い方
AsyncResource
クラスを使用することで、任意の非同期イベントを作成することが可能です。emitBefore
とemitAfter
はセットなので発火する際には必ず両方を呼ぶ必要があります。
const { AsyncResource } = require('async_hooks'); // 自動的にinitが呼ばれる const asyncResource = new AsyncResource('Async'); // Callbackを呼ぶ直前に呼ぶ asyncResource.emitBefore(); // Callbackが終了した直後に呼ぶ asyncResource.emitAfter(); // AsyncResourceが破棄される時に呼ぶ asyncResource.emitDestroy(); // AsyncResourceのインスタンスに割り当てられたasyncIdを返す asyncResource.asyncId(); // 呼び出し元のasyncIdを返す asyncResource.triggerAsyncId();
Async Resourcesの導入
こちらのBluebird
のPRによると、メモリリークの問題も解決し安定してきたようなので、実際にAigle
のライブラリに適用してみようと思います。コミットはこちら。
まず、Nodeのバージョンがv9.3.0以上推奨とのことでv9.3.0以上のみを有効にしました。
const node = typeof process !== 'undefined' && process.toString() === '[object process]'; const supportAsyncHook = node && (() => { const support = '9.3.0'; const [smajor, sminor] = support.match(/\d+/g); const version = process.versions.node; const [vmajor, vminor] = version.match(/\d+/g); return vmajor > smajor || vmajor === smajor && vminor >= sminor; })();
次にconstructor
でAsyncResource
を生成します。
class Aigle { constructor(executor) { ... this._resource = supportAsyncHook && new AsyncResource('PROMISE'); ... } }
今回はPromiseライブラリなので、Native Promiseと同じtypeを使用しました。
これでAigle
インスタンスが生成されるときにinit
が呼ばれるようになります。
次に、onFulfilled
またはonRejected
が呼ばれる直前にemitBefore
を、直後にemitAfter
を追加します。
簡単な方法としては、
const original = onFulfilled; onFulfilled = value => { this._resource.emitBefore(); try { return original(value); } catch (e) { throw e; } finally { this._resource.emitBefore(); } };
以上のように追加するのが簡単ですが、パフォーマンスを考慮して別の関数にしました。callbackが呼ばれる直前・直後に追加すれば問題ありません。
AsyncResource
の問題点としてパフォーマンスが著しく低下するため、デフォルトではAsync Resourcesを無効にしています。有効にする方法は、
Aigle.config({ asyncResource: true });
で使用することができます。
パフォーマンスについては後日調査してまた記事を書こうと思います。
まとめ
Async Hooksを使って非同期イベントのトレースを簡単にすることができるようになりました、デバッグにとても便利そうなライブラリです。
またAsync Resourcesを使うことでカスタムな非同期イベントを生成できますが、パフォーマンスに難がありそうです。Immediate
イベントは自動的に発行されるので特別Async Resourcesを使う必要はないのかなぁとも思いましたが、あまり詳しくないのでわかりません。もし詳しい方いたらぜひシェアしていただけたらうれしいです。
今後も最新の情報収集とパフォーマンスの調査を行っていきたいと思います。
ソース
Sidekiqのベストプラクティス
概要
Sidekiqのベストプラクティスに沿って、メッセージ機能を実装していきます。
0. Sidekiqとは
Sidekiqとはbackgroundでタスクを処理してくれるライブラリです。日付指定でセットして実行したりもできます、便利そうです。
ベストプラクティスに沿ってWorker用のテーブルを用意します。
# messages class CreateMessages < ActiveRecord::Migration[5.1] def change create_table :messages do |t| t.integer "sender_id" t.integer "recipient_id" t.string "body" t.timestamps end end end
# message_tasks class CreateMessageTasks < ActiveRecord::Migration[5.1] def change create_table :message_tasks do |t| t.integer "sender_id" t.integer "recipient_id" t.string "body" t.timestamps end end end
今回はミニマムのデモのため構造は全く同じですが、実際は送信するユーザが複数だったりプッシュ通知をしたりするかもしれません。
1. ジョブのパラメータを小さく・シンプルに
上記のMessageTasks
のテーブルを用意することで、引数をtask_id
のみにすることができます。
class MessageWorker include Sidekiq::Worker def perform(task_id) end end
また呼び出し側では以下のようになります。
task = MessageTask.create(sender_id: sender_id, recipient_id: recipient_id, body: body) MessageWorker.perform_async(task.id)
2. 冪等性とトランザクション
perform
内でエラーが発生した場合、自動リトライ機能が働きます。デフォルトでは25回のリトライ後(おおよそ21日後)そのジョブは削除されマニュアルで対応しないといけません。
それを防ぐためにもMessageTasks
とトランザクションを使用することで、より安全にデータを扱うことができます。
class MessageWorker include Sidekiq::Worker def perform(task_id) task = MessageTask.find(task_id) if task.nil? return end ActiveRecord::Base.transaction do Message.create(sender_id: task.sender_id, recipient_id: task.recipient_id, body: task.body) task.destroy end end end
3. Concurrencyの設定
Concurrencyを設定することによりsidekiqの処理を並列化することが可能です。デフォルト値は25スレッドで推奨値は50未満。100以上設定した場合、安定性の問題があるとの。
config/database.yml
を変更することで変えることができます。この値は可変ではないので、用途に応じて変更する必要があるようです。
production: adapter: mysql2 database: foo_production pool: 25
まとめ
Sidekiqを使うことで同期処理が必要がないタスクは非同期で処理させることができます。同期処理させる必要ないものは非同期にしてレスポンス速度を改善できそうですね。
リンク
ESLintのメソッドチェインのindentの設定について
概要
ESLint
のメソッドチェイン(chain)のindentの設定に問題があり3 -> 4のアップグレードができなかったのですが、なんとか苦戦してなんとかアップグレードした話です。
もっといい方法知っている方いたら教えて欲しいです、ホントに。
問題のコード
例としてarrow functionのショートハンドなどは使用していません。実際のコードはもっと大きいものを想定してください。
さて。
元々のESLint
の設定はこうでした。
rules: indent: - error - 2
Array編
最初のchainのfunctionに複数行にまたがるArrayを渡す際、以下のコードはLintエラーになります。
// 1 (Lintエラー) Promise.all([ 1, 2, 3 ]) .then(value => { console.log(value); });
解決方法としては、
// 2 Promise.all([ 1, 2, 3 ]) .then(value => { console.log(value); }); // 3 Promise .all([ 1, 2, 3 ]) .then(value => { console.log(value); }); // 4 (Lintエラー) Promise.all([ 1, 2, 3 ]) .then(value => { console.log(value); });
2は気持ち悪すぎるので不採用として(Lintエラーにして欲しいレベル)、4でLintエラーにするのであれば、もはや1を採用して欲しいです。 3はアリと考えていましたが、以下の例でとてもいただけないので不採用としました。
// 5 (気持ち悪い) _ .chain([ 1, 2, 3 }) .map(n => { return n * 2; }) .value(); // 6 (短いときはこう書きたい) Promise.all([1, 2, 3]) .then(value => { console.log(value); });
Function編
Functionのほうがもっと厄介です。
// 7 (Lintエラー) new Promise(resolve => { // do something setTimeout(resolve); }) .then(value => { console.log(value); }); // 8 (Lintエラー) [1, 2, 3].map(n => { return n * 2; }) .filter(n => { return n % 2; });
解決方法はArrayの例と同じく、
// 9 new Promise(resolve => { // do something setTimeout(resolve); }) .then(value => { console.log(value); }); // 10 [1, 2, 3].map(n => { return n * 2; }) .filter(n => { return n % 2; });
一つの案としては変数を定義することですが、Functionの例で毎度変数を作らないといけなくなるため不採用にしました。
解決方法
IndentにMemberExpressionというオプションがあり、chain時のindentを定義できます。
上記の例を実現したいとき、chainのindentが0だったり2だったりするため、off
にすることで回避することができました。
rules: indent: - error - 2 - MemberExpression: off
まとめ
ESLintのバージョンをアップデートすることで、多くの機能が追加されているのでもっと厳しくチェックしていただけるようになりました。助かります、ありがとうございます。
がしかし、果たしてこれは良いコードなのか…という疑問も残ります。
例えば、
// 11 const obj = {}; const array = _.chain([ 1, 2, 3, ]).map(n => { return n * 2; }) .filter(n => { return n % 2; }); obj.a = 1;
この例の場合、arrayのchainがどこで終わりかわかりづらいため、その後に続く行が読みづらい可能性が高いです。
// 12 const obj = {}; const array = _ .chain([ 1, 2, 3 ]) .map(n => { return n * 2; }) .filter(n => { return n % 2; }); obj.a = 1;
そういう意味ではこう書いたほうが読みやすい可能性も…
リーダブルコードって難しいですね。
リンク
Perforce + Vim
概要
PerforceをVimで仲良くやっていくためのセットアップを考え中なのですが、特に良いライブラリもなく、はたまた需要もそこまで大きくなく… いったん作業ができるようvim-perforceとvimdiffのセットアップをしました。
vim-perforce
ドキュメントによるとP4info
, P4edit
, P4revert
と P4movetocl
だけサポートしてるそうです。
とVim
上で手動でチェックアウトする必要があります。WebStormを使っていると自動チェックアウトができるそうです。
p4 delete
が一番の曲者なのでサポートして欲しいところではあります。
vimdiff
vimdiffのセットアップは一行足すだけでp4 diff
がvimに置き換わります。
$ export P4DIFF=vimdiff
これはとても見やすく便利です。
まとめ
vim-perforceを改良したいところではありますが、いったんこの2つさえあれば作業は捗るかと思います。
もう少し効率よくするために、ローカル環境ではgitを用意して、gitの情報を元にPerforceのコマンドを自動化しています(謎)。 Perforceはあまりブランチを作成したり複数のことを同時にしたりすることが難しいので、gitでできることはgitに移行したいところです。
リンク
Perforceのセットアップ for Mac
はじめに
PerforceはSubversionのようなバージョン管理システムでbinaryの管理に向いているそうです。今回はp4v
というツールとp4
というコマンドラインツールを導入したのでそのセットアップ方法について書きます。
p4v
のセットアップ
設定はこちらのリンク に沿ってやるだけですが、管理者からServerのアドレスとuser名をいただき入力するだけです。
以上で設定は完了です。
p4v
の使い方
ログイン後、始めに差分を取得するためルートディレクトリで右クリックを押しGet Latest Revision
を取得します。
全てのファイルはRead Onlyになっており編集権限がありません。もし編集したい場合はファイルを右クリックでCheck out
する必要があります。チェックアウトをすると編集が可能になり、その情報は他のユーザにシェアがされます。編集後、右クリックを押しsubmitすることで他のユーザに変更がシェアされます。
もし差分を見たい場合は、右クリックでDiff Against Have Revision
を押すとp4vdiff
が起動し差分を見ることが可能です。
p4
のセットアップ
p4v
だとマウス操作が多いため、やはりCLIでどうにかしたいところです。そのためp4
をインストールします。リンクはこちら。
次にパスなどの情報をセットします。
私は.zshrc
をGithub上で管理しているため、secretな情報を隠すために別ファイルを用意しました。
# ~/.secret.sh export P4PATH=<server-url> export P4USER=<user-name> export P4CLIENT=<workspace-name> p4 login
# ~/.zshrc if [ -f $HOME/.secret.sh ]; then source $HOME/.secret.sh fi
上記の.secret.sh
を.zshrc
で読み込み、terminal起動時にp4
にログインをしています。これで準備は完了です。
編集したいときは、p4 edit
を使用することでチェックアウトが可能になります。また他にもいくつかコマンドがあるので試してください。
まとめ
私はgit
ばかり触っているゆとりですが、少しずつ慣れていこうかなと思っています。
次回はp4
+ vim
あたりについて書こうと思います。
リンク
Ruby on Railsのセットアップ
はじめに
Rubyデビューしたので初心に戻ってその勉強記録を記して行きたいと思います。
手順は
- rbenv (バージョン管理システム)
- nokogiriのインストール
- Railsのインストール
- アプリの起動
です。
rbenvのインストール
rbenvは最も有名なバージョン管理ツールらしいので、流行りに乗ってインストールしたいと思います。
…のつもりが、長いことmacを使っていると自ずとrbenvがインストールされていることもあるようで、gem installが通らなかったりしたのでuninstallから始めます。
手順はこちらです。
uninstall? · Issue #148 · rbenv/rbenv · GitHub
$ rm -rf ~/.rbenv $ brew uninstall rbenv
と、.zshrc
からrbenv
なる記述を削除しました。
それでは気を取り直してインストールします。
$ brew install rbenv
後は.zshrc
に追記します。
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshrc $ echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.zshrc
Rubyのセットアップ
次にrubyをインストールします。
$ rbenv install 2.4.1
最新版をインストールしたい方はrbenv install -l
で最新版をチェックしてください。
rbenv rehash rbenv global 2.4.1
以上で完了です。
nokogiriのインストール
ここで少しハマったのがmacではnokogiriのインストールをmacのインストレーションに沿ってやらないといけないようなので、別途インストールします。
$ gem update --system $ xcode-select --install $ gem install nokogiri
これはmac OS Xというところに記載されています。
Railsのインストール
$ gem install rails
コマンドはこちらだけですね、とても簡単。
アプリの起動
適当なディレクトリに行き、以下のコマンドを叩きます。 チュートリアルに沿って、
$ rails new hello_app
$ cd hello_app
$ rails server
これでhttp://localhost:3000/にアクセスすると以下の画面が表示されます。
まとめ
とても簡単にRailsサーバが立ち上がりました。
Yay! I'm on Rails!
はい。せっかくなので何かサーバ建てたいですね。