ishikawa_pro's memorandum

若手webエンジニアの備忘録です.

Swift Node.js Docker AWS etc...色々やります。

2019年振り返り& 今年の豊富

あけましておめでとうございます。

年末に2019年の振り返りブログを書こうと思っていましたが、思った以上に時間が取れなかったので年が明けてしまいました。
折角途中まで書いていたのでボツにはせずに、ダイジェストで2019年の振り返りと今年の豊富的なことを書いておこうと思います。

1月 ~ 2月

1 ~ 3月は、まだ学生でした。
確か卒業制作というやつも大方決着がついていて、報告書とか作ってた気がします。
学校以外では、クラウドワークスや直請けで、iOSアプリの新規開発などをやっていました。学校以外の時間は仕事をしていた記憶しかないですね。

3月

学校を卒業しました。
特に打ち上げなどもなく、みんな昼過ぎに颯爽と解散したのが印象的でした。(僕自身もさっさと退寮手続きを済ませて実家へ帰りました笑)
学校以外では、3月も基本的には仕事ばかりしてました。

イベントとしては、確か内定先の企業で研修がありました。技術的な研修ではなく、ビジネスマナーの研修でした。
名刺交換の練習、電話の仕方や会議室などの上手・下手など、一杯叩き込まれました。
個人的には、かなりアンコンフォータブルだったので社会人になれるか心配になりました笑

3月末には、就職のため東京へ引っ越しました。
引っ越し先では、電気を契約し忘れていて数時間電気が来ないというハプニングがありましたが、概ね問題なく引っ越しました。

4 ~ 5月

4月1日に入社式があり、2週目くらいから5月末まで、技術研修が始まりました。
研修の中で、ISUCONの過去問を解いたのですが、全然手も足も出せなかったのが印象的でした。

4 ~ 5月でもっとも印象深かった出来事は、人事から会社の方針的にiOSエンジニアへの配属はできないと伝えられたことでした。
iOSエンジニアになるつもりで入社した自分としては、かなり衝撃を受けました。
僕は、サーバーサイドもちょっとだけ経験があったのと、フロントエンドを0から学んでついていける自信がなかったので、サーバーサイドエンジニアになることを決めました。
当時は結構ショックでしたが、今では楽しくNode.jsを書いています。

6 ~ 8月

仕事面では研修を終えて、仮配属されました。
研修中はずっと渋谷にいましたが、仮配属では中目黒の方にあるオフィスに出社するようになりました。
基本的には、Node.jsのバージョン8で動いているアプリケーションの運用を手伝いつつ、Node.jsのバージョンを10系にあげるために(テストがなかったので)黙々とユニットテストを書いていたりしました。今思うと社内やNode.jsの歴史を知ることができて色んな意味でいい勉強になりました。

8月にはAWSの日本のリージョンで起きた障害が起因で、色々と痛い目をみたのが思い出深かったです。(復旧させるのに朝までかかった涙)

9月

9月は仕事面は特に印象的なことはなかった気がします。

プライベートでは、いくつかイベントがありました。
1つ目は、iOSDCに参加しました。 学生時代から行くのが夢だったので参加してみました。
ishikawa-pro.hatenablog.com

2つ目は、ISUCON9予選に出場しました。
これは、就職したら仲間を募って出てみたいなと思ったのと、会社の研修で過去問を解いてみて、より挑戦してみたくなったので会社の同期3人と出場しました。
当日やったことなどは、別エントリーにまとめてあります。
研修でやった時は、手も足も出ませんでしたが、今回は手ぐらいは出せました。(でもスコアはほとんど上がりませんでした。) ishikawa-pro.hatenablog.com

3つ目は、自作キーボードに挑戦しました。
学生時代から肩こりが悩みで、分割キーボードが欲しかったので、会社の同期と一緒に秋葉原にある遊者工房という自作キーボード専門店に行って作ってきました。

yushakobo.jp

下記は、実際に作ったlily58 proというキーボード。
会社で使ってます。

f:id:ishikawa_pro:20200101155707j:plainf:id:ishikawa_pro:20200101155703j:plain
lily58 pro

10月

10月は仕事面でイベントがありました。
仮配属期間が終わって本配属になり、合わせて僕は部署の異動となりました。
中目黒オフィス勤務でしたが、渋谷オフィス勤務に戻りました。
仕事環境も変わりましたが技術面でも、MongoDBやマイクロサービスなど今までとは違う技術を扱うようになりました。

部署が変わって忙しかったので、プライベートはハロウィンの日の渋谷がやばかったくらいしか記憶にありません。

11 ~ 12月

仕事は忙しかったです。
イベント的には、会社でアドベントカレンダーを企画しました。
これも学生時代からやってみたかったことだったので率先して企画して、同期を中心に協力してもらい25日分埋めました。 あと、コーポレートサイトに宣伝記事まで書いてもらいました。
www.cam-inc.co.jp

僕はNode.jsの記事を書きました。 ishikawa-pro.hatenablog.com

今年の豊富

去年は、技術面でほとんど社内から出ることがなかったので、今年はもっと社外向け勉強会などに参加して、社外のエンジニアと交流してみたいなと思ってます。
あと、毎年思ってますが、もうちょっとブログ書きたいです笑

プライベートでは、去年は全然音楽ができなかったので今年は機材を買い直して、また音楽を始めたいなと思ってます。

そんなところですかね笑

今年もよろしくお願いいたします。

社内で見かけるNode.jsデザインパターン

こんにちは。id:ishikawa_proです。
この記事は、CAM Advent Calendar 1日目の記事です。
会社でAdvent Calendar をやりたいと僕が言い出したので、1番手をいただきました!

僕は、今年CAMの新卒として入社し、サーバーサイドエンジニアとして、主にNode.jsを書いてきました。
今回は、業務でNode.jsのアプリケーションを書いていて、理解する上で重要だなと個人的に思ったデザインパターンを紹介したいと思います。

本題

僕は入社してから主に2つの既存サービスにジョインして開発などに携わってきました。
今回は弊社のNode.jsアプリケーションを読んでいて、理解するのに重要だなと思った

  • Singleton
  • DI

の2つのデザインパターンについて、Node.jsでの実装と弊社での使い方について紹介します。

Singleton

まずは、Singletonについてです。

singletonとは

Singletonとは、

  • そのクラスのインスタンスが1つしか生成されないことを保証する

ためのGoFデザインパターンの1つです。
ja.wikipedia.org

Node.jsでの実装

javascriptはmodule pattern自体がsingletonに則しているので、特別意識していなくともsingletonになっています。

// hoge.js
module.exports = {
  hoge: 'hoge'
};
// fuga.js
const hoge = require('./hoge');
console.log(hoge.hoge);
module.exports = hoge;
// main.js
const hoge = require('./hoge');

console.log(hoge.hoge);
hoge.hoge = 'fuga';
console.log(hoge.hoge);

const fuga = require('./fuga');
// ログ
hoge //main.jsの最初のconsole.log
fuga //main.jsの2番目のconsole.log
fuga //fuga.jsのconsole.log

main.js で hoge.jsのObjectのvalueを書き換えてから、fuga.jsをrequireするとfuga.js内でrequireしたhoge.jsの値がmain.jsで書き換えた値になっています。
1度 require されたモジュールはキャッシュされることを利用して、javascriptでは簡単にsingletonを実装することができます。

下記のように、classをインスタンス化したものをexportすることで、よりsingletonらしい(?)書き方もできます。

class Person {
  constructor() {
    this.name = 'ishikawa-pro';
  };

  greet() {
    console.log('My name is ishikawa-pro!!');
  }
}
module.exports = new Person();

社内で使っている所

弊社のNode.jsアプリケーションでは、ORMや別のアプリケーションとやりとりするためのclient ライブラリなどを管理するためにContextという名前のClassをよく用います。
このContextクラスでは色々なmoduleのインスタンス管理などをしており、各所でcontext classの新しいインスタンス生成をされては困るため、先ほどの例のようにContext classを宣言してインスタンスをexportしています。
そしてアプリケーション起動時にContextが初期化され、各controllerなどではcontext経由で色々なライブラリ呼び出して使っています。

// context.js
const hogeApiClient = require('./api_client');
const mysqlClient = require('./mysql_client');

class Context {
  constructor() {
    this.mysql = mysqlClient();
    this.hogeApiClient = hogeApiClient();
  }
}

module.exports = new Context();
// app.js
const express = require('express');
const context = require('./context');
const app = express();

app.get('/person', (req, res) => {
  const {Person} = context.mysql.instance.models;
  Person.findAll().then(res => {
    res.send(res);
  });
});

app.listen(3000);

上の例は結構いい加減ですが(ごめんなさい🙇‍♂️)、 だいたいこのようにcontext経由でORMなどを呼び出したりして使っています。

実際の業務では様々かつ大量のmoduleを使っており、それらのインスタンスなどのライフサイクルを一元管理するためにSingletonを使っています。

注意点

singletonは保守性やテスタビリティの観点から使い所が難しいと言われています。 なので業務で使う時は、グローバル変数としてメソッド間で状態を共有するような使い方などはせず、あくまで必要なライブラリを呼び出すためのToolBox的な役割が大きいです。

DI(Dependency Injection)

続いては、DIについてです。

DIとは

DI自体は定番のパターンなので詳しい説明は避けますが、

  • そのモジュールが依存するオブジェクト(設定ファイルなど)を外部から注入する

ことです。
依存するオブジェクトを差し替えられるにすることで、テスト時はmockに差し替えたり、環境ごとに異なった設定ファイルを読み込ませることが簡単にできるようになります。

ja.wikipedia.org

取り上げておいてなんですが、そもそもjavascriptは動的型付け言語なので型に縛られることもないし、sinonのstubなどを使ってしまえばDIを使わずにテスト時に依存するオブジェクトを差し替えることが可能なので、実際そんなに活躍することはないかもしれないです。
たまに型で縛ってDIとかでカチッと実装したい時もありますが、そんな時はいっそTypeScriptに移行しましょう。笑 (jsで使えるDIコンテナライブラリもありすが、、、)

Node.jsでの実装

それでもたまに使うことがあるので、Node.jsでの実装を書いてみます。
以下はconstructor injectionのパターンです。

// config_development.js
module.exports = {
  db: {
    mysql: {
      adress: 127.0.0.1,
      port: 3306
    }
  }
};
// mysql_client.js
const MySQLClient = require('MySQLClient');

module.exports = (config) => {
  return new MySQLClient(config);
};
// index.js
const config = require('./config_development');
const db = require('./mysql_client')(config.db);

Configのオブジェクトをexportしたmoduleと、configを引数で受けるようになっているmoduleがあり、index.jsでそれぞれをロードして組み立てています。
こうすることで、devとstg環境で異なった情報を渡して初期化したりなどが容易になります。

社内で使っている所

業務では、先ほどのsingletonで紹介したContextを、DIで注入して使っている場合があります。

// src/context.js

const services = require('./services');

class Context {
  constructor() {
    this.services = services(this);
  }
}
module.exports = new Context();
// src/services/index.js
modules.exports = context => {
  retrun {
    user: require('./user')(context),
  };
};
// src/services/user.js
module.exports = context => {
  class User {
    // 色々contextに依存したロジック
  }
  return User;
};
なぜここでDIが必要か

一見、別にDIで注入しなくてもContextはSingletonなんだから、

// src/services/user.js

const context = require('../context');

class User {
  // 色々ロジックを書く
}
return User;

のように、使うところで毎回requireすれば良さそうに見えます。
ですが、context moduleがservicesをrequireしており、services以下のmoduleで context をrequireしてしまうと循環参照がおきてしまいます。
循環参照がおきてしまうと、参照先のmoduleはまだ評価中なので空のオブジェクトが返ってきてしまい、思い通りの挙動にならいのです。

その他にも環境ごとにconfigを読み分けたりなどにもDIを使っていますが、今回はjavascript独特な問題を解決するための手段として使っている部分を取り上げみました。

まとめ

いかがだったでしょうか?
普段、既存サービスに機能を追加したりなどしかしていないと、下回りがどうなっているかを意識しなくなりがちだったので、いつも触っているアプリケーションでどんなことをしているかを理解するために必要そうなデザインパターン取り上げてみました。

まだまだNode.jsについて分からないことだらけなので、今後もどんどん深掘って勉強していきます!

明日は、 先輩フロントエンドエンジニアMuuKojima さんの
Web Componentsがフルに導入されたプロジェクトに入った結果。
です!
お楽しみに!!


参考文献

CORS(オリジン間リソース共有)・Preflight Request まとめ

こんにちは。
だいぶ秋らしくなってきましたね。
今日はCORSやPreflight Requestについてまとめてみます。

iOSアプリ用にAPIサーバーは作ったことがあったけど、フロントエンド向けにAPIサーバーを作ったことがなくて、ブラウザのCORSは全然知らなかったので、ちゃんとまとめておこうという感じです。

CORSとは?

オリジン(ドメイン)をまたいでリソースを共有すること。
要するにXMLHttpRequestやFetch APIで別ドメインAPIサーバーにアクセスすることです。
XMLHttpRequestやFetch APIは基本的に同一オリジンにしかリクエストできないですが、CORSの設定をすることで許可されたクロスオリジンにリクエストが可能になります。

CORSを正しく設定していないとCSRFで攻撃されてしまう可能性があるから、なんとなくクライアントを守るような仕組みだと勘違いしていましたが、守る視点はAPIサーバー側です。
設定もフロントエンドでは特にすることはなく、基本的にサーバー側で設定して、フロントはサーバー側の設定にしたがってブラウザが振る舞いを決めるだけです。
APIサーバーを守るためのルールだという視点があれば、MDNのドキュメントを見てもピンとくると思います。

Preflight Requestとは

Preflight Requestは、実際の通信の前に権限確認のため送信するリクエストです。Preflightが必要かブラウザが判断してリクエストを送ります (後述)。 先ほども書きましたがブラウザが自動でやるので、フロントエンド側が手動で送る必要はありません。
PreflightはOPTIONSメソッドを使って、許可されているMethodとHeaderを問い合わせます。

simple or actual

Preflight Requestは、必要な場合とそうでない場合があります。
下記条件はsimple cross-origin requestといい、Preflight Requestは必要ありません。

  • GET, HEAD, POST メソッド
  • HeaderがCORS-safelisted request header のみ
  • Content-Typeを含む場合に値が、 application/x-www-form-urlencode, multipart/form-data, text-plain のどれか

CORS-safelisted request header に該当するヘッダー名は以下の4つです。

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type

上で述べた条件に当てはまらない場合は、Preflight Requestが必要になります。

MDNのCORSのページを見ると、DPR, Downlink, Save-Data , Viewport-Width, Width というclient hintsも含んでいるように書いていたけど、ググっても他でclient hintsに関する記述をしてるところがなかったので、どなたか知ってる人いれば教えてください。 developer.mozilla.org

リクエストヘッダー

Preflightが発生する場合、クライアントは以下のリクエストヘッダーをつけてOPTIONSメソッドでPreflight Requestを飛ばします。

  • Access-Control-Request-Method
    • 通信を許可してもらいたいメソッドを指定
  • Access-Control-Request-Headers
    • 許可してもらいたいヘッダーをカンマ区切りで列挙
  • Origin

Access-Control-Request-Headers に、上で述べた CORS-safelisted request header は記述不要です。それ以外のカスタムヘッダーなどを追加する場合は列挙が必要です。

レスポンスヘッダー

サーバー側はクライアントからのPreflight Request を受けて、許可するメソッドなどを以下のレスポンスヘッダーで示します。

  • Access-Control-Allow-Origin
    • リソースへのアクセスを許可するオリジン
    • どのオリジンからもアクセスを許可する場合は * を指定
  • Access-Control-Allow-Methods
    • 対象のURLに許容されるメソッド
    • Preflightが不要なメソッドは省略されることがある
  • Access-Control-Allow-Headers
    • 対象のURLに許容されるヘッダー名のリスト
    • CORS-safelisted request header は省略されることがある
  • Access-control-Expose-Headers
    • サーバーから返すレスポンスヘッダーのうち、スクリプトから参照できるヘッダー名の一覧を返す
    • デフォルトでは、 Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma のみ(CORS-safelisted response header)
  • Access-Control-Allow-Credentials
    • クッキーなどの資格情報をサーバーが受け取ることを許可する時に付与される
    • 値はtrueのみ設定可
  • Access-Control-Max-Age
    • Preflight Requestのキャッシュ可能秒数

ブラウザは、上記の内容をみて通信が可能かどうかを判断しています。
サーバー側は、許容しない内容がある場合はレスポンスヘッダーに含めなかったり、401 Forbiddenのステータスを返したりして対応します。

まとめ

上記でまとめたようなPreflight Requestを送ったり送らなかったりしながら、クロスオリジン間でブラウザは通信をしています。
ブラウザのデベロッパーツールで通信を覗くとOPTIONSメソッドでPreflight Requestが飛んでいるのを見ることができます。

感想

僕は入社してすぐの研修で作ったアプリケーションでは、ブラウザでCORSのエラーが出て適当にAccess-Control-Allow-Originに * をつけて対応したような気がします笑
実務でも誰かがいい感じに設定したexpress.js用のmiddlewareをなんとなく使ったり、OPTIONSメソッドがないとなんかフロントがうまくAPI叩けないというざっくりとした認識でしたが、しっかり知識として落とし込めてよかったです。
今回の内容はMDNのドキュメントやReal World HTTPを参照しながらまとめました。

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

頑張って最低週1でブログ書くように頑張ってるけど、続くか心配・・・
来週も頑張ります。

MySQL5.7のJSON型について調べた

こんにちは。
今日はMySQLJSON型を使う機会があったので、勉強したメモです。
基本的には、ドキュメントの内容を引っ張ってきて書いてます。
英語苦手なので解釈間違ってるかも。

dev.mysql.com

調べた経緯

業務で新しい機能を作っていて、テーブル定義のレビューの際に、それJSON型でいいんじゃないかと言われたのがキッカケです。
JSON型が使えるのは知っていましたが、実際に業務で使えるものなのか、どういう時に使えばいいかなどを全然知らなかったので、ちょっと勉強してみました。
今回は、とりあえずJSON型とは、について調べました。
ちょっと長くなりそうだから実際に使うのは次の記事で。

JSON

  • 効率的にJSONドキュメントにアクセスできる。
    • String型のカラムにJSONフォーマットの文字列を入れるよりも効率的。
    • JSON formatになっているかvalidationされる
  • JSONカラムに格納されたJSONドキュメントは、ドキュメントの要素に高速な読み取りアクセス可能な内部フォーマットに変換される
  • 格納するのにLONG BLOBかLONG TEXTとほぼ同じスペースが必要
    • 格納できるサイズはmax_allowed_packet で指定されたサイズに制限される
  • NULL以外のデフォルト値を含めることはできない
  • create, operation, searchなどのJSON値の操作は、関数を利用できる

特徴

JSONカラムの値を読み出す時、ドキュメント内の前後の値を全て読み込まなくても、サブオブジェクトやネストされた値に、キーまたは配列インデックスで直接アクセスできるように構造化されているため、テキスト表現からパースする必要がなく効率よく取り出せる。

indexについて

  • 他のバイナリ型同様にindexは貼れない。
    • JSONカラムの値からgenerated columnsを作って、そこにindexを貼ることで対応できる。

使い道

JSON型はNoSQLっぽく使えそうだから乱用しそうになりますが、generated columnsにしかindexが貼れないので適当なJSONを入れまくって雑に使うようなことは避けたほうが良さそう。多分そういう時は、ドキュメント指向DBとかが良いと思う。(ドキュメント指向DBが雑に使っていいという訳ではないです。)

今回は、今後カラムが増える可能性がある(増えない可能性もある)が、その度にalter tableをしたくないからテーブル設計をどうするか、というのが主題でした。
最初は、色々話をしてクラステーブル継承を使って解決しようとしていましたが、属性が増えて結合するテーブルの数が増えると辛くなるよ、と言われて半構造化データとしてJSON型を使うことにしました。

その他JSON型を選定した理由として、

  • サーバーサイドがnode.jsでJSONとの相性が良い
  • sequlize(ORM)がJSON型に対応している
  • 格納するデータが不揃い
  • generated columnsに貼ったindex以外で検索することがない

などです。
JSON型を使ったことがなかったので、RDBJSONという正規化されてないデータをそのままぶち込むようなことをしてもいいのかという、疑問と不安でよく調べもせずに選択肢から外してたので今回はとても勉強になりました。

次回は、実際に使う感じの記事を書こうと思います。

iOSDC2019に参加しました!

 こんにちは。

ちょっと日が経ってしまいましたが、9/5 ~ 9/7 にあったiOSDCに初めて行ってきたのでその記録です。

 f:id:ishikawa_pro:20190911225910j:plain

iOSDCとは?

詳しくはこちらを参照してください。

iosdc.jp

主にiOSに関する技術者向けのカンファレンスです。

 

なぜ参加したか

iOSアプリ開発を大学2年生の終わり頃からしていて、大学卒業するまではiOSアプリ開発を軸に勉強していました。
学生時代にもiOSDCの存在は知っていましたが、地方に住んでいたのとお金が本当になかったので、iOSDCなどの技術カンファレンスは知ってはいるものの自力では行くことのできない存在でした。 それが就職を機に上京したので、ついに念願のiOSDCに参加することができたわけです。

 

チケット

奮発してサポーターチケットにしました。
高いと感じる人もいるかと思いますが、iOSDCパーカーが貰えたり、聴きたいセッションの席を予約できたりと、かなり特典が充実しているのでサポーターになる価値は十分あります。 f:id:ishikawa_pro:20190912001038j:plain

ランチ

参加者向けにお弁当まで用意されていました。 ランチは、交流スペースのような場所で食べたりランチセッションというランチを食べながら見れるセッションなども用意されていました。

f:id:ishikawa_pro:20190911230058j:plain
1日目の弁当
3日目のランチセッションがBitriseさんの英語セッションで、楽しみだったので弁当撮り忘れました笑
(英語は苦手なのでほぼ聞き取れませんでした)

面白かった・勉強になったセッション

ライブラリのインポートとリンクの仕組み完全解説

fortee.jp

インポートやリンクは、使ってるだけで全然理解してなかったので、その勉強の足がかりになるとてもいいセッションでした。

FatViewControllerを安全に書き換える方法が見つからなかったので、どういう痛みを許容するか考えた

fortee.jp
FatViewControllerを解消することの難しさ・辛さ的な話。

Swiftクリーンコードアドベンチャー ~日々の苦悩を乗り越え、確かな選択をするために~

fortee.jp
コードを抽象化して行く際によく躓くけどイマイチどこが分からないのかがピンと来ていなかったが、このセッションでProtocolのStaticな面とDynamicな面の特徴がよく理解できてないのと、PATについて知ったのでめちゃくちゃ勉強になった。

モバイル決済アプリの作り方

fortee.jp
ここ数年はやってる、*Payに関する話。
法律に関する部分が多かったが勉強になった。

Amazon Pinpoint でユーザーに合わせたプッシュ通知を実現する

fortee.jp

以前、ユーザーに合わせて個別の内容をPush通知で送りたいと言われて、それは難しいですとお断りをしたことがあったので、Amazon Pinpoint試してみようとと思いました。

TBD

fortee.jp
rebuild.fm レギュラーのhakさんとiOSDC実行委員長のtomzohさんとの対談。
rebuild.fmファンとしては、hakさんの話が生で聞けて最高に嬉しかった。

茶会

2日目の終わりに、お酒を飲まないことがマジョリティな懇親会がありました。(見た感じ結構な人がお酒飲んでた気もした笑)
僕は、初対面のiOSエンジニア3人とずっと話をしてました。
ボードゲームがあったり、初心者、女性など色々な属性で集まれるようにもなっていてよかった。

懇親会

次の日にISUCON9予選があり、最高のコンディションで挑みたかったので、残念ながら今年は懇親会不参加でかえりました。
ISUCONの振り返りブログはこちら!
ishikawa-pro.hatenablog.com

まとめ

とにかく最高の3日間でした。
見たかったが時間が被って見れなかった(寝坊した)セッションや、見返したいセッションも沢山あるので動画が上がったら、また見ていこうと思います。
参加するという夢は叶ったので、次はLTでもいいから登壇するという目標で、来年まで頑張ろうと思います。

ISUCON9予選に参加しました!

お久しぶりです。
ISUCON9予選に参加したので、参加するまでのことや当日やったことを残します。

ISUCONとは

こちらを参照してください。

isucon.net

参加のきっかけ

5月頃に僕ともう一人の同期でISUCONに出たいね、という話になって他の同期も誘って、参加することになりました。

メンバー

今回は会社の新卒同期3人でチームを組んで出場しました。
(僕ら以外にも、新卒チームがもう1チーム参加しました。)

当日までに勉強したこと

会社の同期で、毎週 月・火・水の3日間は1時間早く出社して、過去問を解くというチャンレンジを6月頃からしていました。
また、社内でISUCON合宿という1日中ISUCONの過去問をやる合宿までやらせて頂きました。
会社と企画してくれた同期に感謝です 🙏

www.cam-inc.co.jp

当日朝の様子

当日やったこと

言語は、僕がGoを勉強してみたかったので6月下旬頃からGoの勉強をしつつ過去問を解いていて、当日もGoでやりきりました。(普段はNode.js書いてます)

役割分担は、
ko1103: リーダー & 実装
cotsupa: DB周り
id:ishikawa-pro: リポジトリ管理・ログ収集・実装

という感じでした。

時間に沿ってやったこと書いていきます。

10:00 ~ 11:00

リーダーがインスタンス立てたり、僕がGitHubのprivateリポジトリでコードを管理できるようにしたりしてました。

11:00 ~ 11:45

slow query出すようにしたり、alpaccess logを分析できるようにNginxの設定を変えたりしてました。
僕は、ちょっとだけNginxの設定に時間がかかってしまいましたが、他のチームメンバーも含めこの辺の作業は練習しただけあって、比較的すんなりいきました。

11:45 ~ 12:30

slow queyとかalpの吐いたlogを見つつどの辺が自分たちで修正できそうか探し始めました。
ko1103がUserテーブルからデータをとる部分で、カラム指定してちょっとだけスコア上げてくれました。
現時点で 2010イスコインでした
github.com

12:30 ~ 13:00

お昼休憩 f:id:ishikawa_pro:20190908222518j:plain 美味しかったです 😋

13:00 ~ 16:00

インスタンス3台使えたので、それぞれ別のインスタンスで作業して、よかったやつを採用しようという方針になりました。

僕は、itemsに紐付くcategoryを取ってくる所がN+1になっているのに気が付いたので、そこを解消することにしました。 具体的には、 getNewItemsgetNewCategoryItems の2つを直しました。

github.com

github.com

かなり時間をかけてしまったので、Goでの実装力の無さを痛感しました。
でも、categoryを取り出すクエリの呼び出し数をかなり減らせたのでよかったです。
before

Count: 100422  Time=0.00s (15s)  Lock=0.00s (2s)  Rows=1.0 (100422), isucari[isucari]@localhost
  SELECT * FROM `categories` WHERE `id` = N

after

Count: 25332  Time=0.00s (3s)  Lock=0.00s (0s)  Rows=1.0 (25332), isucari[isucari]@localhost
  SELECT * FROM `categories` WHERE `id` = N

(そもそもアプリのインメモリに載せればよかったのは内緒 🤫)
この修正で、2,310 イスコイン。
他メンバーはDB周りで色々試みていたようです。

16:00 ~ 18:00

UserSimpleの取得にも N+1があったので、categoryと同様の手法で解消しようと試んでいましたが力及ばずタイムアウトでした。
他メンバーは引き続きDB周りのチューニングを試んでいましたが ko1103 が原因不明の 2,410 イスコインを一回だけだして、結局それが最高記録で終了となりました。

感想

初のISUCON予選に参加しましたが、実力の無さを痛感しました。
特にGoはまだまだ実力不足で、やりたいことをすぐに実装できなかったのは痛かったです。
ただ、当日までに練習したことは確実に活かすことができたと思うので、コツコツ練習してきてよかったです。
会社の研修でやったISUCONの過去問では、全くスコアが上がらなかったですが、今回は自分の修正でちょっとだけスコアを上げることができたので、成長を実感することができました!

一緒に参加してくれた同期やISUCON運営の方々、ありがとうございました!!!
来年もあればリベンジします 💪

mac版Microsoft Edgeがいい感じ

こんにちは。
東京暑すぎですね。
まだ6月ですが、僕はすでに半袖・短パンで出社してます。

何もやる気が出なかったのでとりあえずブログ更新してみるかという感じで書いてます。

特に技術ネタは無いので、会社の普段使いのブラウザをMicrosoft Edgeに変えてみた話です。(フロントエンドエンジニアではないので、技術的な話はほぼないです)

Microsoft Edgeとは

Microsoftが出してるブラウザです。Win10のデフォルトブラウザですね。
Edgeは、EdgeHTMLという独自のレンダリングエンジンを使ったブラウザでしたが、去年の終わり頃に独自のレンダリングエンジンをやめて、chromiumベースのブラウザに移行するという発表がありました。 japanese.engadget.com
Edgeはwindowsにbuilt-inされたブラウザで、updateはwindows updateなどのタイミングでしかできないため、他のブラウザのように、自分のタイミングで更新できないのが辛かったんじゃないか、みたいな噂を聞いたような気がします。
もちろん、他にも色々理由はあると思いますが、、、、

発表から数ヶ月で公開へ

年末ごろにchromiumベースへの移行を発表して、4月にpreview版公開になりました。 japanese.engadget.com
発表から4ヶ月くらいでpreview版公開はすごいスピードですね笑
尊敬します。

使ってみた

ちょっと忙しかったので、実際に使っていませんでしたが、5月の下旬頃から触ってみるかなという気になって、2週間くらい使ってみました。
触ってみた感想は、Edgeのデザインだけど中身はChromeって感じです。 当たり前だろって感じですが笑
しばらく、google chromeとedgeを両方立ち上げて使ってましたが、今どっちのブラウザを表示してるのか、ぱっと見では分からないくらいです。

特徴とか独自機能とか違い

2週間使ってみて感じた、chromeとの違いをあげてみます。

  • 標準の検索エンジンがbingになってる
    設定画面から色々変更できます。(僕はgoogleにしてます笑)
  • 初期の起動画面が完全にEdge
    (これも色々変更できます。)
    f:id:ishikawa_pro:20190609152904p:plain
    macOS版edgeの画面
  • Microsoft Accountでログインする。
    chromegoogleアカウントにsign inして、設定を同期するのと同じ機能があるようです。
    しかし、まだ開発中のようで、ブックマークしか同期できないみたいです。拡張機能などの同期はtoggle switchはありますがdisableになっておりチェックできません。今後に期待ですね。
    f:id:ishikawa_pro:20190609153950p:plain
    edgeのアカウント設定画面
  • Microsoft Defender Browse Protectionが標準搭載
    Windows版Edgeには昔からある、フィッシング・マルウェア対策機能がmac版にも乗っています。chrome extensionが公開されているので、それを使ってるだけだとは思います。
  • Edge用のextensionアプリがある
    Microsoft Edge Insider Addons というchrome web storeのような所からedge用のextensionが追加できます。
    f:id:ishikawa_pro:20190609161835p:plain
    Microsoft Edge Insider Addonsの画面
  • chrome extensionが使える
    chromium ベースなので、普通にgoogle chromeの extensionも使えます。extensionsの設定ページで Allow extensions from other storesを有効にします。 f:id:ishikawa_pro:20190609160052p:plain あとはchrome web storeなりに行って好きなextensionをインストールすれば普通に動きます。今の所不具合なくchrome extensionも使えてます。
  • 言語設定がdefaultで英語になってる
    細かい話ですが、言語設定が英語になっていて複数言語用意されているサイトだと、英語版がリクエストされて表示されます。
    これも日本語へ変更可能です。

ざっとそんなかんじですかね。多分他にも色々あります。
個人的には、chrome extensionが普通に使えるのが、chromeからすんなりスイッチできて良いです。
ただ、まだ若干バグはあって、mediumの記事でコードと画像が表示されないバグとかはありました。(現在は修正済み)
chromeでは表示できてたので、レンダリングエンジンにも手は加えられてるんだろうなと思います。
現在は、まだbeta版は公開されておらず、毎週updateされるDev版と、毎日更新されるCanary版があります。 僕は、Canary版使ってますが、致命的なバグには遭遇することなく便利に使ってます。

Google1強に嫌気がさしてる皆さんはぜひご利用してみてください。
www.microsoftedgeinsider.com