javascript の Promise おぼえがき

AWS lambda で Node.js と向きう合う必要が出てきたので、最近のモダンな JS を理解する

最近は async/await が使えるらしいが、内部的には Promise を使っているらしいので、まずは Promise から理解する

Promise とは?

非同期処理の実行結果(成功 or 失敗) の受け取り口を用意して、非同期の管理を容易にするためのオブジェクト

コールバック地獄のしんどさを回避するために生まれた

つかいかた

new Promise する

  • 第一引数は、引数が2つある関数
    • 関数の第1引数には、正常終了時に呼び出す関数
    • 関数の第2引数には、異常終了時に呼び出す関数
  • new Promise の戻り値は、then()catch() を持つオブジェクト
    • then() には正常終了時に呼ばれる関数を渡す
    • catch() には異常終了時に呼ばれる関数を渡す
      - 例外を勝手に拾ってくれたりはしないので、自前で例外をコントロールする必要がある
      
  • then()catch() が呼ばれた時点で、promise に渡した関数が実行される

正常終了例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 10);
});

promise.then((value) => {
  console.log("then");
  console.log(value);
}).catch((value) => {
  console.log("catch");
  console.log(value);
});

出力結果

then
foo

異常終了例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('foo');
  }, 10);
});

promise.then((value) => {
  console.log("then");
  console.log(value);
}).catch((value) => {
  console.log("catch");
  console.log(value);
});

出力結果

catch
foo

Promise のチェーン

then() の戻り値を Promise オブジェクトにすることで、Promise をチェーンできる

これにより、非同期処理を同期的に実行できる

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('promise1');
  }, 10);
});

promise1.then((value) => {
  console.log("then 1");

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise2');
    }, 10);
  });

}).then((value) => {
  console.log("then 2");
});

出力結果

then 1
then 2

Promise#all

複数 Promise の並列実行ができる

正常終了例

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise1')
    resolve('promise1');
  }, 10);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise2')
    resolve('promise2');
  }, 10);
});

Promise.all([
  promise1, promise2
]).then((value) => {
  console.log(value)
  console.log('finished')
});

then() には、各 Promiseresolve の引数を配列にしたものが渡される

promise1
promise2
[ 'promise1', 'promise2' ]
finished

異常終了例

1つでも reject() が呼ばれたら catch() が呼ばれる

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('promise1');
  }, 10);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('promise2');
  }, 10);
});

Promise.all([
  promise1, promise2
]).then((value) => {
  console.log(value)
  console.log('finished')
}).catch((value) => {
  console.log(value)
  console.log('catch')
});

出力結果

promise2
catch

reject() が複数発生する場合も、早いもの勝ちで最初の reject() の引数が渡される 処理自体は最後まで行われる

Promise.race()

複数 Promise のうち、1つでも処理が完了したら then() が呼ばれる

初心者向けNoSQLおぼえがき

RDBとの違いとかあんまりわかっていなかったのでメモ

RDBの限界

スキーマの限界

  • 多種仕様なデータや、ネストが深いデータのテーブルを設計するの大変
  • 最近の高ベロシティの要望で、それを高頻度に変更していくのはもっと大変
  • そもそもJSONやXMLでもらったデータとか、データ構造が不定だったりすると、テーブルに乗っけられない

スケールアウトの限界

  • 複数マシンでRDBを管理するの大変
    • レプリケーション大変
    • リードレプリカつくるとか、テーブル分割とか手もなくはない
  • k8sのような分散システムを想定した設計ではない

そこでNoSQLですよ

NoSQL = Not Only SQL

NoSQLの特徴

  • スキーマ定義なしでデータの保持が可能(スキーマレス
  • 速度やスケーラビリティ重視
    • その分、 リレーションを持たない
    • その分、 トランザクションがない
      • 最近はトランザクションをサポートしているNoSQLも多い

NoSQLといっても色々ある

  • KVS
  • ドキュメントDB
  • グラフDB

今回は、KVSと、ドキュメントDBについて解説

KVS

Key Value Store

  • 名前の通りキーと値の1対1のデータ構造

特徴

シンプル

なので大量データ読み書きや分散システムに向いている

  • データ読み書きが高速
  • 水平展開が簡単

複雑なことはできない

  • データ構造は、単一値か配列くらいまで
  • なので、複雑なデータ処理はできない

使いみち

  • webアプリのセッションストア
  • RDBMSのキャッシュ(SQLをキーにする

主な製品

  • redis
  • memcached
  • Amazon ElastiCache
  • google BigTable

ドキュメントDB

保存するデータ構造が自由なデータベース

  • 大抵はJSONやXMLでデータを保存する

特徴

スキーマレス

  • 複雑な階層構造を意識してスキーマを設計しなくて良い
  • 頻繁な構造の変更に追従しやすい

KVSよりは、複雑なデータの保持や検索ができる

使いみち

  • 複雑な階層構造のデータの保持(電子カルテ、色々な情報が付く商品データなど)
  • 頻繁に仕様が変わるデータの保持(API、高ベロシティのアプリ開発とか)

デメリット

  • 全文検索が弱い(らしい)
  • 並列展開しない場合、RDBよりも検索速度が遅い
  • 結合に制約があるため、あまり複雑な検索はできない

主な製品

  • MongoDB
  • Amazon DynamoDB

どういうふうにRDBと使い分けるの?

それぞれ特徴を生かしている分、メリット、デメリットも明確なので、要件に応じて使い分けるのが得策

  • 超大量のデータをすばやく制御したい場合は、NoSQL
  • データのキャッシュはKVS、複雑なデータの保持だけドキュメントDB
  • 逆に複雑なクエリ、明確なスキーマ、トランザクションが重要な場合はRDB

MySQLやPostgreSQLもドキュメントDBやKVSをサポートしているので、一部テーブルだけそちらに移行するのも良い

  • NoSQLもRDBMSも、それぞれお互いの機能をどんどん追加している段階なので、最新の情報をもとに決めたほうが良い

Amazon S3 ストレージクラスおぼえがき

概要

以下の4つのストレージクラスがある (下に行くほど制限があるが安くなる)

  • 標準
  • 標準 – 低頻度アクセス
  • 1 ゾーン – 低頻度アクセス
  • Amazon Glacier

向いているデータ一覧

標準

以下のいずれかのデータ

  • アクセス頻度高
  • SLAやデータの耐久性が重要
  • 超細かいデータが大量にある場合
  • 30日以上保持しないデータ

標準 – 低頻度アクセス

  • アクセス頻度が低く、SLAやデータの耐久性が重要なデータ

1 ゾーン – 低頻度アクセス

  • アクセス頻度が低く、SLAやデータの耐久性がそこまで重要ではないデータ

Amazon Glacier

  • アーカイブ用。取り出しに時間がかかっても良いデータ

早見表

料金

東京リージョンの場合

  ストレージ料金 データ取り出し料金 PUT、COPY、POST料金 GET、SELECT料金
標準 0.025USD/GB 無料 0.0047USD/1000req 0.00037USD/1000req
標準 – 低頻度アクセス 0.019USD/GB 0.01USD/GB 0.01USD/1000req 0.001USD/1000req
1 ゾーン – 低頻度アクセス 0.0152USD/GB 0.01USD/GB 0.01USD/1000req 0.001USD/1000req
Amazon Glacier 0.0005USD/GB 後述 後述 後述

その他特徴

  可用性 最小料金サイズ 最小利用期間
標準 99.99% なし なし
標準 – 低頻度アクセス 99.9% 128KB 30日
1 ゾーン – 低頻度アクセス 99.5% 128KB 30日
Amazon Glacier なし なし 90日
  • 可用性: 1年に可動する論理上の値(SLAはもう一桁低い)
  • 最小料金サイズ: 128KBなら、それ以下のオブジェクトでも、128KBとみなされて課金される
  • 最小利用期間: 30日なら、それ以下のオブジェクトでも、30日とみなされて課金される

標準

アクセス頻度が高く、SLAが重要なデータ
もしくは超細かいデータが大量にある場合
もしくは30日以上保持しないデータ

特徴

  • 取り出し料金は無料
  • 最小料金サイズなし
  • 最小利用期間なし
  • リクエストコストは安い
  • ストレージコストは高い

標準 – 低頻度アクセス

アクセス頻度が低く、消えたら困るデータ向け

  • 標準IA(Infrequent Access)とか呼ばれることもある

注意事項

  • リクエストが高頻度だと、場合によっては標準より高コスト
  • 128KB以下のデータを大量に保持する場合、場合によっては標準より高コスト
  • 30日以下しか保持しないデータを大量に保持する場合、場合によっては標準より高コスト

特徴

  • 取り出し料金は有料
  • ストレージコストは安い
  • リクエストコストは高い
  • オブジェクトが128KB以下の場合も、128KBとみなされて課金される
  • オブジェクトの保持期間が30日以下でも、30日とみなされて課金される

1 ゾーン – 低頻度アクセス

アクセス頻度が低く、SLAやデータの耐久性がそこまで重要ではないデータ

注意事項

標準 – 低頻度アクセスと同じ

特徴

アベイラビリティゾーンが1つで運用される
そのため、アベイラビリティゾーンが破壊された場合データは失われる

Amazon Glacier

アーカイブ用。他のS3と異なり、取り出しに非常に時間が掛かる

  • データをテープやDVDなどのコールドストレージに保持しているらしい

取り出し方式が、標準 大容量 迅速 の3つあり、それぞれ特徴が異なる

特徴

  取り出し 料金 制限
標準 3〜5H 標準 なし
大容量 5〜12H 安い なし
迅速 1〜5min 高い 250MB以内

低冗長化ストレージ

現在非推奨

今回の価格改訂により現時点ではスタンダードストレージを選択した方がお客様にメリットがある状態になっています

aws.typepad.com

  • 標準の仕様だが、データの耐久性は 99.99%
  • Reduced Redundancy Storageの略

参考

aws.amazon.com

Amazon CloudFront おぼえがき

AWS 認定ソリューションアーキテクト – プロフェッショナル 対策

Amazon CloudFront って何?

Amazon CloudFront は、データ、動画、アプリケーション、および API を、低レイテンシーの高速転送により視聴者に安全に配信する、グローバルコンテンツ配信ネットワーク (CDN) サービスです。

https://aws.amazon.com/jp/cloudfront/

特徴

  • 元サーバに負荷をかけずに、サイトの高速化とスケーラビリティを簡単に実現可能
  • 動的コンテンツにも対応

用語

オリジン

元ネタのコンテンツがあるサーバ

  • インターネット経由でアクセスできることが必須

エッジサーバ

コンテンツをキャッシュ & 配信するサーバ。

  • CloudFront DNSが位置情報を元にユーザを最も近いエッジサーバに誘導する

CloudFront Distribution

  • CloudFrontの各設定のひとまとまりをDistributionという
  • Distributionには、 *.cloudfront.net のようなドメインが与えられる
  • web Distributionと、RTMP Distribution がある
    • 通常は web Distribution を使う
    • 以前は「ダウンロード」と「ストリーミング」という名称だった

RTMP

Real Time Messaging Protocol

Adobe Flash プレーヤーとサーバーの間で、音声・動画・データをやりとりするストリーミングのプロトコル。(らしい

いろいろな機能

キャッシュコントロール

  • 単一ファイルサイズのキャッシュは 最大20GB まで
  • URL/リクエスト(GET,HEAD,OPTION)/フォワードオプションの 完全一致 でキャッシュが利用される
    • フォワードオプションは、ヘッダ、Cookieの値、クエリストリングを参照できる
  • キャッシュを無効化するPathを指定できる(最大3000)
  • ワイルドカード指定も可能(最大15個

ダイナミックキャッシング

Behavior Cache TTL(正規表現)を設定することで、path毎にどのオリジンにアクセスするか設定できる

  • 静的コンテンツはS3、動的コンテンツはサーバを参照みたいな使い方を想定

AWS Lambda@Edge

エッジロケーション上でLambdaを実行できる仕組み(すごい!!

使いみち

  • 動的なレスポンス
  • ログ出力
  • アクセスコントロールなど

Price Class

設定すると安価なエッジロケーションのみ使用するようになる

  • 当然その分遅くなる

その他の機能

  • AWS ShieldによるDDos対策(無料
  • エッジ側でコンテンツをGzip圧縮できる

具体的な使い方

プライベートコンテンツの配信

ストリーミング動画配信

  • AWS Elemental MediaConvert でHLSなどのストリーミングプロトコルの動画に変換して、それを配信する
    • 変換結果はS3にupされる
  • HLSのマニフェストで、CloudFront Distributionを指定
    • HLSはマニフェストファイル(m3u8)と、動画ファイルに分かれている

ライブストリーミング動画配信

EBSスナップショットおぼえがき

EBSスナップショットの料金について

0.05USD 1 か月に格納されたデータ 1 GB あたり

差分が保存されるため、複数回スナップショットを取った場合、2回目以降は差分のサイズだけの料金になる

Amazon EBS では空のブロックが保存されないため、スナップショットのサイズはボリュームサイズよりもかなり少なくなるはずです。 ボリュームの最初のスナップショットについては、データ全体のコピーが Amazon S3 に保存されます。 増分スナップショットごとに、Amazon EBS ボリュームの変更部分のみが保存されます。

料金 - Amazon Elastic Block Store(ブロックストレージ)|AWS

EBSスナップショットとAMIの違い

AMI = 「EBS ボリュームの中のデータ(スナップショット) とインスタンスを構成する管理情報」を含む起動テンプレート

EBSスナップショットの取得を自動化したい

Amazon Data Lifecycle Manager (Amazon DLM) を使用して、Amazon EBS ボリュームをバックアップするスナップショットの作成、保持、削除を自動化できます

docs.aws.amazon.com

マイクロサービスおぼえがき

マイクロサービスについて知っておいたほうが Kubernetes 登場の背景を知れそうな気がしてきたのでメモ

成長したシステムを大きなチームで運用するのは大変

いろんな課題がある

チームが大きいと...

  • コミュニケーションコストが高い
  • 意思決定コストが高い

システムが大きいと...

  • 変更コストが高い
    • 影響範囲が見えづらい
    • 「歴史的経緯」が増えて学習コストが上がる
  • テストやビルドに時間がかかる
  • 最初に決定した言語、フレームワークに不向きな機能が
  • インフラリソースを一部のリソースを食う機能のために合わせなければならない

そこでマイクロサービス化ですよ

チームとサービスを分割して効率化させる

チームが小さいと...

  • コミュニケーションコストdown
  • 意思決定コストdown

システムが適切に分割されていると...

  • 各サービスの変更速度up
    • 歴史的経緯の消滅 & 影響範囲が限定的
    • 機能に最適化された、言語やフレームワークの選択
  • テスト/ビルド時間の低下
  • インフラリソースの効率化
    • 各サービス毎に必要な部分だけスケールできる

ということは、最初からマイクロサービスじゃなくてよいの?

そう。

1つのチームが複数のサービスを管理するのはむしろ効率が悪い

それに、ある程度サービスが使われないと、分割の勘所も分からない

マイクロサービスにする際に気をつけることは

各チームに責任・権限をしっかり分担すること

  • コミュニケーション&意思決定速度を上げるため

データもサービス毎に分割すること

ここをしっかり分けないと、マイクロサービス化する意味は薄い

大前提としてサービスをまたぐトランザクションが発生しないようにサービスを分割すべき 今回のように同一トランザクションでデータ更新をしたい処理であれば、サービス同士が疎結合ではないということであり、そもそもサービス分割の粒度が正しくない可能性が高い

各サービスが落ちていても動くように疎結合に設計すること

可用性が高くなる

でも、レコメンデーションエンジンの部分が独立して動いていれば、UI部分でレコメンデーションエンジンサービスの故障を検知したら、お薦めリストを表示しないというのもできるし、お薦めのデフォルトを呼び出すような事も可能になる。 こういう依存先サービスの故障を切り離すような機能をサーキットブレーカーと呼ぶ

ある程度の実装の重複は許容すること

下手に共有ライブラリとか作ると、その部分の変更コストが上がる

Cacoo の場合は gRPC サービスや RabbitMQ に関連する処理などがその典型です。これは microservices の特性上、許容すべきことです。共通の処理をライブラリ化してしまうという手もありますが、そうすると複数のサービスが同一のコードに依存してしまうため、microservices のメリットが減ってしまうことになります。

CacooはなぜKubernetesによるmicroservicesへの道を選んだのか? | ヌーラボ

マイクロサービスの欠点はあるの?

いくつかある。

  • サービス間通信が複雑化する
  • 各サービスが独自のデータベースを持つのでサービス間でデータの一貫性がなくなる
  • アプリケーション全体の可用性が高まるとは限らない(1つのサービスの挙動が悪くなると全体に影響するケースもある
  • 統合テストが大変になる

ASCII.jp:マイクロサービスの境界を決める「DDD」とは? (1/2)

あと、複数サービスが通信する関係上、モノリシックなサービスよりレスポンスは遅くなる

適切にサービスを分割するにはどうしたら良いの

ここが一番マイクロサービス化で難しいところらしい

DDDの「境界づけられたコンテキスト」単位で分けるのが適切らしいです(あんまりわかっていない

DDD本やマイクロサービスアーキテクチャを読むと理解できるのかも...?

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ

マイクロサービスを動作させるのに適切な方法は何?

各サービスの機能をコンテナ化して、サービス毎のコンテナの集まりを管理するのがモダンらしい

これを実践するのに生まれたのが Kubernetes であるという認識

感想

  • マイクロサービス化は、ハイコストハイリターンの「究極的な技術的負債の返済」だと感じた
  • 当たり前だけれど「銀の弾丸」ではない
  • サービス起動時から予め「このくらいの規模になったらマイクロサービス化を実施する」というのを経営陣と共有しておかないと難しそう

次回

マイクロサービスを理解したところで、それを実際に動作させるプラットフォームとしての Kubernetes を理解する

CircleCIのimage circleci/ruby:*-node-browsers が PhantomJS をサポートしなくなった

f:id:kasei_san:20180919124810p:plain

Docker Public Convenience Images Update: Firefox - Announcements - CircleCI Community Discussion

応急処置

circleci/ruby:*-node-browsers-legacy を使う

やるべきこと

PhantomJSはサポートが終了しているので、Headless Chrome とかにさっさと移行しましょう

jser.info