パパエンジニアのアウトプット帳

30歳に突入した1児のパパエンジニアのブログ

VPCピアリング越しのAurora(PostgreSQL)にRedshiftのFederated Queryで接続する

お仕事でRedshiftのFederated Queryを試してみたのでそのメモ。
ネットで見つかるやつは大体パブリックなネットワークとか同じVPC内のやつなので、仕事の環境のようにVPCが分かれている環境の場合に繋ぐのに苦労した。


やることはほぼ参考記事と同じだけど、拡張されたVPCのルーティングを有効にするのがポイント


  1. Secrets ManagerにAuroraの接続情報を保存
  2. Secrets Managerを利用できるIAMロールを作成
  3. Redshiftクラスターの作成
    "クラスターのアクセス許可 (オプション)"で上記で作成したIAMロールを追加
    "ネットワークとセキュリティ"の"拡張されたVPCのルーティング"を有効にする(重要)
  4. External SCHEMAの作成
CREATE EXTERNAL SCHEMA IF NOT EXISTS apg
FROM POSTGRES
DATABASE '接続するデータベース名'
URI 'Auroraのエンドポイント' PORT 5432
IAM_ROLE '2.のIAMロールのARN'
SECRET_ARN '1.のSecrets ManagerのARN';


※ 拡張されたVPCのルーティングを有効にするとRedshiftのコンソールのクエリエディターが利用できなくなるので、別途EC2など立ててpsqlコマンでredshiftに接続します


これでFederated Queryのセットアップが完了したので、RedshiftからSQLでAuroraにアクセスできるようになりました。

(参考)
PostgreSQLPostgreSQL - Amazon Redshift

[新機能] Amazon Redshift Federated QueryがGAになったので試してみました | Developers.IO

遭遇したエラー

timeout expired

これは色々な原因が考えられるので特定が難しいです...
今回の場合は、拡張されたVPCのルーティングを有効にするにしないとSQLを実行してもこのエラーになりました。
同じ手順で拡張されたVPCのルーティングを有効にするにしている場合は、ネットワーク設定の不備が有力なのでセキュリティグループの設定などをしっかりと見直しましょう。

ERROR:
  -----------------------------------------------
  error:  timeout expired

  code:      25000
  context:
  query:     0
  location:  pgclient.cpp:473
  process:   padbmaster [pid=13916]
  -----------------------------------------------

ちゃんとDB名指定しているのにdoes not existになる

これも少しハマったのですが、DB名にMyAppDevみたいな大文字小文字が混ざったDB名にしていると何故か全て小文字の"myappdev"でアクセスしようとするのでmy_app_devみたいなDB名にしましょう。
何か回避する方法がありそうですがまだ分かっていません...

ERROR:
  -----------------------------------------------
  error:  FATAL:  database "myappdev" does not exist

  code:      25000
  context:
  query:     0
  location:  pgclient.cpp:473
  process:   padbmaster [pid=14497]
  -----------------------------------------------

MacでChromecastでデスクトップをキャストができず、システム環境設定>セキュリティとプライバシーの画面収録でChromeのアプリが表示されていない場合

職場でChromecastが使えるようになったけど、なぜかデスクトップをキャストができなかった(タブをキャストはできる)


色々ググってもシステム環境設定のプライバシー >画面収録でChromeにチェック付けてあげればいいですよーって書いてあるが、こっちとらそれすら出てないんじゃいっていう感じだった。(Macの再起動とかも試した


大体こういうのは、やろうとした時にダイアログが開いて許可するとか出ると思っているのだが、それすら出ない...


さらにググって下記にたどりつき、アクセシビリティChromeを追加してみたが変化なし...

dev.classmethod.jp


これは、解決できないかなー...と諦めかけていた時に閃いた

f:id:masaru_tech:20201118144017j:plain

これ、一度Google Hungoutで画面共有をやればいいのでは?と

そして、おもむろに一人でハングアウトを初めて画面共有をするを押すと・・・



キタ━━━━ヽ(゚∀゚ )ノ━━━━!!!!


待っていたダイアログが表示され、許可すると画面収録のところにChromeが追加されるのでそれにチェックを付けてあらためてChromecastでデスクトップをキャストするを選ぶと無事に共有できるようになった

Alpine Linuxにmecabをインストールする

Alpine LinuxのDockerコンテナにmecabを入れる必要があったので参考にしたやつメモ。

docker-mecab/Dockerfile at master · smizy/docker-mecab · GitHub

Python/NLP/機械学習のためのDocker環境構築 - Qiita


mecabgoogle driveからwgetするパターンが多いけど、なぜか自分のところだと server returned error: HTTP/1.1 400 Bad Request とかになるので色々探していたら、上記のqiitaでgithubから持ってこれることが分かったのでそちらをダウンロードしてインストールした。

solidusの画像アップロードができない時

solidusを触り始めら商品の画像アップロードがspoofed_media_typeエラーでできなかった。

でコンソールには下記のようなログが出ていてfileコマンドを実行しようとしているけどないのでcontent type取れずにエラーになっているっぽい。

[paperclip] Content Type Spoof: Filename sample.jpg (image/jpeg from Headers, ["image/jpeg"] from Extension), content type discovered from file command: . See documentation to allow this combination.

なので、fileコマンドをインストールしたらエラーが出なくなった。

apt-get install file

初めにrails spree:installでサンプルの画像が登録できないなーとなって、下記のpaperclipのissueのようにspoofed?でfalseを一時的に返すようにしてたけど、これもfileコマンドないのが原因だったのか...

github.com

embulk-output-bigqueryでパーティションテーブルを扱うときのアレコレ

お仕事でEmbulkを使ってBigQueryにデータインポートやることがあって、その時にパーティションテーブルに対してアレコレやるときに色々検証したのでメモ。
Embulkはv0.9.23でembulk-output-bigqueryはv0.6.4です。

github.com

パーティションの利用方法

embulk-output-bigqueryでパーティションテーブルを扱う方法は下記の2つです。

table名でパーティションデコレータ指定

table: table_name$20200827 のようにパーティションデコレータを利用すると、指定パーティションに書き込めます。
この書き方をすると後述するmodeオプションでreplaceなどを利用した時に指定パーティションだけ上書きできます。


デコレータの書き方も直接20200837のように書くこともできますし、ここに書いてあるようにTime#strftime formatが許されるのでtable_name$%Y%m%d と書くこともできます。
さらに、embulkはliquid templateを利用できるので関数を利用して動的に指定することも可能です。

{% assign target_date = env.TARGET_DATE | default: '2020-08-20' %}

table:table_name${{ target_date | replace: "-", "" }}

time_partitioning.field

もう1つはtime_partitioning.fieldを指定する方法で、DATEかTIMESTAMP型のカラムを指定するとカラムベースのパーティションテーブルになります。
カラムベースでパーティションを分けるので、embulkで一括処理するときにパーティションを気にせずinsertすることができます。
なので、この場合は基本的にもう1つのパーティションデコレータ指定をする必要はありません。


しかし、この場合は後述するmodeでreplaceなどを選んでも対象のパーティションは不明なので、全てのデータがreplaceされてしまいます。
データ重複を避けたいけど、一度に全データを対象に読み込まない場合はこの方法の場合は使えないので注意です。

mode

embulk-output-bigqueryでは下記の5つの書き込みモードをサポートしています。

  • append
    • 一時テーブルにinsertした後、対象テーブルに追記します
  • append_direct
    • 一時テーブルを利用せず、対象テーブルに追記します
    • トランザクションをサポートしないので、途中で失敗しても成功した分のデータはinsertされたままです
  • replace
    • 一時テーブルにinsertした後、対象テーブルに上書きします
  • replace_backup
    • 基本的にはreplaceと同じですが、対象テーブルを上書きする前にバックアップテーブルにデータを退避します
  • delete_in_advance
    • 対象のテーブルやパーティションのデータを削除してからinsertします
    • 一時テーブルは利用しません

BigQueryにはプライマリーキーのような概念はないようなので、同じデータのinputに対して何度も実行した時に重複データができないようにするには、replace,replace_backup,delete_in_advanceの3つを使うしかないと言う事になります。

パーティションテーブル利用時に冪等性を担保するためには

time_partitioning.field を利用している場合は先にも述べたようにreplaceなどは全データが対象になるので、対象のパーティションのみを入れ替えるようなことができません。(毎日全データを対象にしてデータを入れ替えるような運用の場合は大丈夫ですが)


一応、time_partitioning.fieldパーティションデコレータを組み合わせることもできますが、inputに日を跨ぐようなデータが混ざる場合は下記のようなエラーになってしまいます。

org.embulk.exec.PartialExecutionException: org.jruby.exceptions.RaiseException: (Error) failed during waiting a Load job, get_job(test-embulk-287601, embulk_load_job_44809189-0ec6-4cac-9cbd-58a70da7bd93), errors:[{:reason=>"invalid", :message=>"Some rows belong to different partitions rather than destination partition 20200820"}]

なので、異なる日付データが含まれないことが保証されるのであればカラムベースでreplaceやdelete_in_advanceを使って重複データが含まれないようにすることも可能そうです。(試した感じでは一応期待した動きにはなっている)

out:
  ・・・
  table: access_log${{ target_date | replace: "-", "" }}
  time_partitioning:
    type: DAY
    field: date
  mode: replace

※他に調べていたところembulk-filter-rowを使うと行の除外もできるので、これを利用して対象の日付の行以外を除外すれば問題なさそう

カラム変更

schema_update_optionsを指定するとカラム追加に対応できます。
ただ、一時テーブルを作るappend,replace,replace_backupでは元とカラムが異なってしまいコピーできないので利用できません。(なので重複データの考慮もするのであればdelete_in_advance一択となります)
また、time-partitioningの項に書いてあるとおり追加のみで、カラム変更や削除はできません。

Use Tables: patch API to update the schema of the partitioned table, embulk-output-bigquery itself does not support it, though. Note that only adding a new column, and relaxing non-necessary columns to be NULLABLE are supported now. Deleting columns, and renaming columns are not supported.

schema_update_options のALLOW_FIELD_ADDITIONなどについては公式ドキュメント(--schema_update_option)を参照
cloud.google.com

サンドボックス

これはembulk-output-bigqueryと言うよりはBigQueryのサンドボックスモードの場合の注意点です。
GCPはクレジットカードを登録しなくても無料枠の範囲内で利用できるので気軽に試せるのですが、BigQueryはその場合はサンドボックスモードとなり制限があります。

cloud.google.com

古いデータなどで試している場合にパーティションに利用しているカラムが60日より前の日付だとinsertに成功してもデータが0件になります。
appendやreplaceなど一時テーブルに一度インサート→コピーするようなmodeの場合はinとoutの件数が違ってembulk実行時にエラーになります。
delete_in_advanceのように直接デーブルにinsertするような場合はエラーにもならず成功するので、サンドボックスで試している場合はデータの鮮度?に気をつけましょう。

参考

初めてembulk-output-bigqueryを利用するので冪等性はどうやったらできるかな?と調べていて下記の記事を見つけたので、自分でも色々検証してみました。 qiita.com

これ以外だとmercariさんのような感じで差分更新を実現しないといけないので、中々大変そうです。
engineering.mercari.com

input[type=email]はブラウザによって挙動が異なる

お仕事でinput[type=email]に悩まされたのでメモ。

input[type=email]とは

まあ、説明は説明は不要ですかね。
メールアドレスの入力でいい感じに入力形式のチェックもブラウザでしてくれるやつです。

developer.mozilla.org

とまあ、ブラウザ側でのお手軽な入力制限として使うこともあるのですが、実は各ブラウザで挙動が違ってビックリでした。。。

ブラウザ ローカル部(@より前)での非ascii文字の許容 ドメイン部(@より後)での非ascii文字の許容 Punycode変換
Chrome しない する する
Firefox しない する しない
Safari しない しない -

※ちなみに各ブラウザのバージョンは下記です

非ascii文字の許容

ブラウザ毎に弾いてくれたり、くれなかったりします。。。
正確には日本語ドメインなども存在するのでドメイン部は許容されてもいいのですが、Safariさんはそれは許してくれません。

Punycode

今回初めて知ったのですが、日本語ドメインなどはそのままではDNSでひけないので、使用可能な文字列に変換することをPunycode(変換)と言うらしいです。

ja.wikipedia.org

で、ChromeだけこのPunycode変換をブラウザがやってくれるので非ascii文字が含まれると下記のように変換した文字列をサーバー側に送ります。
例)test@example.jpら→test@example.xn—com-373b


なので、サーバー側で日本語文字などが含まれていたら弾きたいと思っていても送られてきたパラメーターはすでに変換されていまっていて含まれていないと。。。
Firefoxだとそのまま変換されずに送られてくるのでまだいいのですが。

どうする?

まあ要件次第といった感じなんですが、フロントサイドでどこまでやるかっていうところです。
(サーバーサイドをノーガードは流石にないと思いますが)
pattern属性で正規表現かけば統一できますが、表示されるメッセージまで変えたければさらにJSでゴニョゴニョする必要がありますし...

actを使うとGithub Actionsのワークフロー定義をローカルで確認しながらやれて便利

お仕事でGithub Actionsを利用してプルリクのラベル自動付与するworkflowを定義するときに、ローカルである程度確認作業とかできないかなーと思っていたらactを思い出したので使ってみました。

actとは

github.com Dockerを利用して.github/workflowsに沿ったワークフローをローカルでもシュッと実行できるようにしているみたいです。

Let's Try

Github Actionsのworkflowの書き方は公式ドキュメントあるのでそれ読めばわかるはずです。 docs.github.com

$ vi .github/workflows/on_pull_request.yml
----以下追加-----
name: sample workflow on pull-request

on:
  pull_request:

jobs:
  helloWorld:
    name: Hello World Job
    runs-on: ubuntu-18.04
    run: echo "Hello World"

保存したらpull_requestイベントが発火した時のworkflowを確認してみます。
actコマンドの実行方法は act イベント名 オプション です。
オプションにlを指定すると実行されるworkflowの一覧が出力されます。

$ act -l pull_request
 ╭─────────────────╮
 │ Hello World Job │
 ╰─────────────────╯

違うイベントにしてみると、実行されるworkflowがないことがわかります。
※イベント名を省略するとpushイベントがデフォルトになっているっぽいです

$ act -l push

試しにもう一つjobを足してみると

$ vi .github/workflows/on_pull_request.yml
-----
name: sample workflow on pull-request

on:
  pull_request:

jobs:
  helloWorld:
    name: Hello World Job
    runs-on: ubuntu-18.04
    run: echo "Hello World"
  HogeFuga:
    name: Hoge Fuga Job
    runs-on: ubuntu-18.04
    run: echo "hoge fuga"

$ act -l pull_request
 ╭─────────────────╮ ╭───────────────╮
 │ Hello World Job │ │ Hoge Fuga Job │
 ╰─────────────────╯ ╰───────────────╯

jobが2つになりました。
さらに、HelloWorld→HogeFugaの順で実行するように定義すると

$ vi .github/workflows/on_pull_request.yml
-----
name: sample workflow on pull-request

on:
  pull_request:

jobs:
  helloWorld:
    name: Hello World Job
    runs-on: ubuntu-18.04
    run: echo "Hello World"
  HogeFuga:
    name: Hoge Fuga Job
    runs-on: ubuntu-18.04
    needs: helloWorld # ←追加
    run: echo "hoge fuga"

$ act -l pull_request
 ╭─────────────────╮
 │ Hello World Job │
 ╰─────────────────╯
         ⬇
  ╭───────────────╮
  │ Hoge Fuga Job │
  ╰───────────────╯

ちゃんと依存関係も反映された表示がされます!
分かりやすい!!

dry-runモード

nオプションを付けるとdry-runで実行されます。

$ act pull_request -n
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
*DRYRUN* [sample workflow on pull-request/Hello World Job] 🚀  Start image=node:12.6-buster-slim
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
*DRYRUN* [sample workflow on pull-request/Hello World Job]   🐳  docker run image=node:12.6-buster-slim entrypoint=["/usr/bin/tail" "-f" "/dev/null"] cmd=[]
*DRYRUN* [sample workflow on pull-request/Hoge Fuga Job  ] 🚀  Start image=node:12.6-buster-slim
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
*DRYRUN* [sample workflow on pull-request/Hoge Fuga Job  ]   🐳  docker run image=node:12.6-buster-slim entrypoint=["/usr/bin/tail" "-f" "/dev/null"] cmd=[]

(ちょっと元がechoの表示がないので実行されていないは分かりにくいですが、確かにdry-runされています)

イベント発火時にどんな情報が来るか

実際にGithub Actionsを利用する場合はさらに詳しい情報を見てやる/やらないしたりすると思います。
(例えば、draftプルリクの時は実行しないようにするとか)
そんな場合は、下記にevent時に渡ってくる情報がサンプルもあるので、それをactのeオプションで指定すると確認できます。 developer.github.com

試しに、先ほどのworkflowのHogeFugaジョブをdraftプルリクの場合は実行しないようにしてみましょう。

$ vi 
name: sample workflow on pull-request

on:
  pull_request:

jobs:
  helloWorld:
    name: Hello World Job
    runs-on: ubuntu-18.04
    run: echo "Hello World"
  HogeFuga:
    name: Hoge Fuga Job
    runs-on: ubuntu-18.04
    needs: helloWorld
    if: ${{ github.event.pull_request.draft == false }} # ←ifを追加
    run: echo "hoge fuga"

pull_requestのサンプルのpayloadのjsonファイルを用意します。

$ vi sample-pullreq.json

----全部だと多いので抜粋------
{
  "action": "opened",
  "pull_request": {
    "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2",
    "draft": true
  }
}

これをeオプションで指定してdry-runしてみます。

$act pull_request -n -e sample-pullreq.json

WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
*DRYRUN* [sample workflow on pull-request/Hello World Job] 🚀  Start image=node:12.6-buster-slim
WARN[0000] unable to get git repo: unable to find git repo
WARN[0000] unable to get git revision: unable to find git repo
WARN[0000] unable to get git ref: unable to find git repo
*DRYRUN* [sample workflow on pull-request/Hello World Job]   🐳  docker run image=node:12.6-buster-slim entrypoint=["/usr/bin/tail" "-f" "/dev/null"] cmd=[]

先ほどと違って *DRYRUN* [sample workflow on pull-request/Hoge Fuga Job ] が実行されていないのが分かります。

最後に

actを使えば、ローカルでGithub Actionsのworkflowを確認しながら定義することができてとても便利です。
他にも環境変数も渡したりできるので、実際に実行することも可能です。(元々actの作者がローカルでのworkflowを自動化したかったのもあるので当然ですが)
Github Actionsのお供にactはオススメだと思います。

その他

Github Actionsでデバッグするときはクラメソさんの記事のようにsecretsにACTIONS_RUNNER_DEBUGとACTIONS_STEP_DEBUGを設定すると良さげです。
dev.classmethod.jp