【vuecli】src配下にapplication configファイルを追加する

ハマった。

環境

$ npm list -g vue-cli

E:\node_modules
+-- @vue/cli-init@3.0.0-rc.10
| `-- vue-cli@2.9.6
`-- vue-cli@2.9.6

$ npm -version
6.4.1

やりたいこと

  • vue cli を使ってテンプレート生成したプロジェクトに、vue用の application コンフィグファイルを作ってRestAPIの接続先サーバーを記述し、アプリケーションで利用したい。
  • npm run から起動するビルドスクリプトなどで、NODE_ENVがdevelopmentならdevlopment.jsを読み込み、productionならproduction.jsを読み込みたい。

構成は以下のとおり

vueproject
 |`- .babelrc
 |`- .editorconfig
 |`- .eslintignore
 |`- .eslintrc.js
 |`- .gitignore
 |`- .postcssrc.js
 |`- build
 |`- config
 |`- dist
 |`- index.html
 |`- node_modules
 |`- package-lock.json
 |`- package.json
 |`- README.md
 |`- src
 |    |`- config
 |    |    |`- development.js   ←
 |    |     `- production.js    ←
 |     `- componentsとか色々なやつ。この中のコードからconfigを参照
 |`- static
  `- test

設定追加に至るまで

結果的にWebpackにalias を設定する方法を採用。

qiita.com qiita.com

webpack.base.conf.js

build/webpack.base.confに下記を追記

module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  // ...
}

↓

module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'config': resolve(`src/config/${process.env.NODE_ENV}.js`) // 追記
    }
  },
  // ...
}

npm run devnpm run build 時に展開されてwebpackに渡される寸法。

npm run devだけだとでNODE_ENV未設定で解決

どうも vuecli で生成したスクリプトでは、npm run dev はNODE_ENVにdevelopmentが自動設定される前にwebpackをrunしてしまうらしい。

事前に環境変数 NODE_ENV にdevelopment を設定しておくだけでも解決するが、 自前用のコードであったしものぐさなので、webpack.dev.conf.jsの下記にハードコードで上書きするようにした。

const devWebpackConfig = merge(baseWebpackConfig, {
  // 下記追記
  resolve: {
    alias: {
      config: resolve(`src/config/development.js`)
    }
  },
  // ...
})

これで、下記コードでコンフィグが参照できるようになる。

development.js

export default {
  apiurl: '{protocol}//{host}/api'
}

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import config from 'config' // これ
import services from './services/entry'

Vue.config.productionTip = false

const configMixin = Vue.mixin({
  created: function () {
    this.$config = config

    for (const key in services) {
      var tagservice = '$' + key
      if (this[tagservice]) {
        console.error(`module name conflicted with $vue.${tagservice}! we take overwrite to ./services/modules.`)
      }
      this['$' + key] = services[key]
    }
  }
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  configMixin,
  components: { App },
  template: '<App/>'
})

【Golang】CSVのDecode

[][]string へのDecodeで我慢できるのであれば、encoding/csv パッケージがそれなりに使える。

  • ファイル終端にいくとEOFする
  • 引用符ありなしを自動解釈
  • 引用符中のダブルクォート2つ重ねを1つにしてくれる
  • 引用符中なら改行文字を何いれてもいい
  • \はエスケープ文字としてみなさない

Decode

package textdata

import (
    "encoding/csv"
    "fmt"
    "io"
    "time"
)

func DecodeCSV(r io.Reader) ([]Textdata, error) {
    rc := csv.NewReader(r)

    datas := make([]Textdata, 0)

    i := 0
    for {
        row, err := rc.Read()
        i = i + 1
        if err != nil {
            if err == io.EOF {
                break
            }
            return nil, err
        }
        if len(row) < 2 {
            return nil, fmt.Errorf("invalid row. too less columns num. [position]: row %d [data]: %s", i, row)
        }
        data := Textdata{}
        data.Caption = row[0]
        data.Body = row[1]
        datas = append(datas, data)
    }

    return datas, nil
}

test

package textdata

import (
    "strings"
    "testing"
)

// Search is a function search textdata in shared repository
func TestDecodeCSV(t *testing.T) {
    gotest := func(csv string, expect []Textdata) {
        r := strings.NewReader(csv)
        datas, err := DecodeCSV(r)
        if err != nil {
            msg := "error occured"
            t.Error(msg)
        }

        if len(expect) != len(datas) {
            t.Errorf("expect %d records, but returns %d.\r\n[expect]:%s\r\n[result]:%s",
                len(expect), len(datas), expect, datas)
            return
        }
    }

    genExpect := func(caption string, body string) Textdata {
        return Textdata{Caption: caption, Body: body, CreateAt: ""}
    }

    gotest(`a,b`, []Textdata{genExpect("a", "b")})
    gotest(`"a",b`, []Textdata{genExpect("a", "b")})
    gotest(`a,"b"`, []Textdata{genExpect("a", "b")})
    gotest(`"""a""","b"`, []Textdata{genExpect("\"a\"", "b")})
    gotest(`"""a\r\nb\rc\nd""","b"`, []Textdata{genExpect("\"a\r\nb\rc\nd\"", "b")})
}

【Postgresql】マテリアライズドビュー

マテリアライズドビューとは

  • 実行結果をテーブルと同じようにキャッシュして、普通のビューならテーブル結合などのコストをかけて実行するものを既に計算済みのデータを読むだけの状態にする機能。
  • 実態はテーブルに似ていて、インデックスなどが張れる
  • 一度キャッシュしたら、REFLESH しないとデータが更新されない

マテビューを作る

CREATE MATERIALIZED VIEW mate_view AS
SELECT
  dept.dept_id, dept.dept_name, emp.emp_id, emp.emp_name
FROM
  dept
  INNER JOIN emp USING (dept_id)
;

マテビューをリフレッシュする

手動実行

www.postgresql.jp

-- 占有ロックあり
REFRESH MATERIALIZED VIEW mate_view;

-- 占有ロックなし、更新/参照されたら都度都度リフレッシュとまる
REFRESH MATERIALIZED VIEW CONCURRENTLY mate_view;

Oracleには自動更新するオプションはあるが、Postgresql10.x現在そういうのはないらしい。

vscode-Remarkで全角文字をテーブルに入れても半角文字幅でしか整形されないのを解決

すごく汚い解決法

  1. RemarkをVSCodeで入れる

  2. VSコードのextensionインストールディレクトリ(%UserProfile%.vscode\extensionsなど)にあるmrmlnc.vscode-remark-1.1.0\node_modules\markdown-table\index.jsを編集

// 下部に↓の関数があるので定義書き換え
// function lengthNoop(value) {
//   return String(value).length;
// }
  
function lengthNoop(value) {
  let i = 0;
  let surrogatePairHead = false;
  for (let c of String(value)) {
    const cp = c.codePointAt();
    // codepointを得てASCII文字セットの範囲なら半角、そうでないなら全角幅として計算
    // サロゲートペアはうまいことかわす
    // 半角カタカナなどは2バイト文字になってしまう。他にもあるけどとりあえず入れるの面倒だったから放置
    // 内部エンコーディングがUTF16らしいのでほしかったら適宜定義
    if (!surrogatePairHead && 0xD800<= cp && cp <= 0xDFFF) {
      surrogatePairHead = true;
      continue;
    }
    surrogatePairHead = false;
    i += (cp & 0xff00) ? 2 : 1;
  }
  return i;
}
  1. VSコードを再起動

VScodeのsettingsってfunction(){} を値にできないんやろか

【Redshift】ANALYZE、VACUUMメモ

2019/06/27 誤りを修正。

各実施で起こること

VACUUM

  • ''DELETE'' や ''UPDATE'' で発生した、削除フラグ付きの領域の掃除。(SELECTで検索するとき、この領域も一応見に行って検索ミスする)
    • 2018年11月ぐらいのアップデートで、削除フラグ付き領域の削除が、ほんとすぐにバックグラウンドで自動実行されるようになった。この機能は実質無視していい。
  • テーブルの ''DISTSTYLE'' の設定に従ったデータのスライス間再分散
    • ''DISTSTYLE EVEN'' だとけっこー軽い
  • テーブルに ''SORTKEY'' が設定されている場合、未ソート領域のソート実施、および未ソート領域のデータがソート済み領域に割入って挿入される場合はソート済み領域へのマージ。
    • めちゃくちゃ重い。列が多いテーブルで、下手すると数十時間かかる。
  • 前回VACUUM実施時からの変更行数が全体の5%以下のとき、内部でVACUUMがスキップされる。
  • テーブルを占有ロックする。参照ロック先行でかかってたら待つし、その後のトランザクションをみんな待たせる。CPU使用リソースとディスクリソースを結構使う。
  • 1つのクラスタで、同時に実行できる明示的なVACUUMは1つのみ。

ANALYZE

  • テーブルの統計情報(このディスクにこの範囲の値のデータがこんだけあってなどの情報)の刷新
    • SELECTでサンプリングしてガリガリ統計とるらしい(AWS公式ドキュメントより)
  • 前回ANALYZE実施時から、パラメータ analyze_threshold_percent の割合以上の行の変更以下の場合、内部でAnalyzeがスキップされる。
  • 裏でSELECTするクエリでテーブルを参照ロックする。CPUリソースを結構使う。(30%ぐらい。全データ300GBぐらいのテーブルだと 十数分かかる)
    • 実行するのは、userid=1 の特殊なユーザー

確認/設定変更SQL

ANALYZE履歴取得

-- ANALYZEの実行履歴を確認するSQL。要スーパーユーザー
-- すでにDROPしたテーブルは名前空欄になるかも
-- 実テーブルあるけどANALYZEログないものはそもそもレコードが出ない
SELECT 
  stl_analyze.userid,            -- ANALYZE実行起因となったクエリを実行したユーザーID
  stl_analyze.xid,               -- ANALYZE実行起因となったクエリのトランザクションID
  stl_analyze.database,          -- 対象テーブルのあるdatabase
  stl_analyze.table_id,          -- 対象テーブルID
  tbl_perm_info.tablename,       -- 対象テーブル名。ログにtable_id残ってるけどシステムテーブルに名前ないものはNULL
  stl_analyze.status,            -- ANALYZE実行したか否か。Full = 全量、Skipped = コマンド実行したけど内部スキップ
  stl_analyze.rows,              -- 実行前の全体のテーブル統計サイズ。削除してcollectしていない領域も含まれる
  stl_analyze.modified_rows,     -- 実行前の最終ANALYZE実施以来の変更行数
  stl_analyze.threshold_percent, -- 実行時の、実施 or Skippedの判断となる変更行割合閾値。パラメータanalyze_threshold_percentの値。
  stl_analyze.is_auto,           -- 自動Analyzeか否か。SELECTしたとき、たまに走る
  convert_timezone('Asia/Tokyo', stl_analyze.starttime),         -- Analyze実行日時
  convert_timezone('Asia/Tokyo', stl_analyze.endtime),           -- Analyze終了日時
  convert_timezone('Asia/Tokyo', stl_analyze.prevtime)           -- Analyze前回実行日時
FROM
  -- システムテーブル
  pg_catalog.stl_analyze
  -- テーブルIDとテーブル名の組を取るサブクエリ。取るとこ間違ってる気がするけど運用できるしGoogle先生もそう言ってる
  LEFT JOIN (SELECT id, TRIM(name) AS tablename FROM pg_catalog.stv_tbl_perm GROUP BY id, name) tbl_perm_info ON (stl_analyze.table_id = tbl_perm_info.id)
--WHERE
--  convert_timezone('Asia/Tokyo', stl_analyze.starttime) >= '2018-10-10'
ORDER BY DATABASE, table_id, starttime;

ANALYZE実行閾値の取得と変更

-- 前回ANALYZE実施以降、全体の行数に対する変更行数がこの%以下であればANALYZEを内部でSkipする
SHOW analyze_threshold_percent;

-- このセッション中だけ一時的に書き換える。
-- 0だと更新がなくても絶対ANALYZE。auto ANALYZEでも実施されるのでバッチがむしろめっちゃ遅くなる。
SET analyze_threshold_percent to 0.01;

-- ユーザーの設定を恒久的に書き換える
ALTER USER username SET analyze_threshold_percent = 0.01;
COMMIT;

-- クラスタ全体の設定変更は、AWS WebConsoleからパラメータを設定する

ANALYZE / VACUUM 実施SQL

VACUUMは、各テーブルの所有ユーザーで実施必須。

ANALYZE実施

個別テーブル

-- このセッションのみAnalyze実施行数の閾値を 0 に
-- 元の値も確認しておく
SHOW analyze_threshold_percent;
SET analyze_threshold_percent TO 0;
SHOW analyze_threshold_percent;

-- Analyze実施
ANAYLZE <schema.tablename>;

-- 任意:Analyze実施行数の閾値を戻す。(セッションを切れば破棄される)
SHOW analyze_threshold_percent;
RESET analyze_threshold_percent;
SHOW analyze_threshold_percent;

入っているユーザーが権限を持つ全テーブル

-- このセッションのみAnalyze実施行数の閾値を 0 に
-- 元の値も確認しておく
SHOW analyze_threshold_percent;
SET analyze_threshold_percent TO 0;
SHOW analyze_threshold_percent;

-- ANALYZE実施
ANAYLZE;

VACUUM実施

個別テーブル

-- トランザクションを切っておかないとVACUUMが失敗する
END;

-- VACUUM実施
-- ※ インターリーブソートキーを設定しているテーブルは FULL → REINDEX にしないとだめ。
VACUUM FULL <schema.tablename>;

-- トランザクションをいちおう始めておく(後続のSQLをrollback可能にする
BEGIN;

入っているユーザーが権限を持つ全テーブル

-- トランザクションを切っておかないとVACUUMが失敗する
END;

-- VACUUM実施
-- ※ インターリーブソートキーを設定しているテーブルは FULL → REINDEX にしないとだめ。
VACUUM FULL;

-- トランザクションをいちおう始めておく(後続のSQLをrollback可能にする
BEGIN;

vue-cliで Cannot find module 'chalk' と言われてnpm run buildが通らない

めちゃはまり

事象

dockerつかってvue-cliからbuildしようとしたとき、chalkがnotfoundで落ちる。

下記DockerFileから抜粋

ENV NODE_ENV=production

WORKDIR /usr/local/app-octface-front
RUN npm install

RUN npm run build

RUN npm run build で下記エラーが出る。

Step 14/18 : RUN npm run build
 ---> Running in bd96983db14f

> oct-face@1.0.0 build /usr/local/app-octface-front
> node build/build.js

internal/modules/cjs/loader.js:583
    throw err;
    ^

Error: Cannot find module 'chalk'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/usr/local/app-octface-front/build/check-versions.js:2:15)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)

package.jsonには chalk があるがインストール後の現物を見るとnode_modules配下に確かにディレクトリがない。おかしい。

一時解決

原因は、package.jsonにchalkなどがdevDependencies で 記述されており、npm installNODE_ENV=production だったのでインストールされなかったようだ。 ENV NODE_ENV=production を、npm install後になるように書き直した。

RUN npm install

ENV NODE_ENV=production
RUN npm run build

根本的に、vue-cliによって生成されたbuild.jsがビルドサーバーで動かないつくりになってるのは違和感。 多分開発で使っていたソースをそのままぶっこむのは何か違うんじゃないかな。試すの面倒くさいから誰か見てほしい。