俺、サービス売って家買うんだ

Swift, Kotlin, Vue.js, 統計, GCP / このペースで作ってればいつか2-3億で売れるのがポっと出来るんじゃなかろうか

MySQLで特定日からの連続する日付の一覧を作る

f:id:ie-kau:20180616033236j:plain

MySQLで分析するときに、連続する日付の表を仮想的に組み立てたいときってありますよね。

SET @start_day = '2017-04-01';
SELECT DATE_FORMAT(DATE_ADD(@start_day, INTERVAL tmp.series DAY), '%Y-%m-%d') AS day
FROM
(
    SELECT 0 series FROM DUAL WHERE (@num:=-1)*0 UNION ALL
    SELECT @num:=@num+1 FROM `information_schema`.COLUMNS
) AS tmp
HAVING DATE_FORMAT(day, '%Y-%m-%d') <= DATE_FORMAT(CURRENT_DATE(), '%Y-%m-%d');

結果

f:id:ie-kau:20181021003440p:plain

ちなみに

BigQueryだともうちょい簡単

SELECT
  date
FROM UNNEST(GENERATE_DATE_ARRAY('2017-04-01', CURRENT_DATE())) AS date
ORDER BY date;

SQLアンチパターン

SQLアンチパターン

MySQLでのユーザー管理系コマンドまとめ - 追加・権限付与 -

f:id:ie-kau:20180616033236j:plain

行う機会が少なくてどうしても覚えられないユーザー管理系のコマンドまとめ!

ユーザー追加

CREATE USER hazumu

パスワード追加

SET PASSWORD FOR hazumu = PASSWORD('パスワード')

権限確認

SHOW GRANTS FOR hazumu

SELECT権限を与える

GRANT SELECT ON DB名.* TO hazumu

まとめてやる

GRANT ALL ON DB名.* to hazumu@ホスト名 IDENTIFIED BY 'パスワード'

ユーザー一覧確認

SELECT Host, User FROM mysql.user;

ユーザー削除

DROP USER hazumu@ホスト名

詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)

詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)

どこで起こったかわからない unhandledRejection のコールスタックをキャプチャする

f:id:ie-kau:20161103231112p:plain

すいませんorz...

2017年後半〜2018年前半ブチクソ忙しくて全くブログの更新ができていませんでした!!!!!!!

facebookとかではちまちま発言してるのですが、会社でのサービスづくりに熱中しておりこちら側がおろそかになっていました。
そろそろ復活させるぞ!!!!

ということで復帰記事ですが完全メモ書きです。

Node / Expressでサーバーを運用していると↓みたいな感じで try catch せずに投げ捨てた非同期処理内でエラーを起こしてしまい UnhandledPromiseRejectionWarning が出力されただけで、どこでエラーが発生したかよくわからない状態になってしまうことがあります。

(node:15344) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 10): ReferenceError: hoge is not defined

これでいうと hogeの ReferenceErrorどこでおこっとるんや!!と。

そういうときは、

process.on('unhandledRejection', (reason, p) => {
    console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
})

上記のように、unhandledRejection のイベントをキャプチャしておけば

Unhandled Rejection at: Promise Promise {
  <rejected> ReferenceError: hoge is not defined
    at type (/home/develop/fugafuga.js:725:18)
    at router.post (/home/develop/routes/hogehoge.js:500:7)
    at <anonymous> } reason: ReferenceError: hoge is not defined
    at type (/home/develop/fugafuga.js:725:18)

という感じでコールスタックを取得することがでるようです。

async/awaitが手になじむまで色々書いてみるよ〜

f:id:ie-kau:20170930235521p:plain:w500


現在運用中のプロジェクトのNodeのバージョンを7から8にあげたのでPromiseで書いていた箇所を徐々にasync/awaitに置き換えています。

簡単に書ける一方でPromsieに慣れすぎていたため、「これ並列処理になるんだっけ」とか、「てか、これ動く?」見たいなレベルで手になじまなかったので色々ためして馴染もうと思います。

利用するタイマー関数

function timer(ms, name) {
  console.log(`name: ${name} start!`)
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(name), ms)
  })
}

Case.1

  • awaitを書いて非同期処理の関数を並べて書く
(async () => {
  const result1 = await timer(10000, 'a')
  const result2 = await timer(10000, 'b')
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.2

  • awaitを書いて非同期処理の関数の戻り値でデストラクチャリング
(async () => {
  const [result1, result2] = [await timer(10000,'a'), await timer(10000, 'b')]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.3

  • Promise.allをawaitしてデストラクチャリング
(async () => {
  const [result1, result2] = await Promise.all([timer(10000, 'a'), timer(10000, 'b')])
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

Case.4

  • 先にpromiseを作ってデストラクチャリング
(async () => {
  const p1 = timer(10000, 'a')
  const p2 = timer(10000, 'b')
  const [result1, result2] = [await p1, await p2]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

これは並列処理になる!

Case.5

  • for文の中でawaitさせる
(async () => {
    for (let {ms, name} of [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]) {
      const result = await timer(ms, name)
      console.log(result)
    }
})()

// name: a start!
// a
// name: b start!
// b
// 経過時間: 20000ms

直列処理になる。

Case.5

  • forEach内のラムダで実行する
(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .forEach(async ({ms, name}) => {
      const result = await timer(ms, name)
      console.log(result)
    })
  console.log('c')

})()

// name: a start!
// name: b start!
// c
// a
// b

並列処理になるがcはラムダ内のawaitを待つことはない。

Case.6

  • mapの戻りでawaitさせてみる
(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(async ({ms, name}) =>  await timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ Promise { <pending> }, Promise { <pending> } ]
// 経過時間: (ほぼ)0ms

ただのpromiseの配列が帰ってくる。 当然だけど↓と同じ。

(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
  console.log(results)

  // name: a start!
  // name: b start!
  // [ Promise { <pending> }, Promise { <pending> } ]
})()

Case.8

(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
    .forEach(async promise => {
      const result = await promise
      console.log(result)
    })
  console.log('c')
})()

// name: a start!
// name: b start!
// c
// a
// b

cは即時出力。timerは並列で実行されるが、forEachのラムダ内はawaitできる。

Case.9

  • promiseの配列をPromise.allの引数に与える
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(({ms, name}) => timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

これが一番シンプルに並列処理の結果を直列に受け取れる気がする。

Case.10

  • mapに渡すラムダ内でasync/awaitをする
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(async ({ms, name}) => await timer(ms, name)))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

そもそもtimerもasync functionもpromiseを返すので二重定義になって意味ない。

完全に理解した!!!!!

GAEにデプロイしたRailsからGoogle Cloud SQL に疎通できない時に確認すること

f:id:ie-kau:20170806002855p:plain


概要

さてさて、去年 Google App Engine に Ruby の runtimeが追加されたことにより、GAE上でRailsを稼働させることが出来るようになりました。

ちょっと時間が空いてしまいましたが絶賛いじり中です。
今日は、GAEに上げたRailsからGoogle Cloud SQLに接続しようとした結果、少しハマってしまったので対処法のメモを残して置こうと思います。

本来の Google Cloud SQLへのつなぎ方

GAE上から Google Cloud SQL に繋ぐ設定は簡単です。 コンテナ上で mysql ソケットは以下のパスに展開されるので Rails の設定で Path を指すように記述します。

/cloudsql/コネクション名

app.yaml

GAE の設定

runtime: ruby
env: flex
entrypoint: bundle exec rackup -p 8080 -E production config.ru
env_variables:
  SECRET_KEY_BASE: 'xxxxxxxxxxxx'
beta_settings:
  cloud_sql_instances: [PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] # Cloud SQL のコネクション名

database.yml

Rails - MySQLの設定

production:
  <<: *default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: USER_NAME
  password: PASSSWORD
  database: DATABASE_NAME
  socket: /cloudsql/[PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] # Cloud SQL のコネクション名

しかし、この設定のみで通信できると考えていたですが、何故か疎通できませんでした。。。

GAE上のstderrログを見る

問題切り分けのためにログを見ます。 ログは Stackdriver に流されているので、検索条件を絞ることで閲覧可能です。

ドロワーメニューから Stackdriver を選んで Logging -> Logs

f:id:ie-kau:20170805194743p:plain

デフォルトでは以下のログが見れるようになっています。

  • stdout
  • stderr
  • nginx.request

f:id:ie-kau:20170805194902p:plain

何が悪かったか??

で、stderr を追っていくと・・・ Rails を起動する前にこんなエラーが・・・

payload: " googleapi: Error 403: Access Not Configured. Cloud SQL Administration API has not been used in project XXXXXX before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview?project=XXXXXX then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry., accessNotConfigured

とのこと。どうやら、Cloud SQL の API を有効にする必要があるらしいです。ログに流れていたリンク先に飛ぶと API 設定画面が開くので、 disable の項目を enable にします。

f:id:ie-kau:20170806003101p:plain

これで疎通完了です。

まとめ

ローカルで cloud_sql_proxy を入れて使う場合には問題なく疎通できるのですが、GAE 上では API の設定を変更する必要があるようでした。
これで GAE / Ruby on Rails の環境が整いましたね。

UFW (Uncomplicated Firewall) を理解しておく

f:id:ie-kau:20170717174929p:plain


UFWとは?

コマンドラインで簡単に設定出来るfirewall。 Ubuntu 8.04 LTSより新しいバージョンならデフォルトでインストールされているので、Ubuntuを利用している場合はこれを利用してfirewallの設定をするのが一般的。

解説

例えば以下の設定があったとする。

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         ALLOW IN    Anywhere
Anywhere                   DENY IN     192.168.1.1
80/tcp                     ALLOW IN    Anywhere
22 (v6)                    ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)

初期設定は、

  • 起動中
  • 内向きのパケットは全て禁止
  • 外向きのパケットは全て許可

フィルタリングルールは上から順に適応されているくので、

  • port 22(ssh目的)は許可
  • 192.168.1.1からのアクセスは禁止
  • port 80(http目的)は許可

という読み方になる。

よく使うコマンド

確認

ルール表示

sudo ufw status

ルール + ステータス

sudo ufw status

設定

ポートで許可 / 禁止

# sudo ufw allow <port>/<optional: protocol>
sudo ufw allow 80/tcp
sudo ufw deny 80/tcp

IP adressで許可 / 禁止

# sudo ufw allow from <ip address>
sudo ufw allow 192.168.1.1
sudo ufw deny  192.168.1.1

編集

連番付きルール表示

sudo ufw status numbered

特定の番号が付いたルールを消す

sudo ufw delete 4

番号指定でルールを追加する

sudo ufw insert 1 allow from <ip address>

ネットワークアドレスでフィルタリングする

sudo ufw deny  192.168.1.0/24

HOST部分を変えながらDOS攻撃してくる場合などの対応のためネットワークアドレスでフィルタリングすることも出来る。

To                         Action      From
--                         ------      ----
Anywhere                   DENY IN     192.168.1.0/24

※サブネットマスクで言うと255.255.255.0なので192.168.1.0〜192.168.1.255

設定手順

sudo ufw allow 22
sudo ufw enable
sudo ufw deny from 192.168.1.1
sudo ufw allow 80/tcp

22番ポートが閉じたまま接続が切れたらマジ厄介なので先に許可設定したまま ufw を enable にする。

参考

NodeでFCMを利用してAndroidにプッシュ通知を送る

f:id:ie-kau:20170628160920p:plain


すいません!すいません!
めちゃくちゃブログの更新滞っちゃいましたねorz

概要

サーバーサイドのNodeからFCM経由でAndroidアプリにプッシュ通知を送る際の実装メモです。

FCMとは?

Firebase Cloud Messagingのことで、Googleが提供するAndroidの通知サービスです。
これまでは Google Cloud Messaging (GCM)が一般的だったのですが、GoogleがFirebaseを買収したことにより、今後はFCMをメインで利用していくことになります。

事前準備

FCMの認証のための秘密鍵を取得します。

設定のサービスアカウントからいけます。 f:id:ie-kau:20170628161547p:plain

利用するライブラリ

自作するのは面倒なので、さすがに誰か作ってるだろーと思って探したらありました。

github.com

コード

初期設定

const fcm = require('fcm-node')
const fcmProvider = new fcm(require('../keys/privatekey.json'))

送信関数

function sendFcm(title, body, token, payload = {}) {
  const message = {
    to: token,   // register token

    notification: {
      title: title,  // プッシュ通知として表示されるタイトル
      body: body  // プッシュ通知本文
    },

    // アプリに送信するペイロード
    // Number型は送れないので文字列にしておく
    data: Object
        .keys(payload)
        .reduce((accum, current) => {
          accum[current] = String(payload[current])
          return accum
        }, {})
  }

  fcmProvider.send(message, (err, response) => {
    if (err) {
      logger.system.error(`sending fcm is failure : ${err}`)
    } else {
      logger.system.info(`sending fcm is success : ${response}`)
    }
  })
}

APNsにも送信するため個別で関数を用意しています。

ちなみに

Firebaseのコンソールからも通知を送信できるのでプッシュが届かない場合にアプリ側の問題か、サーバー側の問題か切り分けることが出来ます。

Have a happy Notification!