ISUCON8 オンライン予選に参加してみて、あらためて Web の世界は広すぎると実感した話

ISUCON8 オンライン予選に参加しました。

isucon.net

コードベースさんに場所をおかりしました。いつもありがとうございます!

結果

どこがボトルネックになっているかほとんど気づけず、何もできませんでした。ボトルネックの探し方をもっと勉強したい(rack-lineprof を使って、get_event と get_events のとこの処理が遅いってことはわかったので、そこだけでもどうにかしたいという気持ちでやったけど、結局何も速くできなかった)

やったこと

以前、Okinawa.rb に参加したときに教えていただいた ISUCONのはじめかたを思い出しながら進めました。(今回は、Rails のお勉強を一緒にやってるメンバーで参加したので、 Ruby を使いました)

shimabukuromeg.hatenadiary.jp

ISUCONでやることのざっくりした流れ(ボトルネックを探して改善を繰り返す)

  • sshの設定で公開鍵はgithubにあげとく作戦(サーバーにすぐsshできるように使う)
  • ssh config 設定(セッションが切れないようにするなど)
  • アプリケーションのコードをgithubにアップする
  • githubにあげたコードをローカルに持ってくる
  • ローカルでアプリを動かすようにする
  • ベンチマーク実行する
  • ブラウザから操作してみる
  • ボトルネックを探す
  • ローカルでコード改修する
  • 環境にデプロイ
  • ベンチマーク実行する

環境構築(10:00 - 12:00)

以下、環境の準備でおよそ2時間程度かかってしまいました。ここでは、ISUCONの本筋とは関係ないところ、事前に準備していればスムーズにいけたはずのところでつまずいてしまっていたので反省。(例えば、ローカルのmysql になぜかアクセスできなくなっていたり、サーバーから githubにコードあげるのに戸惑ったりで、全然ダメダメだった。本来なら30分ぐらいでやっておきたいなと思いました)

  • sshの設定で公開鍵はgithubにあげとく作戦(サーバーにすぐsshできるように使う)
  • ssh config 設定(セッションが切れないようにするなど)
  • アプリケーションのコードをgithubにアップする
  • githubにあげたコードをローカルに持ってくる
  • ローカルでアプリを動かすようにする

ベンチマーク実行/ブラウザから操作/どんなアプリなのか触ってみる(12:00-13:00)

ローカル開発環境の準備が整ったので、ようやくベンチマーク実行して、サーバーのリソース状況やログをみたり、ブラウザから操作してみたりの作業を進めました。なるほど チケットを購入するシステムなのかーだったり、h2oというwebサーバーが動いてるっぽいとか、mysql と bundle のCPU高そうとか、そういったことを考えてました。

h2oをnginxに置き換えてみようと試みる(13:00-13:30)

h2oが何者なのかよくわからなかったので、nginx に置き換えようと思ってやってみましたが、ベンチマーク実行した際に、admin.css あたりで401エラーになってるっぽいというバリデーションエラーが発生してしまい、いまいちどう対応すればよいか、わからなかったので、h2oをそのまま使うことにしました。(nginxはyumでインストールして、webppへ振り分けるように修正しただけだったので他に何か足りなかったのか。。。)

ボトルネックを探して改善を繰り返す(13:00-18:00)

このあとは、ボトルネックを探して、コードを書き直したり、構成を見直したりして、ベンチマークを実行する作業をひたすら繰り返して、スコアを上げていく流れになるはずなのですが、全くもってボトルネックの探しかたがよくわからんという状況におちいって、何もできませんでした。

サボさんの書いてくれてる記事をみながら、とりあえず rack-lineprof の使い方はわかったので、ここで処理が遅い部分のコードを修正しよう!というような感じで進めていきました。

saboyutaka.hatenablog.com

このツールを使ってみると、get_events の中で get_event が繰り返し呼ばれてて、その中で以下のSQLが負荷をかけてるっぽいことに気が付いたので、get_event が繰り返し呼ばれなくても処理できるようにするためにはどうすればよいのか???など考えて、色々コードをいじってみたのですが、結局よくわからず、時間切れになってしまいました。

reservation = db.xquery('SELECT * FROM reservations WHERE event_id = ? AND sheet_id = ? AND canceled_at IS NULL GROUP BY event_id, sheet_id HAVING reserved_at = MIN(reserved_at)', event['id'], sheet['id']).first

まとめ・感想

プログラミングの勉強を今年から始めて、800時間ぐらい学習時間積んでるけど、本当に何もわからないなあと感じました。あらためてWebの世界は本当に広い。この人たちと同じ土俵で会話できる日は来るんだろうか、という気持ちになった。

今回、ISUCONに参加してみて、すごく勉強になったし(勉強になるようなきっかけをたくさんいただけた)、もっとWebのことを知りたいと思いました。運営してくれたチームの皆さま、本当にありがとうございました!来年開催されるんだったら絶対にまた出たい!

Rails を学んでるプログラミング初学者が、いきなりLaravel 書き始めても、意外と書けそうって気持ちになった話

RubyRailsチュートリアルの勉強ばっかりしてる プログラミング初学者(railsチュートリアル2周、学習時間800時間ぐらいの初学者)が、いきなりLaravel を書き初めても、少し勉強してみたら意外とかけそう?!って気持ちになった時の話です。「少し勉強したこと」の部分について書いてみます。プログラミング初学者で、Rails 勉強してるけど、Laravel もちょっと書いてみたい!と思ったかたの参考になれば嬉しいです。

少し勉強したことTODO

  • ドットインストール Laravelのコース 1周(5-8時間ぐらい)
  • PHPフレームワーク Laravel入門 Kindle版 1周(15-20時間ぐらい)

ドットインストール Laravelのコース 1周(8時間ぐらい)

ドットインストールのLaravel コースを1周やると、簡単なブログアプリっぽいものが作れます(認証とかはなくて、簡単なCRUDの処理ができる程度のもの)。シュッと手を動かして、サクッと動くものを作ってみると、Laravelがどんな感じかイメージしやすかったので、よかったです。

https://dotinstall.com/lessons/basic_laravel_v2

PHPフレームワーク Laravel入門 Kindle版 1周(15-20時間ぐらい)

ドットインストールで動くものを作った後に、この本を1周やってみると、ドットインストールでやったことが、いろいろと補足されてる感じあって、より理解が深まりました。個人的に、文章も優しく書いてある感じがあって、すごくわかりやすかったです。

www.shuwasystem.co.jp

意外とかけそう、と思ったことについて

Laravel を少し書いてみると、Rails に出てくる登場人物と、laravel に出てくる登場人物がほぼ同じで、概念もほぼ一緒だと思いました。なので 勉強するときに、頭に入ってきやすかったのかなと思います。routing があって、コントローラーがあって、モデルがあって、ビューがあって.....と、Rails チュートリアルで教えてもらったことが、laravel でもほぼ同じじゃないかなあと思ったのが、少し勉強したTODOをやり終わっての感想です。Railsチュートリアルが laravelを教えてくれたかも知れないと思いました。

qiita.com

なかなか難しそう、と思ったことについて

とはいえ、書けそう!と思ったレベルが、Railsチュートリアルやったことある人レベルの書けそうなので、深いところは Laravel も Rails もわかっていないです。いろいろネットの情報を調べていて、DI とか サービスコンテナ とか、ミドルウェアとかよくわかってないなあと思ってるので、もっと勉強していきたいです。

qiita.com

nunulk.hatenablog.com

ミドルウェアは、Rails でいうと Rackのmiddlewareの考え方と同じらしいけど、よくわかってない。キャッチアップする!

attracie.hatenablog.com

その他 教材

Laravelは、ドキュメントとか動画がめっちゃ充実してる。英語さえわかれば、、、この辺りを使うともっと良さそう。。。

laracasts.com

今度 Laravelの本が出るようです。気になるのでめちゃめちゃ読みたい。

まとめ

Rails と Laravel を比較できるほど、どちらも理解できてないですが、Railsチュートリアル2週ぐらいの学習レベルだと、どちらのフレームワークも同じように感じました。Rails を勉強した後に、Laravel をやってみると学習効率良さそうだと思います(その逆も)。まだまだどちらのフレームワークも理解できてないので、、、実際、深く知っていくと、いろいろと違うとこありそうだし、いま見えてないとこで同じようなことがあったりしそうだなと思っています。もっと勉強する!!!

参考

RailsとLaravelの比較はさぼさん資料がめっちゃ参考になる。

docs.google.com

Laravel 開発構築メモ(備忘録メモ)

最近Laravelをやり始めたので、環境の設定とか忘れちゃわないように備忘録メモ。

僕の環境

PhpStorm 2018.2.2

Docker for Mac Version 18.06.1-ce-mac73 (26764)

開発環境の準備

サボさんが作ってくれたリポジトリをcloneして、make build して、 make serve したらすぐ使えるようになる〜

(docker自体のことに関しては、いまいち理解できてないので、勉強する!!!)

Phpstormの設定(xdebugを使えるようにする)

  • その1 ツールバーのPreferenceから以下キャプチャの箇所の設定する。ポート設定

  • その2

ここまでの設定ができたら、受話器のとこクリックして、Xdebug からの通信を受け付ける状態にする

9001ポートがListenされてる

ブラウザからアクセスしてみる(以下のキャプチャはドットインストールでLaravelのコースを一通りやって作ったサンプルアプリ)

breakポイントより前でどんな感じに値が入っていたか確認できる

まとめ

xdebugめちゃめちゃ便利!!!(いろんな人にみてもらいたい気持ちで書き始めたけど、途中自分でもあまり理解できてなくて、ちゃんと説明出なそうことに気づいて断念した。30分以上かかりそうだったので、また落ち着いた時にトライする)

参考

多分この記事がめっちゃ参考なるかと!

blog.shin1x1.com

vuejsお勉強メモ

rails&laravel mixで環境作って、ドットインストールのコースを一通りやってみた。コンポーネントむずい。

やったこと

  • 環境構築(rails + laravel mix)

qiita.com

  • ドットインストールのvuejsのコース一通り。todoアプリ作成&いいねボタン作成とカウントなど

github.com

コンポーネントとは???

コンポーネントは名前付きの再利用可能な Vue インスタンスです

jp.vuejs.org

コンポーネント指向とは

f:id:shimabukuromeg:20180816224105p:plain

なんとなくの理解

  • html側でdiv要素を書いて、適当なidをつける。vuejs側は、vueのインスタンスを生成するときに、html側のdiv要素(element)のidを指定する。そうすると、UI側(UI側???って表現があってるかわかんないけど、html側)とvuejs側が紐づいて、vuejs側で持ってるデータ (配列)とかメソッドを利用できるようになる。

jp.vuejs.org

  • v-model だとか v-for とか v- から始まる特殊な属性をディレクティブと呼ぶ。(html側に書くやつ)

  • submit された時のイベントですが、イベントを紐付けるには v-on というディレクティブを使う(v-on はよく使うので @ で @submit のように省略)このとき、イベントが発生して呼び出すメソッドは、vuejs側のvueインスタンスの、method: {} の中に定義する。

f:id:shimabukuromeg:20180817081711p:plain

  • データに応じてクラスを付け替えるには、 v-bind:class というディレクティブを使う。<span v-bind:class="{done: todo.isDone}"> これはtodo.isDoneがtrueだったら、spanタグのclassにdoneを付与する。

  • <li v-show="!todos.length">Nothing to do, yay!</li> v-showディレクティブ。これは!todos.lengthがtrueだったら、Nothing to do, yay!が表示されるってやつ

  • 部品を再利用するために Component っていう機能を使う

  • Componentの機能を使うとき、まずhtml側では、vueのインスタンス作成するときに紐付けたdiv領域の中で、適当なオリジナル要素を作成する。例えば、<like-component></like-component> のような感じ。ここの部分をvuejs側から操作できるようになるイメージ。

  • vuejs側はどんな感じにするのかっていうと、最初にvueのappのインスタンスを作成したやつの中に、components: { 'like-component': likeComponent } って書いて、コンポーネントを使うよって定義してあげる。ここで定義したやつを、vueのappのインスタンスの上の方で定義する。例えば、Vue.extend としてあげて、その中にどの中身を入れたいかは、 template で書く。このtemplateに書いたやつが、最初にhtml側で書いた <like-component></like-component> の中で表示されるようになる。

  • さらにmethodとかデータを使いたかったら、Vue.extend したvueのインスタンスの中にいろいろ書いていく。

    var likeComponent = Vue.extend({
        // props: ['message'],
        props: {
          message: {
              type: String,
              default: 'Like'
          }
        },
        data: function () {
            return {
                count: 0
            }
        },
       template: '<button @click="countUp">{{ message }} {{ count }}</button>',
        methods: {
           countUp: function () {
               this.count++;
               this.$emit('increment');
           }
        }
    });

ドットインストール vuejsのコース一通りやったらできるtodoアプリのコード。

app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue'));

document.addEventListener("DOMContentLoaded", function () {

    var likeComponent = Vue.extend({
        // props: ['message'],
        props: {
          message: {
              type: String,
              default: 'Like'
          }
        },
        data: function () {
            return {
                count: 0
            }
        },
       template: '<button @click="countUp">{{ message }} {{ count }}</button>',
        methods: {
           countUp: function () {
               this.count++;
               this.$emit('increment');
           }
        }
    });
    var vm = new Vue({
        el: '#app',
        components: {
          'like-component': likeComponent
        },
        data: {
            newItem: '',
                todos: [],
            total: 0
        },
        watch: {
            todos: {
                handler: function () {
                    localStorage.setItem('todos',JSON.stringify(this.todos));
                },
                deep: true
            }
        },
        mounted: function () {
            this.todos = JSON.parse(localStorage.getItem('todos')) || []
        },
        methods: {
            addItem: function () {
                var item = {
                    title: this.newItem,
                    isDone: false
                };
                this.todos.push(item);
                this.newItem = '';
            },
            deleteItem: function (index) {
                if (confirm(('agre you sure?'))) {
                    this.todos.splice(index, 1);
                }
            },
            purge: function () {
                if(!confirm('delete finished?')){
                    return;
                }
                this.todos = this.remaining;
            },
            incrementtotal: function () {
                this.total++;
            }
        },
        computed: {
            remaining: function () {
                return this.todos.filter(function(todo){
                    return !todo.isDone
                });
            }
        }
    });
});

index.html.erb

<div class="container">

  <div id="app">
    <p>Total Likes: {{ total }}</p>
    <like-component message="Like" @increment="incrementtotal"></like-component>
    <like-component message="Awesome" @increment="incrementtotal"></like-component>
    <like-component message="Great" @increment="incrementtotal"></like-component>
    <like-component @increment="incrementtotal"></like-component>
    
    <h1>
      <button @click="purge">Purge</button>
      My Todo
      <span class="info">({{ remaining.length }}/{{ todos.length }})</span>
    </h1>
    <ul>
      <li v-for="(todo, index) in todos">
        <input type="checkbox" v-model="todo.isDone">
        <span v-bind:class="{done: todo.isDone}">
          {{ todo.title }}
        </span>
        <span @click="deleteItem(index)" class="command">[x]</span>
      </li>
      <li v-show="!todos.length">Nothing to do, yay!</li>
    </ul>

    <form @submit.prevent="addItem">
      <input type="text" v-model="newItem">
      <input type="submit" value="Add">
    </form>
  </div>

</div>

rails enum の日本語表示の作業メモ

結果的に、ほぼ、これ通りにやるとできた。

qiita.com

最初、この辺りをみてて、同じようやってたはずなのに日本語化され図、いろいろとハマる。

www.monokoto.xyz

kimuraysp.hatenablog.com

いろいろ調べてみた結果、理由はおそらく、localeの言語設定変更、config/application.rbconfig.i18n.default_locale = :ja を追加してなかったからだと思われる。追加したらちゃんと日本語化された。

qiita.com

設定反映後、pryでいろいろ確認してみた作業ログ

[32] pry(main)> i = Item.find(2)
  Item Load (0.8ms)  SELECT  "items".* FROM "items" WHERE "items"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
=> #<Item:0x000055a71e328ed8
 id: 2,
 name: "シンボル",
 category_id: 11,
 user_id: 2,
 price: 3500,
 description: "引越しするので譲ります",
 status: "published",
 created_at: Mon, 13 Aug 2018 15:30:58 UTC +00:00,
 updated_at: Tue, 14 Aug 2018 01:40:55 UTC +00:00>
[33] pry(main)> i.status_i18n
=> "公開"
[36] pry(main)> i.status
=> "published"
[38] pry(main)> Item.statuses
=> {"draft"=>0, "published"=>1, "done"=>2}
[39] pry(main)> Item.statuses.keys
=> ["draft", "published", "done"]

github.com

正規表現お勉強メモ

正規表現お勉強メモ

このシリーズすごくわかりやすかった。振り返る場所メモ。

rubular.com

t.co

f:id:shimabukuromeg:20180814084717p:plain

laravel-mixがやっていることを理解する(理解するというか、railsのフロント環境をlaravel-mixで構築してみて、 よくわからないところを整理する)

以下のサボの記事を参考に構築する。(気になったところをメモしていく)

RailsでLaravel Mix(webpack)を使って15分でES6を書きはじめる

qiita.com

webpack.mix.js は何者?

→resources/assets/js/app.jsをコンパイルして、public/js配下に出力してくれるようにしてくれてるやつっぽい。アセットをどのようにコンパイルするか定義してる。

let mix = require('laravel-mix');
mix.setPublicPath('public')
    .js('resources/assets/js/app.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css');

if (mix.inProduction()) {
    mix.version();
}

参考情報

Laravel mixは、jsやsassなどのアセットを簡単にいい感じにコンパイルしてくれるビルドツールで、コンパイル対象のファイルを増やしたい時とかは、webpack.mix.jsに書く。

public/mix-manifest.json何者?

→yarn run devコマンドを実行して(package.jsonに定義されてるコマンド npm run dev が実行されて)、アセットファイルがコンパイルされると、public下に作成されてるやつ。このファイルの中身をみると、resource配下のjs/app/jsをコンパイルして、public配下のjs/app.jsを作成したよって意味っぽい(cssも同じ)

$ cat public/mix-manifest.json
{
    "/js/app.js": "/js/app.js?id=2357e5caf066e7a52ea8",
    "/css/app.css": "/css/app.css?id=52caa47f003ce624f239"
}
  • yarn run dev バージョニングされない
  • yarn run prod バージョニングされる とかある。

app/helpers/laravel_mix_helper.rbは、何やってるんだろ?

$ cat app/helpers/laravel_mix_helper.rb
module LaravelMixHelper
  class LaravelMixError < StandardError; end
  MANIFEST_FILE = 'public/mix-manifest.json'

  def mix(path)
    unless File.exists?(MANIFEST_FILE)
      raise LaravelMixError.new('The Mix manifest does not exist. Run `yarn run dev`.')
    end

    manifest = JSON.parse(File.read(MANIFEST_FILE))
    asset_path(manifest[path])
  end
end

このmixメソッドが呼び出される場所は、例えば、以下の$ cat app/views/layouts/application.html.erbとか。laravel_mix_helperは、MANIFEST_FILEがあるかどうかチェックして、なかったらアセットファイルをコンパイルするよう(yarn run dev)、例外が発生するようになってて、MANIFEST_FILEが存在したら、そのファイルを元にコンパイルした後のアセットファイルのパスを返してるっぽい。

$ cat app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>RailsLaravelMix</title>
    <%= csrf_meta_tags %>
    <link rel="stylesheet" href="<%= mix('/css/app.css') %>">
    <script src="<%= mix('/js/app.js') %>"></script>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
    $ cat resources/assets/js/app.js
    require('./bootstrap');
    
    window.Vue = require('vue');
    Vue.component('example-component', require('./components/ExampleComponent.vue'));
    
    const app = new Vue({
        el: '#app'
    });
    

以下の感じに書き換えたら、表示された

$ cat resources/assets/js/app.js
require('./bootstrap');
window.Vue = require('vue');
Vue.component('example-component', require('./components/ExampleComponent.vue'));

document.addEventListener("DOMContentLoaded", function() {
    const app = new Vue({
        el: '#app'
    });
});

次は、この環境を使って、vuejsを書いてみる。

参考

  • webpack.mix.js って何?追記

qiita.com

Laravel mix事始め

qiita.com