bussorenre Laboratory

hoge piyo foo bar

Google Chrome で「サードパーティーCookieが無効になっています」と表示され、はてなブログにログインできなくなる問題とその対応

対処方法

アドレスバーに chrome://flags/ と入力して、試験運用版の機能をオンにしたりオフにする必要があります。

f:id:bussorenre:20191106132813p:plain

その中の「SameSite by default cookies」を「Disable」にする必要があります。 (変更後は、再起動する必要があります)

f:id:bussorenre:20191106132815p:plain

どうして

SameSite Updates - The Chromium Projects を読むと、 Chrome 78 から、一部のユーザーで 「SameSite by default cookies」の Enable がデフォルトになったとのこと。

経緯は Chromium Blog: Developers: Get Ready for New SameSite=None; Secure Cookie Settings に書いてあります

要するにセキュリティアップデートですね。

技術的な解説は HTTP クッキーをより安全にする SameSite 属性について (Same-site Cookies) | ラボラジアン が参考になります

今後、SameSite=None を指定した場合(クロスオリジンであってもクッキーを送信させたい場合)は、Secure属性の付与も必須になります。

この辺は、サービスを提供している側が対応する必要があるので、現段階でエンドユーザーに出来ることはありません(カスタマーサポートに連絡するくらい?)

早くはてなに対応してほしい所ですが、結構根深い問題なのでM80 (全ユーザーにこの変更が適応される予定のバージョン)までに対応されるかは不明です。 (今の所、はてな公式からアナウンスはないです)

Scala で Functor っぽいものを実際に実装して挙動を追ってみる

こんにちは。 @bussorenre です。今日はScala です。 定型句となって恐縮ですが、「ここは表現がおかしい」とか「間違ってる」とか「もっとこうしたほうがいい」等ありましたら、Twitter で罵倒する前にコメントをいただけると凄く助かります。。。

はじめに

Functor を自分なりの理解で書いてみます。「実際実装してみて挙動を追ってみるか」と言うスタンスの補足解説的な記事です。 eed3si9n さんの独習Scalaz のページを読み解きながら書いたので、先にこちらをご覧になることをオススメします。

eed3si9n.com

また、そもそもFunctor とはなんぞやという解説記事は以下の翻訳ページが参考になりました。

qiita.com

このページでは、上述のページを読んでいて「ん?」ってなったところを、可能な限りScalaで書いて理解を深めていこうと思います。

map

Functor は日本語で関手(かんしゅ)と呼ばれます。Scala で書くとこんな感じです。

trait Functor[F[_]] {
  def map(fa: F[A])(f: A => B): F[B]
}

FunctorF[A]F[B] に変換するmap 関数を定義します。要するにmap が定義できれば Functorということかな…?。入力値 F[A]A => Bに変換する関数を定義します。A と Bは同型でも問題なさそう。Fだと分かりづらいので、具体的な型で実際に例を見てみましょう。以下の例は、F = Option の場合の実装です。

def map[A, B](fa: Option[A])(f:A => B): Option[B] = {
  fa match {
    case Some(a) => Some(f(a))
    case None => None
  }
}

Option の中身が Some なら中の値に対して関数fを適応し、中身が Noneならそのまま Noneを返しています。簡単ですね。

F = Either の場合はこんな感じになりますかね…?

def map[A, B, C](fa: Either[A, B])(f:B => C): Either[A, C] = {
  fa match {
    case Left(a) => Left(a)
    case Right(b) => Right(f(b))
  }
}

この例では右側だけをmap していますが、左側だけをmap する leftMap などもScalaz.\/ には定義されています。

関数へのFunctor

さて、独習Scalaz を読むと Scalaz は Function1 に対する Functor のインスタンスも定義する。 という記述があります。下記コードもそこからの抜粋。

scala> ((x: Int) => x + 1) map {_ * 7}
res30: Int => Int = <function1>

scala> res30(3)
res31: Int = 28

さてどういうことだろうか?と思って、実際にScalaz のFunction.scala を見に行きます。

override def map[A, B](fa: () => A)(f: A => B) = () => f(fa())

Scalaz のどこかに, () を 「なんでも引数指定可な関数」みたいな意味の拡張があるんだろうか…?それともどこかに別の定義があるんだろうか?とにかく、関数のFunctor は、関数の合成を表すことが出来る。 A → B → C という順番で処理を実行してくれる A => C という関数を定義出来るみたいだ…。

lift

「持ち上げ」などと言うが、コレは何をやっているんだろうか? 実際に定義を見に行くとこんな感じになっています。

/** Lift `f` into `F`. */
def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f)

A => B と引数にとって、 F[A] => F[B] という関数を返しています。先程のOption の具体例を引き続き使うと、以下のような感じ

val f = (x: Int) => x + 100

def map[A, B](fa: Option[A])(f:A => B): Option[B] = {
  fa match {
    case Some(a) => Some(f(a))
    case None => None
  }
}

def lift[A, B](f: A => B): Option[A] => Option[B] = map(_)(f)

lift(f)
res0: Option[Int] => Option[Int] = $$Lambda$910/314622131@5a97b17c

res0(Some(100))
res1: Option[Int] = Some(200)

Int => Int という関数を Option[Int] => Option[Int] という関数に変換されました。これにより、先程定義した Int => Int という変換が、Option という文脈で包まれて登場しても問題なく利用できるということになります。 これ、渡すのがOption ではなく関数gだった場合、関数g が一つの文脈 という事になるんだろうか…?

まとめ

Functor の実装を追いながら、実際にOption という具体例で動作を追ってみた。

  • def map(fa: F[A])(f: A => B): F[B]
  • override def map[A, B](fa: () => A)(f: A => B) = () => f(fa())
  • def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f)

個人的にOption がわかりやすいのでOption を使ったけど、 Either でも List でも何でも良いと思う。

TypeScript で Option っぽいものを作ってみる

こんばんは。 @bussorenre です。趣味でTypeScript をはじめました。

Option っぽいもの

Scala には、値が無いことを許容する仕組みとして Option という仕組みがあります。Some(A) は値がある状態。 None は値がない状態を示します。 中の値を取り出すには、get というメソッドを用いますが、None に対してget を実行してしまうとエラーになってしまします。

値があるか無いかわからないけど中の値を操作したい時、 map を使います。

以下に例を示します。

val a: Option[Int] = Some(100) // or None

// a が Some でも None でも実行できる
a.map(x => x + 100)

a match {
    case Some(a) => println(a)    // 200
    case None => println("None")  // None
}

これと似たような物をTypeScript で再現します。あくまで「Option っぽいもの」なので、厳密にはOption ではありません。

案1: 共用体を使う

最初に思いついた方法です。共用体を使います。共用体は代入可能型を絞ることができます。

let a: number | null = 100
a = null // 問題なし
a = "hogehogehogehoge" // エラーになる

これを用いて、nullable な変数は全部 A | null にしてしまえばいいなじゃないかと思いつきます。しかし、毎回値が有効か、null かをif 文でチェックする必要があり、面倒です。

let a: number | null = 100 // or null
if ( a === null) {
    // error
}
console.log(a)

案2: もうちょっといい感じに共用体を使う。

interface を用いて、Some型とNone 型を定義し、それらを type で 共用型にします

interface Some<A> {
  type: 'some';
  value: A;
}
interface None {
  type: 'none';
}
type Option<A> = Some<A> | None;

これは比較的良さそうです。Option<A>Some<A>None を持ちません。しかし、Option.map みたいな芸当ができません。無理やりmap 関数を作るとすればこんな感じになりそうです。

function map<A, B>(obj: Option<A>, f: (obj: A)=> B): Option<B> {
  if (obj.type === 'some') {
    return {
      type: 'some',
      value: f(obj.value),
    };
  } else {
    return {
      type: 'none',
    };
  }
}

案3: 素直にクラスを使ってみる。

Option<A> というインターフェースを用意し、そこから派生させたインターフェース Some<A>None を定義します。また、それぞれ Some<A> を継承したクラスSome<A>を定義し、実際のmapget の挙動も定義します。None も同様です。

interface Option<A> {
    get(): A | null
    map<B>(f: (a: A) => B): Option<B>
}

interface Some<A> extends Option<A> {
    value: A
    get(): A
}

interface None extends Option<null> {
    get(): null
}

class Some<A> implements Some<A> {
    constructor(value: A) {
        this.value = value
    }
    get(): A {
        return this.value
    }
    map<B>(f: (a: A) => B): Some<B> {
        return new Some(f(this.value))
    }
}

class None implements None {
    constructor() {
    }

    get(): null {
        return null
    }

    map<A, B>(f: (a: A) => B): None {
        return new None
    }
}


let a: Option<number> = new Some(100)
console.log(a.map( x => x + 100))    // Some(200)

let b: Option<number> = new None
console.log(b.map( x => x + 100))    // None


console.log(new Some(100).map(x => x + 100))    // 200
console.log(new None().map( (x: number) => x + 100))    // new None.map はできない。x の型を明示しないとコンパイルエラー

get ができて、map が出来る、Option っぽいものができました。めでたしめでたし。

最後に

決り文句的で恐縮ですが、「もっとこうしたらいい」とか「ここ間違っているよ」等があれば、ご指摘いただけると幸いです。

技術書典7で定価よりお安く頂いた「実践TypeScript - BFF と Next.js & Nuxt.js の型定義 吉井健文著」が非常にわかりやすくて勉強になっています。良書をありがとうございます。

追記

プロトタイプを使うことにより、更にScala のOption っぽいものに近づけることが出来るみたいです。勉強になりました…。 https://github.com/AlexGalays/spacelift/blob/master/src/option/index.ts

ISUCON9 に参加し惨敗しました

お久しぶりです。 @bussorenre です。

前々から存在は知っていたものの、なかなかプライベートの予定が合わず参加出来なかったISUCON にようやく初参加出来ました!! 結果は、最終スコア2910 (初期スコア 2100くらい)で、まぁものの見事に惨敗です 😇

やったことの振り返りと、次回参加とかに向けての反省をば

チーム構成

会社のメンバーと組みました。と言っても普段業務で一緒に仕事をしているメンバーではなく、全く別のプロダクト開発に従事ししているメンバー、つまりほぼほぼ「はじめまして」のメンバーでチームを組みました。 メンバー全員にruby 知見があるので、使用言語はruby に即決。それぞれの担当としては以下の通りです。

  • @Yoshimaru46
  • @Miyamizu
    • アプリケーションの改善
  • @bussorenre
    • 計測環境の整備やデプロイの自動化。ミドルウェアのチューニング等

@Miyamizu 氏が9月入社のメンバーで、@Yoshimaru46 氏がそのメンターということもあり、二人のペアプロ体制が最初に出来上がり、そこに私が乗っかった形のチーム構成です。

事前準備

チームが決まったのが8月最終日くらいだったので、事前準備にあまり時間を割けなかったのですが、就業後の時間等を利用し、物理的に集まって作戦会議等をしていました。

作戦会議で決めたことは以下のとおりです。

  • 目標
    • isucon に挑戦する(0次予選を突破する = 起床して課題と向き合う)
    • 0点を回避する(必ずスコアが出る状態で終了する)
    • 出来ることを確実にやる

初挑戦から優勝を狙いに行く強者もいますが、我々の実力的に(準備期間的にも)まずそれは難しいだろうということで、「わからないことに挑戦すること」を最も重要な目的としました。 雑に「絶対優勝すっぞ!!」とか言うと、圧があってかえって挑戦するマインドを萎縮させかねないので、「まぁ楽しもうぜ」ということを、共有しました。

一方で、出来ることは必ずやるようにしようという目標も設定しました。具体的には以下の事を必ずやるようにしようと事前に決めました。

  • アプリケーション系
    • index をしっかり貼る
    • N+1問題の解消
    • SQLの並列化
    • 画像をバイナリでDBに持っているのをやめる
    • 可読性低いコードをやめる
  • 計測系
    • スロウクエリの自動検出
    • デプロイの自動化
    • nginx のログをalp で統計する
  • ミドルウェア
    • nginx じゃないhttpサーバーが来たらnginx にする(わからないから)
    • DBロックの調査
    • Cache Control
    • タイムアウト/キャッシュのの設定を適切に
    • Worker数を適切な数に

また、alibaba Cloud という普段使い慣れないクラウドが舞台なので、確実にインスタンスを立ててssh 出来るようにする練習を行いました。

予選開始前から、解くべき課題を見つけるくらいまで

予選当日は9時過ぎくらいに会社に集合し、設営を行い、9:50分ごろから事前ブリーフィングを行いました。

まずは午前中にやることを共有し、私は以下のことを担当することになりました。

  • 起動しているサービスを確認する service --status-all
  • 全ての情報をgithub に乗せる
    • application (全実装)
    • conf(nginx や mysql など)
  • ↑で載せた情報をシンボリックリンクにする
  • デプロイ自動化
  • alp の構築
  • 全体的に基本的なnginx.conf の見直し

私がこれらの基本的な設定に取り組んでいる間に、他二人のチームメンバーには課題を読み込んでもらい、N+1等、解決できそうな問題を発見してもらうことにしました。

諸々の準備が整ったのが11:52分。その頃には「どうやら /users/transactions.json あたりがN+1でヤバそう」という情報がアプリケーションコードからも、alp の統計からもう読み解くことが出来ました。

昼食。とりあえず寿司を発注する

メインイベント このあたりで、絶対集中力が尽きるだろうと思っていたのと、前日のチームが寿司を発注しているのをTLで観測したのを受け、「これはもう寿司頼むしかねーな!」という感じで頼みました。 発注から1時間足らずで届いたのにはびっくり。銀のさら様様です。

f:id:bussorenre:20190908130418j:plain

昼食を取りながら、お互い次に取り組むことを決めますが、どうもアプリケーション側にボトルネックが多いと判断し、私も午後からアプリケーション側を見ることになりました。 予定ではnginx の最適化とか、せっかくインスタンス三台使えるんだからDBとapp 分けるかとか考えていたのですが、アプリケーションを改善しないとインスタンス分離してもそもそもスコアが出ないだろうという判断の元決定しました。

午後:地獄のN+1潰し

このあたりで普段の業務を如何にライブラリや既存の仕組みにに乗っかかってるだけか反省することが多かったです…… 😇 join を使ってN+1を解消するのに結構時間がかかりました。普段如何にORMに頼っているかわかりますね。

あーでもないこーでもないと3人で言いながら /users/tranzaction.json/new_items のアクセスを早くした頃にはもう16 時でした……。

「N+1潰したからベンチ上がるやろ!」と思い意気揚々とベンチを回すも、上述の「2910」までしかスコアが出ず 😇 改めてalpを確認すると、たしかにそこはかなり改善しているのですが、今度は /buy のレスポンスが遅く、更に殆どのリクエストが4xx系のエラーを返していることに気が付きます。

このとき今更「そういえば、今回のスコアは商品の売上合計だったよな」ということに気が付き、よりたくさん商品を売りさばくチューニングをしなければ行けなかったことに気が付きます。問題の趣旨の理解が出来たのが16:30頃

必死にbuy の改善に取り組むも、「外部APIの仕様わからん」「なんでこんな403ばっかり返しているのかわからん」等と言ってるうちに18時となりゲームセット。 (※ 最後はポータルの不具合で18:10 まで時間が延長になりましたが…)

最終スコアは2910でした。

反省

大きく2つで、

  • 焦らずもっと課題とドキュメントを読み込む事
  • 環境構築系のスクリプトをもっと事前に用意しておけばよかった事

が課題でした。特に後者は、「本当に解くべき課題がなにか」を理解するのに遅れたという痛恨のミスです。

作問担当がメルカリということで、メルカリかメルペイっぽいアプリが来るだろうなーという予想を立てていたんだから、メルカリっぽいサービスを適当に作ってみるとかをしても良かったかなと思います。 この辺は実装経験が差をつけるかなと。

後者に関しては↑の課題理解をより早く行うためにある課題だと思っていて、構築系の自動化・高速化はもうちょっと備えていきたいかなという所です。

今回、チームメンバー全員がisucon 初挑戦という非常にフレッシュなチームで、個人的にはかなり楽しくさせていただきました。 チームメンバーの Yoshimaru46氏 と Miyamizu氏には改めて感謝を。そしてまたぜひ来年一緒に挑戦して予選突破しましょう!!笑

最後になりましたが、isucon 運営の皆様、非常に楽しい大会を提供していただきありがとうございました!!

tmux 2.9 にバージョンアップしたときのconf の変更等

普段使っているMacbrew を更新し、いくつかのパッケージを最新にしたら、tmux から以下のような警告が出るようになった。

.tmux.conf:65: invalid option: status-attr
.tmux.conf:68: invalid option: window-status-fg
.tmux.conf:69: invalid option: window-status-bg
.tmux.conf:73: invalid option: window-status-current-fg
.tmux.conf:74: invalid option: window-status-current-bg
.tmux.conf:78: invalid option: pane-border-fg
.tmux.conf:79: invalid option: pane-active-border-fg
.tmux.conf:82: invalid option: message-bg
.tmux.conf:83: invalid option: message-fg

調べてみると、tmux 2.9 から option の指定が一部変更になったらしい。 以下のように変更すると治った

 # default statusbar colors
-set-option -g status-bg colour236 #base02
-set-option -g status-fg colour136 #yellow
-set-option -g status-attr default
+set-option -g status-style bg=colour236,fg=colour136
+#set-option -g status-style default

 # default window title colors
-set-window-option -g window-status-fg colour244 #base0
-set-window-option -g window-status-bg default
+set-window-option -g window-status-style fg=colour244,bg=default
 #set-window-option -g window-status-attr dim

 # active window title colors
-set-window-option -g window-status-current-fg colour166 #orange
-set-window-option -g window-status-current-bg default
+set-window-option -g window-status-current-style fg=colour166,bg=default
 #set-window-option -g window-status-current-attr bright

 # pane border
-set-option -g pane-border-fg colour235 #base02
-set-option -g pane-active-border-fg colour240 #base01
+set-option -g pane-border-style fg=colour235
+set-option -g pane-active-border-style fg=colour240 #base01

 # message text
-set-option -g message-bg colour235 #base02
-set-option -g message-fg colour166 #orange
+set-option -g message-style bg=colour235,fg=colour166

個人の思考の整理に良いと思い、再び Scrapbox を使い始めた

「この記事は個人の見解であり、所属する組織の公式見解ではありません」

最近、個人でScrapbox を使うようになったので、僕もユースケースを紹介します。

Scrapbox の特徴としては、

  • リアルタイムでの複数人編集
  • タグ付けによるリンクの自動生成
  • 独自記法によるリッチテキストエディットが可能

と言った感じでしょうか。

当初は Scrapbox がそこまで良いと思っていなかった

去年くらいに、社内で @mactkg 氏にコレが良いぞと紹介してもらったのですが、そのときはあまり自分には刺さりませんでした。

刺さらなかった理由としては

  • scrapbox記法という独自記法を覚えるコスト高くね??
  • markdown で書けるDropbox pager のほうが良くね??
  • プロジェクト管理用のwikiなら Confluence でよくね?
  • 情報発信(アウトプット)なら、Qiita or ブログでよくね?
  • 作業メモとか雑なやつは手元のメモ帳(当時はSublime Textを使ってました)を使えばよくね?

などなど、当時イメージしていた使い方だと代替手段があり、良いユースケースが思いつかないままでした。

先人の取り組みを見て行ける気がした

しかし、1年間社内や社外で様々な使い方を見ているうちに、「思考の整理ツールとして使えるな。」と思い、使い直すことにしました。

参考にしたのは下記リンクです。

4つめの記事は、「個人の思考の整理」ではなく、「チームの思考の整理」として使えている例な気がします。

中途半端なアウトプットを中途半端なまま出力できる

自分の「知識の獲得や、アウトプット」と行った活動を見直した際、今まで私が取っていた行動として

  • 物理的な紙(ノートなど)に書き留める
  • ブログやqiita にまとめて公開する

の大きく2つの方法があったのですが、前者は手がしんどい。そもそも字が汚い。オンライン共有できない。とデメリットが多いです。

ブログやqiita にまとめるのは、整形にすごい手間がかかり、雑な事を書くとブラド・ツェペシュもビックリするくらい串刺しにされる。という理由で、アウトプットに時間がかかり、結果としてアウトプット頻度が減っていく傾向にありました。

そこで、「Wiki」がいいんじゃね?と思って個人のAWS上にnginx を立てて、さてエンジン何にしようと考えてたところ、「あ、そういやScrapbox いいんじゃね?」ってことで思い直して今に至ります。

もともと、同等の機能として、DropboxPaper を使っていたのですが、諸々の理由でDropbox を捨てることにしたので、Scrapbox が残ることになりました。

Scrapbox の最も良い点としては、やはり雑な状態で雑にアウトプット出来る。という点でしょうか。 今回のこの記事も、ある程度雑な状態で二ヶ月くらい寝かせておき、まとまってきた段階で、「よし、整理して公開するか」となったので、アウトプットが腐らずに済みます。

実際のscrapbox はココにあります。 れんれぼっくす

f:id:bussorenre:20181221190306p:plain
れんれぼっくす

今はまだ私のれんれボックスは、自分と、もうひとりにしか編集権限を渡していないのですが、少し整備が進むと、編集権限はクローズドだけど情報はオープンみたいなwiki が作れるような気がしています。

結論:Scrapbox 良いぞ

Scrapbox に対する不満がないわけではありません。特に、markdown 互換が無いのがやはり厳しいです。 しかし、それを補ってあまりある力を今のところは感じており、アウトプットに悩んでいる人には良いツールだと思ってます。

何かの参考になればぜひぜひ。