astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

複数ブランチの同時並行作業にはGit Worktreeを

猫より犬好きプログラマのにゃんです。主な仕事はマサカリの投擲とベランダに来る野良猫の餌やりです。よろしくお願いします。

皆さんバージョン管理システムを使ってますか?今では多くの現場でGitを使っていると思います。ゲーム開発など巨大なバイナリのアセットが多い現場ではGitではなくプロプライエタリのPerforceを使用されているかもしれません。他にもGitと同時期に開発されたMercurialというツールもありますが、いずれにせよ現在のdevシーンではバージョン管理システムはあって当たり前のツールの一つになっています。

弊社ではGitを使っています。Gitは機能豊富ですがその分知られていない機能も多く、ネットを見渡せば色々な記事が転がっていますが、今回は私も一つGitの便利機能を紹介したいと思います。

f:id:astamuse:20200617105902p:plain

worktreeで複数の作業ディレクトリを作る

複数のブランチを同時並行で修正したくなったらどうしてますか?都度stashしてcheckout(switch)しますか?いっそ新たにcloneしちゃいますか?

それでもいいのですが、Gitは1つのリポジトリに対して複数のワークツリー(作業ディレクトリ)を作成することができるのです。

ワークツリーの作成

git worktree add <チェックアウトするディレクトリ> [-b] <チェックアウトするブランチ名>

-bは新しいブランチを作成します。checkout -b のbと同じですね。

試しにローカルの適当なリポジトリで、new_workdirディレクトリに新しいワークツリーを作ってfeature/testブランチをチェックアウトしてみます

~/.dotfiles $ git worktree add new_workdir -b feature/test
Preparing worktree (checking out 'feature/test')
HEAD is now at dd92ad4 add https url support

作成したワークツリーに行ってみましょう。feature/testブランチがチェックアウトされています。

~/.dotfiles $ cd new_workdir

~/.dotfiles/new_workdir $ git status
On branch feature/test
nothing to commit, working tree clean

今回は既存のワークツリー内部に新しいワークツリーを作成しましたが、新しいワークツリーは自動的に既存ワークツリーのコミット対象外になるので安心して作成してください。もちろん既存ワークツリーの外に作成することもできます。ファイルの変更をwatchする機能を使っている場合などは外に作った方がいいかもしれません。

なお、複数のワークツリーで同じブランチをチェックアウトすることはできません

~/.dotfiles/new_workdir $ git switch master
fatal: 'master' is already checked out at '/Users/nyan-wang/.dotfiles'

これは地味に役立つ制限で、2カ所にcloneして作業した場合、2カ所で同じブランチを弄ってしまうような悲劇が発生しますが、worktreeではそんな事故を防げます。worktreeを知る前には2カ所で同じブランチを修正して泣く泣く手動マージとか地味な作業をしたこともありました。

ワークツリーの一覧

作成したワークツリーは git worktree list で確認できます

~/.dotfiles/new_workdir $ git worktree list
/Users/nyan-wang/.dotfiles              dd92ad4 [master]
/Users/nyan-wang/.dotfiles/new_workdir  dd92ad4 [feature/test]

ワークツリーの削除

ワークツリーが不要になったらgit worktree removeで削除できます。

git worktree remove new_workdir

実はrmコマンドでワークツリーを消してしまっても問題ありません。あくまで同一のリポジトリの別ワークツリーなのでコミットした変更は全て残っています。ただし、ワークツリーの管理情報が残っておりworktree listすると消したワークツリーが表示されてしまいます。そんなときは git worktree prune で消したワークツリーの管理情報を掃除できます。

特に既存ワークツリーの外に作るとうっかりrm -rfで消してしまいがちですが、消してしまっても安心です。

worktreeのcloneに対する利点

worktreeを使った場合cloneに対して以下の利点があります

  1. git hook設定やuser.nameなどconfigのカスタマイズが共有されている
  2. 作業完了後にリモートにpushせず削除してしまっても、コミットは残っているので安全安心
  3. 二箇所で同じブランチを弄ってしまう失敗を防げる

それでは皆さま、よきGitライフをお過ごしください〜

続・NGINX Unitを試してみた話

こんにちは。開発部のtorigakiです。 今回はNGINX Unitを簡単に検証した話をさせていただければと思います。

タイトルに「続」がついている理由ですが、実は2年前に社内勉強会の発表ネタ(当時の発表資料はこちら)としてNGINX Unitを検証してみたことがありまして、当時のバージョンではNode.jsとJavaが未対応だったのですが、最新バージョンでは対応済みになっていたのでブログネタとして再び検証してみようと思った次第です。

NGINX Unitとは?

要約すると以下になります。

  • NGINX社製 軽量アプリケーションサーバ。
  • RESTful APIやJSON経由でサービス無停止でリアルタイムに設定変更がきる。
  • 1つのサーバに複数の言語の実行系や同一言語の複数バージョンの実行系を搭載できる。

インストール方法

今回は「Ubuntu 18.04.2 LTS」にインストールしてみました。

インストール方法は公式サイト通りにやってみました。

  1. 以下のコマンドを実行します。
# sudo curl -sL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
  1. 以下内容の「/etc/apt/sources.list.d/unit.list」を作成します。
deb https://packages.nginx.org/unit/ubuntu/ cosmic unit
deb-src https://packages.nginx.org/unit/ubuntu/ cosmic unit
  1. 以下コマンドを実行します。
# apt update
# apt install unit
# apt install unit-dev unit-go1.10 unit-jsc8 unit-jsc11 unit-perl \
      unit-php unit-python2.7 unit-python3.6 unit-python3.7 unit-ruby

起動・停止方法

以下コマンドで起動・停止できます。

# systemctl start unit
# systemctl stop unit

サーバー設定

NGINX UnitはJSON形式のファイルに設定内容を記述し、curlコマンドで反映させます。

以下はPHPの設定を反映させる場合の例です。

1.以下内容のPHPファイルを配置します。

  • 配置場所
/var/www/php/index.php
  • index.phpの内容
<?php echo "PHP Hello, Unit!"; ?>

2.以下内容のJSONファイルを作成します。

{
  "listeners": {
      "*:8080": {
          "pass": "applications/php_app"
      }
  },
  "applications": {
      "php_app": {
          "type": "php",
          "root": "/var/www/php/"
      }
  }
}

3.以下のcurlコマンドで反映させます。

curl -X PUT -d @php.json  --unix-socket /var/run/control.unit.sock  http://localhost/config

4.反映確認

以下のcurlコマンドで確認できます。

# curl http://localhost:8080/
PHP Hello, Unit!

ベンチマークとってみた

各言語ごとに性能差がどれくらいでるのか確認してみました。 ベンチマークにはこちらのサイトを参考にApache Benchを使用しました。

検証環境(物理PC)は以下になります。

OS:Ubuntu 18.04.2 LTS
CPU:4コア
メモリ:15GB

NGINX Unitバージョン:1.18.0-1

Apache Benchで実行したコマンドは以下になります。

ab -n <総リクエスト数> -c <同時リクエスト数><URL> 

実行したコマンド
ab -n 100000 -c 100 http://localhost:8080/ 

ベンチマーク結果で今回比較対象にする値は以下になります。

  • Requests per second(1秒で処理できるリクエスト数)(数値が高い方が好ましい)
  • Time per request(1リクエストの平均処理時間)(数値が低い方が好ましい)

各言語のアプリ設定は公式サイトのサンプル通りに設定しました。

検証方法は上記abコマンドで各言語のアプリに5回アクセスし、その平均値をとってみました。

各言語ごとのアプリ内容

  • Go
package main

import (
    "io";
    "net/http";
    "unit.nginx.org/go"
)

func main() {
    http.HandleFunc("/",func (w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello, Unit!")
    })
    unit.ListenAndServe(":8080", nil)
}
  • Node.js
#!/usr/bin/env node

require("unit-http").createServer(function (req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.end("Hello, Unit!")
}).listen()
  • Java
<%@ page language="java" contentType="text/plain" %>
<%= "Hello, Unit!" %>
  • Perl
my $app = sub {
    return [
        "200",
        [ "Content-Type" => "text/plain" ],
        [ "Hello, Unit!" ],
    ];
};
  • PHP
<?php echo "Hello, Unit!"; ?>
  • Python
def application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return (b"Hello, Unit!")
  • Ruby
app = Proc.new do |env|
    ["200", {
        "Content-Type" => "text/plain",
    }, ["Hello, Unit!"]]
end

run app

ベンチマーク結果

  • Requests per second
Go Node.js Java Perl PHP Python Ruby
2009.72 1809.132 1851.91 1981.946 1998.446 1945.988 1969.374

f:id:astamuse:20200609145145p:plain

  • Time per request
Go Node.js Java Perl PHP Python Ruby
0.5024 0.5552 0.543 0.5056 0.502 0.5148 0.5106

f:id:astamuse:20200609145207p:plain

今回は、「Hello, Unit!」の文字を表示するだけの簡単なアプリによる検証でしたので、このベンチマークの数値はアプリによっては変動するのではないかなと思われます。

まとめ

今回はNGINX UnitをUbuntuにインストールし、簡単なアプリを起動してベンチマークをとってみました。

本来であればphp-fpmやrailsなどとの比較検証もやるべきかと思うのですが、今回は時間がとれなかったため、また別の機会に検証できればと思っております。

弊社では引き続きエンジニア・デザイナーを募集中ですので、ご興味のある方は下からご応募いただければと思います。

scrollIntoView が便利だった話

f:id:astamuse:20200601151445j:plain

こんにちは、フロントエンドエンジニアの minamo です。

東京都でも緊急事態宣言が解除され、営業再開する施設が増えてきましたね。

今週はなんと2ヶ月ぶりに映画館に行きました(多い時は週3回は行っていたので、かなり久しぶり)!

公開延期作品が多い代わりに昔の名作が上映される映画館も多いので、2ヶ月通えなかったぶん、どんどん映画を見まくろうと思います。

2度目のブログはちょっとした内容ですが、 JavaScript で便利なメソッドがあったのでご紹介したいと思います。

scrollIntoView() メソッド

scrollIntoView() とは、対象の要素が ブラウザの表示範囲に入るまで スクロールする、というメソッドです。

developer.mozilla.org

事例紹介

これは私が scrollIntoView() メソッドを知るきっかけだったのですが、モーダルウィンドウの中でスクロール移動したい!というときに便利でした。

f:id:astamuse:20200601151456p:plain
元のページはスクロールしないよう固定しています(字が汚くてすみません)

最初は安直に scrollTo() で移動すればいいや〜と思ってたんですが、モーダルウィンドウ表示のように overflow-y: scroll; している中ではこの方法は使えなかったんですね。

f:id:astamuse:20200601151501p:plain
ウィンドウのスクロール位置は動かないわけですから、当然です。

そこで scrollIntoView() の出番です。

const element = document.querySelector(‘.title’); // 移動させたい要素を指定
element.scrollIntoView();

これで指定した要素が見える位置までスクロール移動してくれます!

オプション(引数)

alignToTop

デフォルトの true では表示させたい要素の上辺がブラウザの上辺に合わせて表示されますが

false を指定すると、表示させたい要素の下辺がブラウザの下辺に合わせて表示されます。

f:id:astamuse:20200601151507p:plain

element.scrollIntoView(false);

scrollIntoViewOptions

behavior: 'smooth' でスムーススクロールができます。

element.scrollIntoView({  
    behavior: 'smooth'  
});

オプションでやってくれるなんて気がきいてますね。

注意事項

一部ブラウザ (IE, Safari) では smooth オプションが有効にならないそうです。

caniuse.com

astamuse ではいつでもエンジニア&デザイナーを募集しています。

もちろんリモートでの面接にも対応していますので、お話だけでもお気軽にどうぞ!

Copyright © astamuse company, ltd. all rights reserved.